diff --git a/priv/static/static/font/font/fontello.eot b/priv/static/static/font/font/fontello.eot
old mode 100644
new mode 100755
index 30867c926..b9cdfcb5d
Binary files a/priv/static/static/font/font/fontello.eot and b/priv/static/static/font/font/fontello.eot differ
diff --git a/priv/static/static/font/font/fontello.svg b/priv/static/static/font/font/fontello.svg
old mode 100644
new mode 100755
index b5a6725a9..0e460ea5e
--- a/priv/static/static/font/font/fontello.svg
+++ b/priv/static/static/font/font/fontello.svg
@@ -54,6 +54,12 @@
+
+
+
+
+
+
@@ -68,6 +74,8 @@
+
+
diff --git a/priv/static/static/font/font/fontello.ttf b/priv/static/static/font/font/fontello.ttf
old mode 100644
new mode 100755
index 87c3d0d9b..f1b9f19d2
Binary files a/priv/static/static/font/font/fontello.ttf and b/priv/static/static/font/font/fontello.ttf differ
diff --git a/priv/static/static/font/font/fontello.woff b/priv/static/static/font/font/fontello.woff
old mode 100644
new mode 100755
index 3a87b4b62..141abc65a
Binary files a/priv/static/static/font/font/fontello.woff and b/priv/static/static/font/font/fontello.woff differ
diff --git a/priv/static/static/font/font/fontello.woff2 b/priv/static/static/font/font/fontello.woff2
old mode 100644
new mode 100755
index 009e5e851..efed5cf73
Binary files a/priv/static/static/font/font/fontello.woff2 and b/priv/static/static/font/font/fontello.woff2 differ
diff --git a/priv/static/static/js/app.36ed651e315106252e56.js b/priv/static/static/js/app.36ed651e315106252e56.js
deleted file mode 100644
index 20cbd78ef..000000000
Binary files a/priv/static/static/js/app.36ed651e315106252e56.js and /dev/null differ
diff --git a/priv/static/static/js/app.36ed651e315106252e56.js.map b/priv/static/static/js/app.36ed651e315106252e56.js.map
deleted file mode 100644
index b6aab4397..000000000
Binary files a/priv/static/static/js/app.36ed651e315106252e56.js.map and /dev/null differ
diff --git a/priv/static/static/js/app.c914d9a57d5da7aa5553.js b/priv/static/static/js/app.c914d9a57d5da7aa5553.js
new file mode 100644
index 000000000..e7b09c97e
Binary files /dev/null and b/priv/static/static/js/app.c914d9a57d5da7aa5553.js differ
diff --git a/priv/static/static/js/app.c914d9a57d5da7aa5553.js.map b/priv/static/static/js/app.c914d9a57d5da7aa5553.js.map
new file mode 100644
index 000000000..f469d271c
Binary files /dev/null and b/priv/static/static/js/app.c914d9a57d5da7aa5553.js.map differ
diff --git a/priv/static/static/js/manifest.6c8fd5aa8c8c4aee99d3.js b/priv/static/static/js/manifest.6c8fd5aa8c8c4aee99d3.js
deleted file mode 100644
index 5de86e9fb..000000000
Binary files a/priv/static/static/js/manifest.6c8fd5aa8c8c4aee99d3.js and /dev/null differ
diff --git a/priv/static/static/js/manifest.bf15f24d205c8cf4ee4a.js b/priv/static/static/js/manifest.bf15f24d205c8cf4ee4a.js
new file mode 100644
index 000000000..b6de44a86
Binary files /dev/null and b/priv/static/static/js/manifest.bf15f24d205c8cf4ee4a.js differ
diff --git a/priv/static/static/js/manifest.6c8fd5aa8c8c4aee99d3.js.map b/priv/static/static/js/manifest.bf15f24d205c8cf4ee4a.js.map
similarity index 92%
rename from priv/static/static/js/manifest.6c8fd5aa8c8c4aee99d3.js.map
rename to priv/static/static/js/manifest.bf15f24d205c8cf4ee4a.js.map
index a360856d5..c0cd90ac0 100644
Binary files a/priv/static/static/js/manifest.6c8fd5aa8c8c4aee99d3.js.map and b/priv/static/static/js/manifest.bf15f24d205c8cf4ee4a.js.map differ
diff --git a/priv/static/static/js/vendor.0d1eeaf25aa1d2fc51b0.js b/priv/static/static/js/vendor.0d1eeaf25aa1d2fc51b0.js
new file mode 100644
index 000000000..7e0119cc8
Binary files /dev/null and b/priv/static/static/js/vendor.0d1eeaf25aa1d2fc51b0.js differ
diff --git a/priv/static/static/js/vendor.0d1eeaf25aa1d2fc51b0.js.map b/priv/static/static/js/vendor.0d1eeaf25aa1d2fc51b0.js.map
new file mode 100644
index 000000000..ddc023b43
Binary files /dev/null and b/priv/static/static/js/vendor.0d1eeaf25aa1d2fc51b0.js.map differ
diff --git a/priv/static/static/js/vendor.360be732615100da981f.js b/priv/static/static/js/vendor.360be732615100da981f.js
deleted file mode 100644
index 1bdfb6fca..000000000
Binary files a/priv/static/static/js/vendor.360be732615100da981f.js and /dev/null differ
diff --git a/priv/static/static/js/vendor.360be732615100da981f.js.map b/priv/static/static/js/vendor.360be732615100da981f.js.map
deleted file mode 100644
index 5641e6272..000000000
Binary files a/priv/static/static/js/vendor.360be732615100da981f.js.map and /dev/null differ
diff --git a/priv/static/sw-pleroma.js b/priv/static/sw-pleroma.js
index c96edf186..3c00d22d5 100644
Binary files a/priv/static/sw-pleroma.js and b/priv/static/sw-pleroma.js differ
diff --git a/priv/static/sw.js b/priv/static/sw.js
index 4b7940486..d01b6b249 100644
Binary files a/priv/static/sw.js and b/priv/static/sw.js differ
diff --git a/test/bookmark_test.exs b/test/bookmark_test.exs
new file mode 100644
index 000000000..b81c102ef
--- /dev/null
+++ b/test/bookmark_test.exs
@@ -0,0 +1,52 @@
+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/emoji_test.exs b/test/emoji_test.exs
new file mode 100644
index 000000000..2eaa26be6
--- /dev/null
+++ b/test/emoji_test.exs
@@ -0,0 +1,106 @@
+defmodule Pleroma.EmojiTest do
+ use ExUnit.Case, async: true
+ alias Pleroma.Emoji
+
+ 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, path, tags} = emoji
+
+ assert tuple_size(emoji) == 3
+ 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, path, tags} = emoji
+
+ assert tuple_size(emoji) == 3
+ assert is_binary(code)
+ assert is_binary(path)
+ assert is_list(tags)
+ end
+ end
+
+ 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
+ |> Emoji.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
+ |> Emoji.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
+ |> Emoji.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
+ |> Emoji.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
+ |> Emoji.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
+ |> Emoji.match_extra("/emoji/custom/special.png")
+ |> to_string()
+
+ assert group == "special file"
+ end
+
+ test "no mathing returns nil", %{groups: groups} do
+ group =
+ groups
+ |> Emoji.match_extra("/emoji/some_undefined.png")
+
+ refute group
+ end
+ end
+end
diff --git a/test/fixtures/httpoison_mock/emelie.atom b/test/fixtures/httpoison_mock/emelie.atom
new file mode 100644
index 000000000..ddaa1c6ca
--- /dev/null
+++ b/test/fixtures/httpoison_mock/emelie.atom
@@ -0,0 +1,306 @@
+
+
+ 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't spending time with my pets, I'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'm going to make this vital change to my world building in the morning, no way I'll forget this, it'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's you people'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'm re-watching Tales from Earthsea for the first time since I read the books, and that Therru doesn'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't stop meowing for more, and I'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'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'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'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/httpoison_mock/status.emelie.json b/test/fixtures/httpoison_mock/status.emelie.json
new file mode 100644
index 000000000..4aada0377
--- /dev/null
+++ b/test/fixtures/httpoison_mock/status.emelie.json
@@ -0,0 +1,64 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ {
+ "ostatus": "http://ostatus.org#",
+ "atomUri": "ostatus:atomUri",
+ "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+ "conversation": "ostatus:conversation",
+ "sensitive": "as:sensitive",
+ "Hashtag": "as:Hashtag",
+ "toot": "http://joinmastodon.org/ns#",
+ "Emoji": "toot:Emoji",
+ "focalPoint": {
+ "@container": "@list",
+ "@id": "toot:focalPoint"
+ }
+ }
+ ],
+ "id": "https://mastodon.social/users/emelie/statuses/101849165031453009",
+ "type": "Note",
+ "summary": null,
+ "inReplyTo": null,
+ "published": "2019-04-01T05:02:05Z",
+ "url": "https://mastodon.social/@emelie/101849165031453009",
+ "attributedTo": "https://mastodon.social/users/emelie",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "cc": [
+ "https://mastodon.social/users/emelie/followers"
+ ],
+ "sensitive": false,
+ "atomUri": "https://mastodon.social/users/emelie/statuses/101849165031453009",
+ "inReplyToAtomUri": null,
+ "conversation": "tag:mastodon.social,2019-04-01:objectId=94350309:objectType=Conversation",
+ "content": "
You gotta take whatever bellyrubbing opportunity you can get before she changes her mind 🦁 #mastocats
",
+ "contentMap": {
+ "en": "
You gotta take whatever bellyrubbing opportunity you can get before she changes her mind 🦁 #mastocats
"
+ },
+ "attachment": [
+ {
+ "type": "Document",
+ "mediaType": "video/mp4",
+ "url": "https://files.mastodon.social/media_attachments/files/013/049/816/original/e7831178a5e0d6d4.mp4",
+ "name": null
+ }
+ ],
+ "tag": [
+ {
+ "type": "Hashtag",
+ "href": "https://mastodon.social/tags/mastocats",
+ "name": "#mastocats"
+ }
+ ],
+ "replies": {
+ "id": "https://mastodon.social/users/emelie/statuses/101849165031453009/replies",
+ "type": "Collection",
+ "first": {
+ "type": "CollectionPage",
+ "partOf": "https://mastodon.social/users/emelie/statuses/101849165031453009/replies",
+ "items": []
+ }
+ }
+}
diff --git a/test/fixtures/httpoison_mock/webfinger_emelie.json b/test/fixtures/httpoison_mock/webfinger_emelie.json
new file mode 100644
index 000000000..0b61cb618
--- /dev/null
+++ b/test/fixtures/httpoison_mock/webfinger_emelie.json
@@ -0,0 +1,36 @@
+{
+ "aliases": [
+ "https://mastodon.social/@emelie",
+ "https://mastodon.social/users/emelie"
+ ],
+ "links": [
+ {
+ "href": "https://mastodon.social/@emelie",
+ "rel": "http://webfinger.net/rel/profile-page",
+ "type": "text/html"
+ },
+ {
+ "href": "https://mastodon.social/users/emelie.atom",
+ "rel": "http://schemas.google.com/g/2010#updates-from",
+ "type": "application/atom+xml"
+ },
+ {
+ "href": "https://mastodon.social/users/emelie",
+ "rel": "self",
+ "type": "application/activity+json"
+ },
+ {
+ "href": "https://mastodon.social/api/salmon/15657",
+ "rel": "salmon"
+ },
+ {
+ "href": "data:application/magic-public-key,RSA.u3CWs1oAJPE3ZJ9sj6Ut_Mu-mTE7MOijsQc8_6c73XVVuhIEomiozJIH7l8a7S1n5SYL4UuiwcubSOi7u1bbGpYnp5TYhN-Cxvq_P80V4_ncNIPSQzS49it7nSLeG5pA21lGPDA44huquES1un6p9gSmbTwngVX9oe4MYuUeh0Z7vijjU13Llz1cRq_ZgPQPgfz-2NJf-VeXnvyDZDYxZPVBBlrMl3VoGbu0M5L8SjY35559KCZ3woIvqRolcoHXfgvJMdPcJgSZVYxlCw3dA95q9jQcn6s87CPSUs7bmYEQCrDVn5m5NER5TzwBmP4cgJl9AaDVWQtRd4jFZNTxlQ==.AQAB",
+ "rel": "magic-public-key"
+ },
+ {
+ "rel": "http://ostatus.org/schema/1.0/subscribe",
+ "template": "https://mastodon.social/authorize_interaction?uri={uri}"
+ }
+ ],
+ "subject": "acct:emelie@mastodon.social"
+}
diff --git a/test/fixtures/image.jpg b/test/fixtures/image.jpg
index 09834bb5c..edff6246b 100644
Binary files a/test/fixtures/image.jpg and b/test/fixtures/image.jpg differ
diff --git a/test/fixtures/lambadalambda.json b/test/fixtures/lambadalambda.json
new file mode 100644
index 000000000..1f09fb591
--- /dev/null
+++ b/test/fixtures/lambadalambda.json
@@ -0,0 +1,64 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "toot": "http://joinmastodon.org/ns#",
+ "featured": {
+ "@id": "toot:featured",
+ "@type": "@id"
+ },
+ "alsoKnownAs": {
+ "@id": "as:alsoKnownAs",
+ "@type": "@id"
+ },
+ "movedTo": {
+ "@id": "as:movedTo",
+ "@type": "@id"
+ },
+ "schema": "http://schema.org#",
+ "PropertyValue": "schema:PropertyValue",
+ "value": "schema:value",
+ "Hashtag": "as:Hashtag",
+ "Emoji": "toot:Emoji",
+ "IdentityProof": "toot:IdentityProof",
+ "focalPoint": {
+ "@container": "@list",
+ "@id": "toot:focalPoint"
+ }
+ }
+ ],
+ "id": "https://mastodon.social/users/lambadalambda",
+ "type": "Person",
+ "following": "https://mastodon.social/users/lambadalambda/following",
+ "followers": "https://mastodon.social/users/lambadalambda/followers",
+ "inbox": "https://mastodon.social/users/lambadalambda/inbox",
+ "outbox": "https://mastodon.social/users/lambadalambda/outbox",
+ "featured": "https://mastodon.social/users/lambadalambda/collections/featured",
+ "preferredUsername": "lambadalambda",
+ "name": "Critical Value",
+ "summary": "\u003cp\u003e\u003c/p\u003e",
+ "url": "https://mastodon.social/@lambadalambda",
+ "manuallyApprovesFollowers": false,
+ "publicKey": {
+ "id": "https://mastodon.social/users/lambadalambda#main-key",
+ "owner": "https://mastodon.social/users/lambadalambda",
+ "publicKeyPem": "-----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"
+ },
+ "tag": [],
+ "attachment": [],
+ "endpoints": {
+ "sharedInbox": "https://mastodon.social/inbox"
+ },
+ "icon": {
+ "type": "Image",
+ "mediaType": "image/gif",
+ "url": "https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif"
+ },
+ "image": {
+ "type": "Image",
+ "mediaType": "image/gif",
+ "url": "https://files.mastodon.social/accounts/headers/000/000/264/original/28b26104f83747d2.gif"
+ }
+}
diff --git a/test/formatter_test.exs b/test/formatter_test.exs
index 7d8864bf4..97eb2f583 100644
--- a/test/formatter_test.exs
+++ b/test/formatter_test.exs
@@ -181,6 +181,31 @@ test "does not give a replacement for single-character local nicknames who don't
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} hey dude i hate @#{third_user.nickname}"
+ {expected_text, mentions, [] = _tags} = Formatter.linkify(text, safe_mention: true)
+
+ assert mentions == [{"@#{user.nickname}", user}]
+
+ assert expected_text ==
+ "
@#{user.nickname} hey dude 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
end
describe ".parse_tags" do
@@ -220,10 +245,10 @@ test "it can parse mentions and return the relevant users" do
end
test "it adds cool emoji" do
- text = "I love :moominmamma:"
+ text = "I love :firefox:"
expected_result =
- "I love
"
+ "I love
"
assert Formatter.emojify(text) == expected_result
end
@@ -244,9 +269,11 @@ test "it does not add XSS emoji" do
end
test "it returns the emoji used in the text" do
- text = "I love :moominmamma:"
+ text = "I love :firefox:"
- assert Formatter.get_emoji(text) == [{"moominmamma", "/finmoji/128px/moominmamma-128.png"}]
+ assert Formatter.get_emoji(text) == [
+ {"firefox", "/emoji/Firefox.gif", ["Gif", "Fun"]}
+ ]
end
test "it returns a nice empty result when no emojis are present" do
diff --git a/test/healthcheck_test.exs b/test/healthcheck_test.exs
new file mode 100644
index 000000000..e05061220
--- /dev/null
+++ b/test/healthcheck_test.exs
@@ -0,0 +1,22 @@
+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, :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
index 29cab17f3..08738276e 100644
--- a/test/html_test.exs
+++ b/test/html_test.exs
@@ -10,6 +10,8 @@ defmodule Pleroma.HTMLTest do
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:
"""
@@ -18,12 +20,26 @@ defmodule Pleroma.HTMLTest do
"""
+ @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')
"""
@@ -44,6 +60,8 @@ test "normalizes HTML as expected" do
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')
"""
@@ -58,6 +76,36 @@ test "does not allow attribute-based XSS" do
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
@@ -66,6 +114,8 @@ test "normalizes HTML as expected" do
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')
"""
@@ -80,5 +130,34 @@ test "does not allow attribute-based XSS" do
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
end
diff --git a/test/jobs_test.exs b/test/jobs_test.exs
deleted file mode 100644
index d55c86ccc..000000000
--- a/test/jobs_test.exs
+++ /dev/null
@@ -1,83 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.JobsTest do
- use ExUnit.Case, async: true
-
- alias Jobs.WorkerMock
- alias Pleroma.Jobs
-
- setup do
- state = %{
- queues: Enum.into([Jobs.create_queue(:testing)], %{}),
- refs: %{}
- }
-
- [state: state]
- end
-
- test "creates queue" do
- queue = Jobs.create_queue(:foobar)
-
- assert {:foobar, set} = queue
- assert :set == elem(set, 0) |> elem(0)
- end
-
- test "enqueues an element according to priority" do
- queue = [%{item: 1, priority: 2}]
-
- new_queue = Jobs.enqueue_sorted(queue, 2, 1)
- assert new_queue == [%{item: 2, priority: 1}, %{item: 1, priority: 2}]
-
- new_queue = Jobs.enqueue_sorted(queue, 2, 3)
- assert new_queue == [%{item: 1, priority: 2}, %{item: 2, priority: 3}]
- end
-
- test "pop first item" do
- queue = [%{item: 2, priority: 1}, %{item: 1, priority: 2}]
-
- assert {2, [%{item: 1, priority: 2}]} = Jobs.queue_pop(queue)
- end
-
- test "enqueue a job", %{state: state} do
- assert {:noreply, new_state} =
- Jobs.handle_cast({:enqueue, :testing, WorkerMock, [:test_job, :foo, :bar], 3}, state)
-
- assert %{queues: %{testing: {running_jobs, []}}, refs: _} = new_state
- assert :sets.size(running_jobs) == 1
- assert [ref] = :sets.to_list(running_jobs)
- assert %{refs: %{^ref => :testing}} = new_state
- end
-
- test "max jobs setting", %{state: state} do
- max_jobs = Pleroma.Config.get([Jobs, :testing, :max_jobs])
-
- {:noreply, state} =
- Enum.reduce(1..(max_jobs + 1), {:noreply, state}, fn _, {:noreply, state} ->
- Jobs.handle_cast({:enqueue, :testing, WorkerMock, [:test_job, :foo, :bar], 3}, state)
- end)
-
- assert %{
- queues: %{
- testing:
- {running_jobs, [%{item: {WorkerMock, [:test_job, :foo, :bar]}, priority: 3}]}
- }
- } = state
-
- assert :sets.size(running_jobs) == max_jobs
- end
-
- test "remove job after it finished", %{state: state} do
- {:noreply, new_state} =
- Jobs.handle_cast({:enqueue, :testing, WorkerMock, [:test_job, :foo, :bar], 3}, state)
-
- %{queues: %{testing: {running_jobs, []}}} = new_state
- [ref] = :sets.to_list(running_jobs)
-
- assert {:noreply, %{queues: %{testing: {running_jobs, []}}, refs: %{}}} =
- Jobs.handle_info({:DOWN, ref, :process, nil, nil}, new_state)
-
- assert :sets.size(running_jobs) == 0
- end
-end
diff --git a/test/media_proxy_test.exs b/test/media_proxy_test.exs
index ddbadfbf5..a4331478e 100644
--- a/test/media_proxy_test.exs
+++ b/test/media_proxy_test.exs
@@ -177,4 +177,13 @@ defp decode_result(encoded) do
{:ok, decoded} = decode_url(sig, base64)
decoded
end
+
+ test "mediaproxy whitelist" do
+ Pleroma.Config.put([:media_proxy, :enabled], true)
+ Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"])
+ url = "https://feld.me/foo.png"
+
+ unencoded = url(url)
+ assert unencoded == url
+ end
end
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 12b4292aa..581db58a8 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -29,21 +29,102 @@ test "notifies someone when they are directly addressed" do
assert notification.activity_id == activity.id
assert other_notification.activity_id == activity.id
end
+
+ test "it creates a notification for subscribed users" do
+ user = insert(:user)
+ subscriber = insert(:user)
+
+ User.subscribe(subscriber, user)
+
+ {:ok, status} = TwitterAPI.create_status(user, %{"status" => "Akariiiin"})
+ {:ok, [notification]} = Notification.create_notifications(status)
+
+ assert notification.user_id == subscriber.id
+ end
end
describe "create_notification" do
test "it doesn't create a notification for user if the user blocks the activity author" do
activity = insert(:note_activity)
- author = User.get_by_ap_id(activity.data["actor"])
+ author = User.get_cached_by_ap_id(activity.data["actor"])
user = insert(:user)
{:ok, user} = User.block(user, author)
assert nil == Notification.create_notification(activity, user)
end
+ test "it doesn't create a notificatin 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}"})
+
+ assert nil == Notification.create_notification(activity, muter)
+ end
+
+ test "it doesn't create 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
+ })
+
+ assert nil == Notification.create_notification(activity, muter)
+ end
+
+ test "it disables notifications from people on remote instances" do
+ user = insert(:user, info: %{notification_settings: %{"remote" => false}})
+ other_user = insert(:user)
+
+ create_activity = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "type" => "Create",
+ "to" => ["https://www.w3.org/ns/activitystreams#Public"],
+ "actor" => other_user.ap_id,
+ "object" => %{
+ "type" => "Note",
+ "content" => "Hi @#{user.nickname}",
+ "attributedTo" => other_user.ap_id
+ }
+ }
+
+ {:ok, %{local: false} = activity} = Transmogrifier.handle_incoming(create_activity)
+ assert nil == Notification.create_notification(activity, user)
+ end
+
+ test "it disables notifications from people on the local instance" do
+ user = insert(:user, info: %{notification_settings: %{"local" => false}})
+ other_user = insert(:user)
+ {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
+ assert nil == Notification.create_notification(activity, user)
+ end
+
+ test "it disables notifications from followers" do
+ follower = insert(:user)
+ followed = insert(:user, info: %{notification_settings: %{"followers" => false}})
+ User.follow(follower, followed)
+ {:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
+ assert nil == Notification.create_notification(activity, followed)
+ end
+
+ test "it disables notifications from people the user follows" do
+ follower = insert(:user, info: %{notification_settings: %{"follows" => false}})
+ followed = insert(:user)
+ User.follow(follower, followed)
+ follower = Repo.get(User, follower.id)
+ {:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
+ assert nil == Notification.create_notification(activity, follower)
+ end
+
test "it doesn't create a notification for user if he is the activity author" do
activity = insert(:note_activity)
- author = User.get_by_ap_id(activity.data["actor"])
+ author = User.get_cached_by_ap_id(activity.data["actor"])
assert nil == Notification.create_notification(activity, author)
end
@@ -84,6 +165,28 @@ test "it doesn't create a notification for repeat-unrepeat-repeat chains" do
{:ok, dupe} = TwitterAPI.repeat(user, status.id)
assert nil == Notification.create_notification(dupe, retweeted_user)
end
+
+ test "it doesn't create duplicate notifications for follow+subscribed users" do
+ user = insert(:user)
+ subscriber = insert(:user)
+
+ {:ok, _, _, _} = TwitterAPI.follow(subscriber, %{"user_id" => user.id})
+ User.subscribe(subscriber, user)
+ {:ok, status} = TwitterAPI.create_status(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} =
+ TwitterAPI.create_status(user, %{"status" => "inwisible", "visibility" => "direct"})
+
+ assert {:ok, []} == Notification.create_notifications(status)
+ end
end
describe "get notification" do
diff --git a/test/object/containment_test.exs b/test/object/containment_test.exs
new file mode 100644
index 000000000..452064093
--- /dev/null
+++ b/test/object/containment_test.exs
@@ -0,0 +1,58 @@
+defmodule Pleroma.Object.ContainmentTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Object.Containment
+ alias Pleroma.User
+
+ import Pleroma.Factory
+
+ describe "general origin containment" do
+ 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"})
+ })
+
+ {:error, _} = User.get_or_fetch_by_ap_id("https://n1u.moe/users/rye")
+ end
+ end
+end
diff --git a/test/object/fetcher_test.exs b/test/object/fetcher_test.exs
new file mode 100644
index 000000000..72f616782
--- /dev/null
+++ b/test/object/fetcher_test.exs
@@ -0,0 +1,90 @@
+defmodule Pleroma.Object.FetcherTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Activity
+ alias Pleroma.Object
+ alias Pleroma.Object.Fetcher
+ import Tesla.Mock
+
+ setup do
+ mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ 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 "it works with objects only available via Ostatus" do
+ {:ok, object} = Fetcher.fetch_object_from_id("https://shitposter.club/notice/2827873")
+ assert activity = Activity.get_create_by_object_ap_id(object.data["id"])
+ assert activity.data["id"]
+
+ {:ok, object_again} = Fetcher.fetch_object_from_id("https://shitposter.club/notice/2827873")
+
+ assert object == object_again
+ end
+
+ test "it correctly stitches up conversations between ostatus and ap" do
+ last = "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
+ {:ok, object} = Fetcher.fetch_object_from_id(last)
+
+ object = Object.get_by_ap_id(object.data["inReplyTo"])
+ assert object
+ 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 "all objects with fake directions are rejected by the object fetcher" do
+ {:error, _} =
+ Fetcher.fetch_and_contain_remote_object_from_id(
+ "https://info.pleroma.site/activity4.json"
+ )
+ end
+ end
+end
diff --git a/test/object_test.exs b/test/object_test.exs
index 911757d57..d138ee091 100644
--- a/test/object_test.exs
+++ b/test/object_test.exs
@@ -5,9 +5,15 @@
defmodule Pleroma.ObjectTest do
use Pleroma.DataCase
import Pleroma.Factory
+ import Tesla.Mock
alias Pleroma.Object
alias Pleroma.Repo
+ 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"])
@@ -58,4 +64,26 @@ test "ensures cache is cleared for the object" do
assert cached_object.data["type"] == "Tombstone"
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
end
diff --git a/test/plugs/legacy_authentication_plug_test.exs b/test/plugs/legacy_authentication_plug_test.exs
index 302662797..8b0b06772 100644
--- a/test/plugs/legacy_authentication_plug_test.exs
+++ b/test/plugs/legacy_authentication_plug_test.exs
@@ -47,16 +47,18 @@ test "it authenticates the auth_user if present and password is correct and rese
|> assign(:auth_user, user)
conn =
- with_mock User,
- reset_password: fn user, %{password: password, password_confirmation: password} ->
- send(self(), :reset_password)
- {:ok, user}
- end do
- conn
- |> LegacyAuthenticationPlug.call(%{})
+ with_mocks([
+ {:crypt, [], [crypt: fn _password, password_hash -> password_hash end]},
+ {User, [],
+ [
+ reset_password: fn user, %{password: password, password_confirmation: password} ->
+ {:ok, user}
+ end
+ ]}
+ ]) do
+ LegacyAuthenticationPlug.call(conn, %{})
end
- assert_received :reset_password
assert conn.assigns.user == user
end
diff --git a/test/plugs/uploaded_media_plug_test.exs b/test/plugs/uploaded_media_plug_test.exs
new file mode 100644
index 000000000..49cf5396a
--- /dev/null
+++ b/test/plugs/uploaded_media_plug_test.exs
@@ -0,0 +1,43 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors
+# 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/registration_test.exs b/test/registration_test.exs
new file mode 100644
index 000000000..6143b82c7
--- /dev/null
+++ b/test/registration_test.exs
@@ -0,0 +1,59 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 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/scheduled_activity_test.exs b/test/scheduled_activity_test.exs
new file mode 100644
index 000000000..edc7cc3f9
--- /dev/null
+++ b/test/scheduled_activity_test.exs
@@ -0,0 +1,64 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 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 context do
+ DataCase.ensure_local_uploader(context)
+ end
+
+ describe "creation" do
+ 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/scheduled_activity_worker_test.exs b/test/scheduled_activity_worker_test.exs
new file mode 100644
index 000000000..e3ad1244e
--- /dev/null
+++ b/test/scheduled_activity_worker_test.exs
@@ -0,0 +1,19 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.ScheduledActivityWorkerTest do
+ use Pleroma.DataCase
+ alias Pleroma.ScheduledActivity
+ import Pleroma.Factory
+
+ test "creates a status from the scheduled activity" do
+ user = insert(:user)
+ scheduled_activity = insert(:scheduled_activity, user: user, params: %{status: "hi"})
+ Pleroma.ScheduledActivityWorker.perform(:execute, 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
+end
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 18f77f01a..ea59912cf 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -216,7 +216,7 @@ def oauth_app_factory do
redirect_uris: "https://example.com/callback",
scopes: ["read", "write", "follow", "push"],
website: "https://example.com",
- client_id: "aaabbb==",
+ client_id: Ecto.UUID.generate(),
client_secret: "aaa;/&bbb"
}
end
@@ -240,6 +240,16 @@ def oauth_token_factory do
}
end
+ def oauth_authorization_factory do
+ %Pleroma.Web.OAuth.Authorization{
+ token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false),
+ scopes: ["read", "write", "follow", "push"],
+ valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10),
+ user: build(:user),
+ app: build(:oauth_app)
+ }
+ end
+
def push_subscription_factory do
%Pleroma.Web.Push.Subscription{
user: build(:user),
@@ -257,4 +267,28 @@ def notification_factory do
user: build(:user)
}
end
+
+ def scheduled_activity_factory do
+ %Pleroma.ScheduledActivity{
+ user: build(:user),
+ scheduled_at: NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(60), :millisecond),
+ params: build(:note) |> Map.from_struct() |> Map.get(:data)
+ }
+ end
+
+ def registration_factory do
+ user = insert(:user)
+
+ %Pleroma.Registration{
+ user: user,
+ provider: "twitter",
+ uid: "171799000",
+ info: %{
+ "name" => "John Doe",
+ "email" => "john@doe.com",
+ "nickname" => "johndoe",
+ "description" => "My bio"
+ }
+ }
+ end
end
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index 78e8efc9d..5b355bfe6 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -36,6 +36,43 @@ def get("https://osada.macgirvin.com/channel/mike", _, _, _) do
}}
end
+ def get("https://mastodon.social/users/emelie/statuses/101849165031453009", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/httpoison_mock/status.emelie.json")
+ }}
+ end
+
+ def get("https://mastodon.social/users/emelie", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/httpoison_mock/emelie.json")
+ }}
+ end
+
+ def get(
+ "https://mastodon.social/.well-known/webfinger?resource=https://mastodon.social/users/emelie",
+ _,
+ _,
+ _
+ ) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/httpoison_mock/webfinger_emelie.json")
+ }}
+ end
+
+ def get("https://mastodon.social/users/emelie.atom", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/httpoison_mock/emelie.atom")
+ }}
+ end
+
def get(
"https://osada.macgirvin.com/.well-known/webfinger?resource=acct:mike@osada.macgirvin.com",
_,
@@ -679,6 +716,10 @@ 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
+
def get("https://social.heldscal.la/user/23211", _, _, Accept: "application/activity+json") do
{:ok, Tesla.Mock.json(%{"id" => "https://social.heldscal.la/user/23211"}, status: 200)}
end
diff --git a/test/support/jobs_worker_mock.ex b/test/support/jobs_worker_mock.ex
deleted file mode 100644
index 0fb976d05..000000000
--- a/test/support/jobs_worker_mock.ex
+++ /dev/null
@@ -1,19 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Jobs.WorkerMock do
- require Logger
-
- def perform(:test_job, arg, arg2) do
- Logger.debug({:perform, :test_job, arg, arg2})
- end
-
- def perform(:test_job, payload) do
- Logger.debug({:perform, :test_job, payload})
- end
-
- def test_job(payload) do
- Pleroma.Jobs.enqueue(:testing, __MODULE__, [:test_job, payload])
- end
-end
diff --git a/test/tasks/instance.exs b/test/tasks/instance.exs
new file mode 100644
index 000000000..6917a2376
--- /dev/null
+++ b/test/tasks/instance.exs
@@ -0,0 +1,62 @@
+defmodule Pleroma.InstanceTest do
+ use ExUnit.Case, async: true
+
+ setup do
+ File.mkdir_p!(tmp_path())
+ on_exit(fn -> File.rm_rf(tmp_path()) 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"
+ ])
+ 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 File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
+ 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\";\n)
+ end
+end
diff --git a/test/tasks/relay_test.exs b/test/tasks/relay_test.exs
index c9d90fa2e..9d260da3e 100644
--- a/test/tasks/relay_test.exs
+++ b/test/tasks/relay_test.exs
@@ -31,7 +31,7 @@ test "relay is followed" do
local_user = Relay.get_actor()
assert local_user.ap_id =~ "/relay"
- target_user = User.get_by_ap_id(target_instance)
+ target_user = User.get_cached_by_ap_id(target_instance)
refute target_user.local
activity = Utils.fetch_latest_follow(local_user, target_user)
@@ -48,7 +48,7 @@ test "relay is unfollowed" do
Mix.Tasks.Pleroma.Relay.run(["follow", target_instance])
%User{ap_id: follower_id} = local_user = Relay.get_actor()
- target_user = User.get_by_ap_id(target_instance)
+ target_user = User.get_cached_by_ap_id(target_instance)
follow_activity = Utils.fetch_latest_follow(local_user, target_user)
Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance])
@@ -60,7 +60,8 @@ test "relay is unfollowed" do
ActivityPub.fetch_activities([], %{
"type" => "Undo",
"actor_id" => follower_id,
- "limit" => 1
+ "limit" => 1,
+ "skip_preload" => true
})
assert undo_activity.data["type"] == "Undo"
diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs
index 7b814d171..eaf4ecf84 100644
--- a/test/tasks/user_test.exs
+++ b/test/tasks/user_test.exs
@@ -50,7 +50,7 @@ test "user is created" do
assert_received {:mix_shell, :info, [message]}
assert message =~ "created"
- user = User.get_by_nickname(unsaved.nickname)
+ user = User.get_cached_by_nickname(unsaved.nickname)
assert user.name == unsaved.name
assert user.email == unsaved.email
assert user.bio == unsaved.bio
@@ -75,7 +75,7 @@ test "user is not created" do
assert_received {:mix_shell, :info, [message]}
assert message =~ "will not be created"
- refute User.get_by_nickname(unsaved.nickname)
+ refute User.get_cached_by_nickname(unsaved.nickname)
end
end
@@ -88,7 +88,7 @@ test "user is deleted" do
assert_received {:mix_shell, :info, [message]}
assert message =~ " deleted"
- user = User.get_by_nickname(user.nickname)
+ user = User.get_cached_by_nickname(user.nickname)
assert user.info.deactivated
end
@@ -109,7 +109,7 @@ test "user is deactivated" do
assert_received {:mix_shell, :info, [message]}
assert message =~ " deactivated"
- user = User.get_by_nickname(user.nickname)
+ user = User.get_cached_by_nickname(user.nickname)
assert user.info.deactivated
end
@@ -121,7 +121,7 @@ test "user is activated" do
assert_received {:mix_shell, :info, [message]}
assert message =~ " activated"
- user = User.get_by_nickname(user.nickname)
+ user = User.get_cached_by_nickname(user.nickname)
refute user.info.deactivated
end
@@ -150,7 +150,7 @@ test "user is unsubscribed" do
assert_received {:mix_shell, :info, [message]}
assert message =~ "Successfully unsubscribed"
- user = User.get_by_nickname(user.nickname)
+ user = User.get_cached_by_nickname(user.nickname)
assert Enum.empty?(user.following)
assert user.info.deactivated
end
@@ -178,7 +178,7 @@ test "All statuses set" do
assert_received {:mix_shell, :info, [message]}
assert message =~ ~r/Admin status .* true/
- user = User.get_by_nickname(user.nickname)
+ user = User.get_cached_by_nickname(user.nickname)
assert user.info.is_moderator
assert user.info.locked
assert user.info.is_admin
@@ -204,7 +204,7 @@ test "All statuses unset" do
assert_received {:mix_shell, :info, [message]}
assert message =~ ~r/Admin status .* false/
- user = User.get_by_nickname(user.nickname)
+ user = User.get_cached_by_nickname(user.nickname)
refute user.info.is_moderator
refute user.info.locked
refute user.info.is_admin
@@ -245,7 +245,97 @@ test "invite token is generated" do
end) =~ "http"
assert_received {:mix_shell, :info, [message]}
- assert message =~ "Generated"
+ 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
+ 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
end
end
diff --git a/test/upload_test.exs b/test/upload_test.exs
index bdda01b3f..946ebcb5a 100644
--- a/test/upload_test.exs
+++ b/test/upload_test.exs
@@ -56,7 +56,7 @@ test "copies the file to the configured folder with deduping" do
assert List.first(data["url"])["href"] ==
Pleroma.Web.base_url() <>
- "/media/e7a6d0cf595bff76f14c9a98b6c199539559e8b844e02e51e5efcfd1f614a2df.jpg"
+ "/media/e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781.jpg"
end
test "copies the file to the configured folder without deduping" do
diff --git a/test/user_invite_token_test.exs b/test/user_invite_token_test.exs
new file mode 100644
index 000000000..276788254
--- /dev/null
+++ b/test/user_invite_token_test.exs
@@ -0,0 +1,96 @@
+defmodule Pleroma.UserInviteTokenTest do
+ use ExUnit.Case, async: true
+ use Pleroma.DataCase
+ 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)}
+ invite = Repo.insert!(invite)
+ 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)}
+ invite = Repo.insert!(invite)
+ 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}
+ invite = Repo.insert!(invite)
+ refute UserInviteToken.valid_invite?(invite)
+ end
+ end
+end
diff --git a/test/user_test.exs b/test/user_test.exs
index c57eb2c06..7be47e5fb 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -5,9 +5,11 @@
defmodule Pleroma.UserTest do
alias Pleroma.Activity
alias Pleroma.Builders.UserBuilder
+ alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI
+
use Pleroma.DataCase
import Pleroma.Factory
@@ -121,9 +123,9 @@ test "follow takes a user and another user" do
{:ok, user} = User.follow(user, followed)
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
- followed = User.get_by_ap_id(followed.ap_id)
+ followed = User.get_cached_by_ap_id(followed.ap_id)
assert followed.info.follower_count == 1
assert User.ap_followers(followed) in user.following
@@ -145,6 +147,15 @@ test "can't follow a user who blocked us" do
{:error, _} = User.follow(blockee, blocker)
end
+ test "can't subscribe to a user who blocked us" do
+ blocker = insert(:user)
+ blocked = insert(:user)
+
+ {:ok, blocker} = User.block(blocker, blocked)
+
+ {:error, _} = User.subscribe(blocked, blocker)
+ end
+
test "local users do not automatically follow local locked accounts" do
follower = insert(:user, info: %{locked: true})
followed = insert(:user, info: %{locked: true})
@@ -177,7 +188,7 @@ test "unfollow takes a user and another user" do
{:ok, user, _activity} = User.unfollow(user, followed)
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert user.following == []
end
@@ -187,7 +198,7 @@ test "unfollow doesn't unfollow yourself" do
{:error, _} = User.unfollow(user, user)
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert user.following == [user.ap_id]
end
@@ -199,6 +210,13 @@ test "test if a user is following another user" do
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})
+ userwithnumbers = insert(:user, %{nickname: "#{user.id}garbage"})
+ assert userwithnumbers == User.get_cached_by_nickname_or_id(userwithnumbers.nickname)
+ end
+
describe "user registration" do
@full_user_data %{
bio: "A guy",
@@ -239,7 +257,7 @@ test "it sends a welcome message if it is set" do
activity = Repo.one(Pleroma.Activity)
assert registered_user.ap_id in activity.recipients
- assert activity.data["object"]["content"] =~ "cool site"
+ assert Object.normalize(activity).data["content"] =~ "cool site"
assert activity.actor == welcome_user.ap_id
Pleroma.Config.put([:instance, :welcome_user_nickname], nil)
@@ -538,8 +556,8 @@ test "gets all friends (followed users) for a given user" do
{:ok, res} = User.get_friends(user)
- followed_one = User.get_by_ap_id(followed_one.ap_id)
- followed_two = User.get_by_ap_id(followed_two.ap_id)
+ 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)
@@ -550,7 +568,7 @@ test "gets all friends (followed users) for a given user" do
test "it sets the info->note_count property" do
note = insert(:note)
- user = User.get_by_ap_id(note.data["actor"])
+ user = User.get_cached_by_ap_id(note.data["actor"])
assert user.info.note_count == 0
@@ -561,7 +579,7 @@ test "it sets the info->note_count property" do
test "it increases the info->note_count property" do
note = insert(:note)
- user = User.get_by_ap_id(note.data["actor"])
+ user = User.get_cached_by_ap_id(note.data["actor"])
assert user.info.note_count == 0
@@ -576,7 +594,7 @@ test "it increases the info->note_count property" do
test "it decreases the info->note_count property" do
note = insert(:note)
- user = User.get_by_ap_id(note.data["actor"])
+ user = User.get_cached_by_ap_id(note.data["actor"])
assert user.info.note_count == 0
@@ -678,7 +696,7 @@ test "blocks tear down cyclical follow relationships" do
assert User.following?(blocked, blocker)
{:ok, blocker} = User.block(blocker, blocked)
- blocked = Repo.get(User, blocked.id)
+ blocked = User.get_cached_by_id(blocked.id)
assert User.blocks?(blocker, blocked)
@@ -696,7 +714,7 @@ test "blocks tear down blocker->blocked follow relationships" do
refute User.following?(blocked, blocker)
{:ok, blocker} = User.block(blocker, blocked)
- blocked = Repo.get(User, blocked.id)
+ blocked = User.get_cached_by_id(blocked.id)
assert User.blocks?(blocker, blocked)
@@ -714,13 +732,29 @@ test "blocks tear down blocked->blocker follow relationships" do
assert User.following?(blocked, blocker)
{:ok, blocker} = User.block(blocker, blocked)
- blocked = Repo.get(User, blocked.id)
+ 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, blocker} = User.subscribe(blocked, blocker)
+
+ assert User.subscribed_to?(blocked, blocker)
+ refute User.subscribed_to?(blocker, blocked)
+
+ {:ok, blocker} = 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
@@ -791,6 +825,16 @@ test ".deactivate can de-activate then re-activate a user" do
assert false == user.info.deactivated
end
+ test ".delete_user_activities deletes all create activities" do
+ user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
+ {:ok, _} = User.delete_user_activities(user)
+
+ # TODO: Remove favorites, repeats, delete activities.
+ refute Activity.get_by_id(activity.id)
+ end
+
test ".delete deactivates a user, all follow relationships and all create activities" do
user = insert(:user)
followed = insert(:user)
@@ -808,9 +852,9 @@ test ".delete deactivates a user, all follow relationships and all create activi
{:ok, _} = User.delete(user)
- followed = Repo.get(User, followed.id)
- follower = Repo.get(User, follower.id)
- user = Repo.get(User, user.id)
+ followed = User.get_cached_by_id(followed.id)
+ follower = User.get_cached_by_id(follower.id)
+ user = User.get_cached_by_id(user.id)
assert user.info.deactivated
@@ -819,7 +863,7 @@ test ".delete deactivates a user, all follow relationships and all create activi
# TODO: Remove favorites, repeats, delete activities.
- refute Repo.get(Activity, activity.id)
+ refute Activity.get_by_id(activity.id)
end
test "get_public_key_for_ap_id fetches a user that's not in the db" do
@@ -879,7 +923,11 @@ test "finds a user by full or partial nickname" do
user = insert(:user, %{nickname: "john"})
Enum.each(["john", "jo", "j"], fn query ->
- assert user == User.search(query) |> List.first() |> Map.put(:search_rank, nil)
+ assert user ==
+ User.search(query)
+ |> List.first()
+ |> Map.put(:search_rank, nil)
+ |> Map.put(:search_type, nil)
end)
end
@@ -887,7 +935,11 @@ test "finds a user by full or partial name" 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() |> Map.put(:search_rank, nil)
+ assert user ==
+ User.search(query)
+ |> List.first()
+ |> Map.put(:search_rank, nil)
+ |> Map.put(:search_type, nil)
end)
end
@@ -941,6 +993,7 @@ test "finds a user whose name is nil" do
User.search("lain@pleroma.soykaf.com")
|> List.first()
|> Map.put(:search_rank, nil)
+ |> Map.put(:search_type, nil)
end
test "does not yield false-positive matches" do
@@ -955,10 +1008,10 @@ test "works with URIs" do
results = User.search("http://mastodon.example.org/users/admin", resolve: true)
result = results |> List.first()
- user = User.get_by_ap_id("http://mastodon.example.org/users/admin")
+ user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
assert length(results) == 1
- assert user == result |> Map.put(:search_rank, nil)
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
end
end
@@ -1072,30 +1125,20 @@ test "Adds rel=me on linkbacked urls" do
end
end
- test "bookmarks" do
+ test "follower count is updated when a follower is blocked" do
user = insert(:user)
+ follower = insert(:user)
+ follower2 = insert(:user)
+ follower3 = insert(:user)
- {:ok, activity1} =
- CommonAPI.post(user, %{
- "status" => "heweoo!"
- })
+ {:ok, follower} = Pleroma.User.follow(follower, user)
+ {:ok, _follower2} = Pleroma.User.follow(follower2, user)
+ {:ok, _follower3} = Pleroma.User.follow(follower3, user)
- id1 = activity1.data["object"]["id"]
+ {:ok, _} = Pleroma.User.block(user, follower)
- {:ok, activity2} =
- CommonAPI.post(user, %{
- "status" => "heweoo!"
- })
+ user_show = Pleroma.Web.TwitterAPI.UserView.render("show.json", %{user: user})
- id2 = activity2.data["object"]["id"]
-
- assert {:ok, user_state1} = User.bookmark(user, id1)
- assert user_state1.bookmarks == [id1]
-
- assert {:ok, user_state2} = User.unbookmark(user, id1)
- assert user_state2.bookmarks == []
-
- assert {:ok, user_state3} = User.bookmark(user, id2)
- assert user_state3.bookmarks == [id2]
+ assert Map.get(user_show, "followers_count") == 2
end
end
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs
index a1e83b380..30adfda36 100644
--- a/test/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/web/activity_pub/activity_pub_controller_test.exs
@@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
alias Pleroma.Activity
alias Pleroma.Instances
alias Pleroma.Object
- alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectView
alias Pleroma.Web.ActivityPub.UserView
@@ -51,7 +50,7 @@ test "it returns a json representation of the user with accept application/json"
|> put_req_header("accept", "application/json")
|> get("/users/#{user.nickname}")
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
end
@@ -66,7 +65,7 @@ test "it returns a json representation of the user with accept application/activ
|> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}")
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
end
@@ -84,7 +83,7 @@ test "it returns a json representation of the user with accept application/ld+js
)
|> get("/users/#{user.nickname}")
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
end
@@ -254,6 +253,36 @@ test "it inserts an incoming activity into the database", %{conn: conn} do
assert Activity.get_by_ap_id(data["id"])
end
+ test "it accepts messages from actors that are followed by the user", %{conn: conn} do
+ recipient = insert(:user)
+ actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
+
+ {:ok, recipient} = User.follow(recipient, actor)
+
+ data =
+ File.read!("test/fixtures/mastodon-post-activity.json")
+ |> Poison.decode!()
+
+ 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)
+ :timer.sleep(500)
+ assert Activity.get_by_ap_id(data["id"])
+ end
+
test "it rejects reads from other users", %{conn: conn} do
user = insert(:user)
otheruser = insert(:user)
@@ -543,7 +572,7 @@ test "it works for more than 10 users", %{conn: conn} do
user = insert(:user)
Enum.each(1..15, fn _ ->
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
other_user = insert(:user)
User.follow(user, other_user)
end)
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 2b83bfb1d..f8e987e58 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -84,17 +84,21 @@ test "it fetches the appropriate tag-restricted posts" do
{:ok, status_two} = CommonAPI.post(user, %{"status" => ". #essais"})
{:ok, status_three} = CommonAPI.post(user, %{"status" => ". #test #reject"})
- fetch_one = ActivityPub.fetch_activities([], %{"tag" => "test"})
- fetch_two = ActivityPub.fetch_activities([], %{"tag" => ["test", "essais"]})
+ 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"]
})
@@ -140,7 +144,7 @@ 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 == new_activity
+ 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
@@ -192,8 +196,10 @@ test "adds an id to a given object if it lacks one and is a note and inserts it
}
{:ok, %Activity{} = activity} = ActivityPub.insert(data)
- assert is_binary(activity.data["object"]["id"])
- assert %Object{} = Object.get_by_ap_id(activity.data["object"]["id"])
+ object = Object.normalize(activity.data["object"])
+
+ assert is_binary(object.data["id"])
+ assert %Object{} = Object.get_by_ap_id(activity.data["object"])
end
end
@@ -206,7 +212,11 @@ test "removes doubled 'to' recipients" do
to: ["user1", "user1", "user2"],
actor: user,
context: "",
- object: %{}
+ object: %{
+ "to" => ["user1", "user1", "user2"],
+ "type" => "Note",
+ "content" => "testing"
+ }
})
assert activity.data["to"] == ["user1", "user2"]
@@ -218,20 +228,61 @@ test "increases user note count only for public activities" do
user = insert(:user)
{:ok, _} =
- CommonAPI.post(Repo.get(User, user.id), %{"status" => "1", "visibility" => "public"})
+ CommonAPI.post(User.get_cached_by_id(user.id), %{
+ "status" => "1",
+ "visibility" => "public"
+ })
{:ok, _} =
- CommonAPI.post(Repo.get(User, user.id), %{"status" => "2", "visibility" => "unlisted"})
+ CommonAPI.post(User.get_cached_by_id(user.id), %{
+ "status" => "2",
+ "visibility" => "unlisted"
+ })
{:ok, _} =
- CommonAPI.post(Repo.get(User, user.id), %{"status" => "2", "visibility" => "private"})
+ CommonAPI.post(User.get_cached_by_id(user.id), %{
+ "status" => "2",
+ "visibility" => "private"
+ })
{:ok, _} =
- CommonAPI.post(Repo.get(User, user.id), %{"status" => "3", "visibility" => "direct"})
+ CommonAPI.post(User.get_cached_by_id(user.id), %{
+ "status" => "3",
+ "visibility" => "direct"
+ })
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert user.info.note_count == 2
end
+
+ test "increases replies count" do
+ user = insert(:user)
+ 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
@@ -270,7 +321,8 @@ test "doesn't return blocked activities" do
booster = insert(:user)
{:ok, user} = User.block(user, %{ap_id: activity_one.data["actor"]})
- activities = ActivityPub.fetch_activities([], %{"blocking_user" => user})
+ activities =
+ ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@@ -278,7 +330,8 @@ test "doesn't return blocked activities" do
{:ok, user} = User.unblock(user, %{ap_id: activity_one.data["actor"]})
- activities = ActivityPub.fetch_activities([], %{"blocking_user" => user})
+ activities =
+ ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@@ -287,16 +340,18 @@ test "doesn't return blocked activities" do
{:ok, user} = User.block(user, %{ap_id: activity_three.data["actor"]})
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
- activity_three = Repo.get(Activity, activity_three.id)
+ activity_three = Activity.get_by_id(activity_three.id)
- activities = ActivityPub.fetch_activities([], %{"blocking_user" => user})
+ 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})
+ activities =
+ ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@@ -304,6 +359,51 @@ test "doesn't return blocked activities" do
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, blocker} = 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 concerning blocked users" do
+ blocker = insert(:user)
+ blockee = insert(:user)
+ friend = insert(:user)
+
+ {:ok, blocker} = 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 muted activities" do
activity_one = insert(:note_activity)
activity_two = insert(:note_activity)
@@ -312,14 +412,20 @@ test "doesn't return muted activities" do
booster = insert(:user)
{:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]})
- activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
+ 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})
+ 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)
@@ -327,7 +433,8 @@ test "doesn't return muted activities" do
{:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]})
- activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
+ activities =
+ ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@@ -336,16 +443,17 @@ test "doesn't return muted activities" do
{:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]})
{:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster)
%Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id)
- activity_three = Repo.get(Activity, activity_three.id)
+ activity_three = Activity.get_by_id(activity_three.id)
- activities = ActivityPub.fetch_activities([], %{"muting_user" => user})
+ 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})
+ activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true})
assert Enum.member?(activities, activity_two)
assert Enum.member?(activities, activity_three)
@@ -353,6 +461,20 @@ test "doesn't return muted activities" do
assert Enum.member?(activities, activity_one)
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, _object} = CommonAPI.repeat(activity_three.id, booster)
+
+ [announce_activity] = ActivityPub.fetch_activities([user.ap_id | user.following])
+
+ 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})
@@ -424,6 +546,33 @@ test "retrieves ids up to max_id" do
assert length(activities) == 20
assert last == last_expected
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, user} = 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, user} = CommonAPI.hide_reblogs(user, booster)
+ {:ok, user} = 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 "like an object" do
@@ -473,7 +622,7 @@ test "unliking a previously liked object" do
{:ok, _, _, object} = ActivityPub.unlike(user, object)
assert object.data["like_count"] == 0
- assert Repo.get(Activity, like_activity.id) == nil
+ assert Activity.get_by_id(like_activity.id) == nil
end
end
@@ -524,7 +673,7 @@ test "unannouncing a previously announced object" do
assert unannounce_activity.data["actor"] == user.ap_id
assert unannounce_activity.data["context"] == announce_activity.data["context"]
- assert Repo.get(Activity, announce_activity.id) == nil
+ assert Activity.get_by_id(announce_activity.id) == nil
end
end
@@ -559,43 +708,6 @@ test "fetches the latest Follow activity" do
end
end
- describe "fetching an object" do
- test "it fetches an object" do
- {:ok, object} =
- ActivityPub.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} =
- ActivityPub.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 "it works with objects only available via Ostatus" do
- {:ok, object} = ActivityPub.fetch_object_from_id("https://shitposter.club/notice/2827873")
- assert activity = Activity.get_create_by_object_ap_id(object.data["id"])
- assert activity.data["id"]
-
- {:ok, object_again} =
- ActivityPub.fetch_object_from_id("https://shitposter.club/notice/2827873")
-
- assert object == object_again
- end
-
- test "it correctly stitches up conversations between ostatus and ap" do
- last = "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
- {:ok, object} = ActivityPub.fetch_object_from_id(last)
-
- object = Object.get_by_ap_id(object.data["inReplyTo"])
- assert object
- end
- end
-
describe "following / unfollowing" do
test "creates a follow activity" do
follower = insert(:user)
@@ -663,7 +775,7 @@ test "it creates a delete activity and deletes the original object" do
assert delete.data["actor"] == note.data["actor"]
assert delete.data["object"] == note.data["object"]["id"]
- assert Repo.get(Activity, delete.id) != nil
+ assert Activity.get_by_id(delete.id) != nil
assert Repo.get(Object, object.id).data["type"] == "Tombstone"
end
@@ -672,23 +784,35 @@ test "decrements user note count only for public activities" do
user = insert(:user, info: %{note_count: 10})
{:ok, a1} =
- CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "public"})
+ CommonAPI.post(User.get_cached_by_id(user.id), %{
+ "status" => "yeah",
+ "visibility" => "public"
+ })
{:ok, a2} =
- CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "unlisted"})
+ CommonAPI.post(User.get_cached_by_id(user.id), %{
+ "status" => "yeah",
+ "visibility" => "unlisted"
+ })
{:ok, a3} =
- CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "private"})
+ CommonAPI.post(User.get_cached_by_id(user.id), %{
+ "status" => "yeah",
+ "visibility" => "private"
+ })
{:ok, a4} =
- CommonAPI.post(Repo.get(User, user.id), %{"status" => "yeah", "visibility" => "direct"})
+ CommonAPI.post(User.get_cached_by_id(user.id), %{
+ "status" => "yeah",
+ "visibility" => "direct"
+ })
- {:ok, _} = a1.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
- {:ok, _} = a2.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
- {:ok, _} = a3.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
- {:ok, _} = a4.data["object"]["id"] |> Object.get_by_ap_id() |> ActivityPub.delete()
+ {:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
+ {:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
+ {:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
+ {:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert user.info.note_count == 10
end
@@ -712,6 +836,36 @@ test "it creates a delete activity and checks that it is also sent to users ment
assert user.ap_id in delete.data["to"]
end
+
+ test "decreases reply count" do
+ user = insert(:user)
+ user2 = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
+ reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
+ ap_id = activity.data["id"]
+
+ {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
+ {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
+ {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
+ {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
+
+ _ = CommonAPI.delete(direct_reply.id, user2)
+ assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
+ assert object.data["repliesCount"] == 2
+
+ _ = CommonAPI.delete(private_reply.id, user2)
+ assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
+ assert object.data["repliesCount"] == 2
+
+ _ = CommonAPI.delete(public_reply.id, user2)
+ assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
+ assert object.data["repliesCount"] == 1
+
+ _ = CommonAPI.delete(unlisted_reply.id, user2)
+ assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
+ assert object.data["repliesCount"] == 0
+ end
end
describe "timeline post-processing" do
@@ -750,7 +904,11 @@ test "it filters broken threads" do
activities = ActivityPub.fetch_activities([user1.ap_id | user1.following])
- assert [public_activity, private_activity_1, private_activity_3] == activities
+ private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
+
+ assert [public_activity, private_activity_1, private_activity_3] ==
+ activities
+
assert length(activities) == 3
activities = ActivityPub.contain_timeline(activities, user1)
@@ -760,15 +918,6 @@ test "it filters broken threads" do
end
end
- test "it can fetch plume articles" do
- {:ok, object} =
- ActivityPub.fetch_object_from_id(
- "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/"
- )
-
- assert object
- end
-
describe "update" do
test "it creates an update activity with the new user data" do
user = insert(:user)
@@ -790,15 +939,6 @@ test "it creates an update activity with the new user data" do
end
end
- test "it can fetch peertube videos" do
- {:ok, object} =
- ActivityPub.fetch_object_from_id(
- "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
- )
-
- assert object
- end
-
test "returned pinned statuses" do
Pleroma.Config.put([:instance, :max_pinned_statuses], 3)
user = insert(:user)
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 8184dbbae..78429c7c6 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -5,6 +5,8 @@
defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
use Pleroma.DataCase
alias Pleroma.Activity
+ alias Pleroma.Object
+ alias Pleroma.Object.Fetcher
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -49,16 +51,14 @@ test "it fetches replied-to activities if we don't have them" do
|> Map.put("object", object)
{:ok, returned_activity} = Transmogrifier.handle_incoming(data)
+ returned_object = Object.normalize(returned_activity.data["object"])
assert activity =
Activity.get_create_by_object_ap_id(
"tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
)
- assert returned_activity.data["object"]["inReplyToAtomUri"] ==
- "https://shitposter.club/notice/2827873"
-
- assert returned_activity.data["object"]["inReplyToStatusId"] == activity.id
+ assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
end
test "it works for incoming notices" do
@@ -81,7 +81,7 @@ test "it works for incoming notices" do
assert data["actor"] == "http://mastodon.example.org/users/admin"
- object = data["object"]
+ object = Object.normalize(data["object"]).data
assert object["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822"
assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
@@ -99,7 +99,7 @@ test "it works for incoming notices" do
assert object["sensitive"] == true
- user = User.get_by_ap_id(object["actor"])
+ user = User.get_cached_by_ap_id(object["actor"])
assert user.info.note_count == 1
end
@@ -108,7 +108,9 @@ 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)
- assert Enum.at(data["object"]["tag"], 2) == "moo"
+ object = Object.normalize(data["object"])
+
+ assert Enum.at(object.data["tag"], 2) == "moo"
end
test "it works for incoming notices with contentMap" do
@@ -116,8 +118,9 @@ test "it works for incoming notices with contentMap" do
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 data["object"]["content"] ==
+ assert object.data["content"] ==
"
@lain
"
end
@@ -125,8 +128,9 @@ 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 data["object"]["content"] ==
+ assert object.data["content"] ==
"
henlo from my Psion netBook
message sent from my Psion netBook
"
end
@@ -142,24 +146,27 @@ 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 data["object"]["emoji"] == %{
+ 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 data["object"]["tag"]
+ 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 data["object"]["url"] == "https://prismo.news/posts/83"
+ assert object.data["url"] == "https://prismo.news/posts/83"
end
test "it cleans up incoming notices which are not really DMs" do
@@ -181,15 +188,15 @@ test "it cleans up incoming notices which are not really DMs" do
data = Map.put(data, "object", object)
- {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+ {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
assert data["to"] == []
assert data["cc"] == to
- object = data["object"]
+ object_data = Object.normalize(activity).data
- assert object["to"] == []
- assert object["cc"] == to
+ assert object_data["to"] == []
+ assert object_data["cc"] == to
end
test "it works for incoming follow requests" do
@@ -205,7 +212,27 @@ test "it works for incoming follow requests" do
assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Follow"
assert data["id"] == "http://mastodon.example.org/users/admin#follows/2"
- assert User.following?(User.get_by_ap_id(data["actor"]), user)
+ assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
+ 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)
+ target = User.get_or_fetch("http://mastodon.example.org/users/admin")
+
+ {:ok, user} = 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 works for incoming follow requests from hubzilla" do
@@ -222,7 +249,7 @@ test "it works for incoming follow requests from hubzilla" do
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_by_ap_id(data["actor"]), user)
+ assert User.following?(User.get_cached_by_ap_id(data["actor"]), user)
end
test "it works for incoming likes" do
@@ -232,14 +259,14 @@ test "it works for incoming likes" do
data =
File.read!("test/fixtures/mastodon-like.json")
|> Poison.decode!()
- |> Map.put("object", activity.data["object"]["id"])
+ |> Map.put("object", activity.data["object"])
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
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"]["id"]
+ assert data["object"] == activity.data["object"]
end
test "it returns an error for incoming unlikes wihout a like activity" do
@@ -249,7 +276,7 @@ test "it returns an error for incoming unlikes wihout a like activity" do
data =
File.read!("test/fixtures/mastodon-undo-like.json")
|> Poison.decode!()
- |> Map.put("object", activity.data["object"]["id"])
+ |> Map.put("object", activity.data["object"])
assert Transmogrifier.handle_incoming(data) == :error
end
@@ -261,7 +288,7 @@ test "it works for incoming unlikes with an existing like activity" do
like_data =
File.read!("test/fixtures/mastodon-like.json")
|> Poison.decode!()
- |> Map.put("object", activity.data["object"]["id"])
+ |> Map.put("object", activity.data["object"])
{:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
@@ -303,7 +330,7 @@ test "it works for incoming announces with an existing activity" do
data =
File.read!("test/fixtures/mastodon-announce.json")
|> Poison.decode!()
- |> Map.put("object", activity.data["object"]["id"])
+ |> Map.put("object", activity.data["object"])
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@@ -313,7 +340,7 @@ test "it works for incoming announces with an existing activity" do
assert data["id"] ==
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
- assert data["object"] == activity.data["object"]["id"]
+ assert data["object"] == activity.data["object"]
assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
end
@@ -325,7 +352,7 @@ test "it does not clobber the addressing on announce activities" do
data =
File.read!("test/fixtures/mastodon-announce.json")
|> Poison.decode!()
- |> Map.put("object", activity.data["object"]["id"])
+ |> Map.put("object", Object.normalize(activity).data["id"])
|> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
|> Map.put("cc", [])
@@ -334,6 +361,53 @@ test "it does not clobber the addressing on announce activities" do
assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
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", [])
+
+ 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)
+
+ 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 works for incoming update activities" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
@@ -404,7 +478,7 @@ test "it works for incoming deletes" do
object =
data["object"]
- |> Map.put("id", activity.data["object"]["id"])
+ |> Map.put("id", activity.data["object"])
data =
data
@@ -413,7 +487,7 @@ test "it works for incoming deletes" do
{:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
- refute Repo.get(Activity, activity.id)
+ refute Activity.get_by_id(activity.id)
end
test "it fails for incoming deletes with spoofed origin" do
@@ -425,7 +499,7 @@ test "it fails for incoming deletes with spoofed origin" do
object =
data["object"]
- |> Map.put("id", activity.data["object"]["id"])
+ |> Map.put("id", activity.data["object"])
data =
data
@@ -433,7 +507,7 @@ test "it fails for incoming deletes with spoofed origin" do
:error = Transmogrifier.handle_incoming(data)
- assert Repo.get(Activity, activity.id)
+ assert Activity.get_by_id(activity.id)
end
test "it works for incoming unannounces with an existing notice" do
@@ -443,7 +517,7 @@ test "it works for incoming unannounces with an existing notice" do
announce_data =
File.read!("test/fixtures/mastodon-announce.json")
|> Poison.decode!()
- |> Map.put("object", activity.data["object"]["id"])
+ |> Map.put("object", activity.data["object"])
{:ok, %Activity{data: announce_data, local: false}} =
Transmogrifier.handle_incoming(announce_data)
@@ -458,7 +532,7 @@ test "it works for incoming unannounces with an existing notice" do
assert data["type"] == "Undo"
assert data["object"]["type"] == "Announce"
- assert data["object"]["object"] == activity.data["object"]["id"]
+ assert data["object"]["object"] == activity.data["object"]
assert data["object"]["id"] ==
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
@@ -486,7 +560,7 @@ test "it works for incomming unfollows with an existing follow" do
assert data["object"]["object"] == user.ap_id
assert data["actor"] == "http://mastodon.example.org/users/admin"
- refute User.following?(User.get_by_ap_id(data["actor"]), user)
+ refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
end
test "it works for incoming blocks" do
@@ -503,7 +577,7 @@ test "it works for incoming blocks" do
assert data["object"] == user.ap_id
assert data["actor"] == "http://mastodon.example.org/users/admin"
- blocker = User.get_by_ap_id(data["actor"])
+ blocker = User.get_cached_by_ap_id(data["actor"])
assert User.blocks?(blocker, user)
end
@@ -530,8 +604,8 @@ test "incoming blocks successfully tear down any follow relationship" do
assert data["object"] == blocked.ap_id
assert data["actor"] == blocker.ap_id
- blocker = User.get_by_ap_id(data["actor"])
- blocked = User.get_by_ap_id(data["object"])
+ blocker = User.get_cached_by_ap_id(data["actor"])
+ blocked = User.get_cached_by_ap_id(data["object"])
assert User.blocks?(blocker, blocked)
@@ -560,7 +634,7 @@ test "it works for incoming unblocks with an existing block" do
assert data["object"]["object"] == user.ap_id
assert data["actor"] == "http://mastodon.example.org/users/admin"
- blocker = User.get_by_ap_id(data["actor"])
+ blocker = User.get_cached_by_ap_id(data["actor"])
refute User.blocks?(blocker, user)
end
@@ -591,7 +665,7 @@ test "it works for incoming accepts which were pre-accepted" do
assert activity.data["object"] == follow_activity.data["id"]
- follower = Repo.get(User, follower.id)
+ follower = User.get_cached_by_id(follower.id)
assert User.following?(follower, followed) == true
end
@@ -613,7 +687,7 @@ test "it works for incoming accepts which were orphaned" do
{:ok, activity} = Transmogrifier.handle_incoming(accept_data)
assert activity.data["object"] == follow_activity.data["id"]
- follower = Repo.get(User, follower.id)
+ follower = User.get_cached_by_id(follower.id)
assert User.following?(follower, followed) == true
end
@@ -633,7 +707,7 @@ test "it works for incoming accepts which are referenced by IRI only" do
{:ok, activity} = Transmogrifier.handle_incoming(accept_data)
assert activity.data["object"] == follow_activity.data["id"]
- follower = Repo.get(User, follower.id)
+ follower = User.get_cached_by_id(follower.id)
assert User.following?(follower, followed) == true
end
@@ -652,7 +726,7 @@ test "it fails for incoming accepts which cannot be correlated" do
:error = Transmogrifier.handle_incoming(accept_data)
- follower = Repo.get(User, follower.id)
+ follower = User.get_cached_by_id(follower.id)
refute User.following?(follower, followed) == true
end
@@ -671,7 +745,7 @@ test "it fails for incoming rejects which cannot be correlated" do
:error = Transmogrifier.handle_incoming(accept_data)
- follower = Repo.get(User, follower.id)
+ follower = User.get_cached_by_id(follower.id)
refute User.following?(follower, followed) == true
end
@@ -696,7 +770,7 @@ test "it works for incoming rejects which are orphaned" do
{:ok, activity} = Transmogrifier.handle_incoming(reject_data)
refute activity.local
- follower = Repo.get(User, follower.id)
+ follower = User.get_cached_by_id(follower.id)
assert User.following?(follower, followed) == false
end
@@ -718,7 +792,7 @@ test "it works for incoming rejects which are referenced by IRI only" do
{:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
- follower = Repo.get(User, follower.id)
+ follower = User.get_cached_by_id(follower.id)
assert User.following?(follower, followed) == false
end
@@ -737,7 +811,7 @@ test "it rejects activities without a valid ID" do
test "it remaps video URLs as attachments if necessary" do
{:ok, object} =
- ActivityPub.fetch_object_from_id(
+ Fetcher.fetch_object_from_id(
"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
)
@@ -764,6 +838,30 @@ test "it remaps video URLs as attachments if necessary" do
assert object.data["attachment"] == [attachment]
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.data["object"])
+
+ message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "cc" => [user.ap_id],
+ "object" => [user.ap_id, object.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, object.data["id"]]
+ assert activity.data["content"] == "blocked AND reported!!!"
+ assert activity.data["actor"] == other_user.ap_id
+ assert activity.data["cc"] == [user.ap_id]
+ end
end
describe "prepare outgoing" do
@@ -868,7 +966,7 @@ test "it strips internal hashtag data" do
test "it strips internal fields" do
user = insert(:user)
- {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :moominmamma:"})
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :firefox:"})
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
@@ -948,7 +1046,7 @@ test "it upgrades a user to activitypub" do
{:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert user.info.note_count == 1
{:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
@@ -956,13 +1054,10 @@ test "it upgrades a user to activitypub" do
assert user.info.note_count == 1
assert user.follower_address == "https://niu.moe/users/rye/followers"
- # Wait for the background task
- :timer.sleep(1000)
-
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert user.info.note_count == 1
- activity = Repo.get(Activity, activity.id)
+ activity = Activity.get_by_id(activity.id)
assert user.follower_address in activity.recipients
assert %{
@@ -985,10 +1080,10 @@ test "it upgrades a user to activitypub" do
refute "..." in activity.recipients
- unrelated_activity = Repo.get(Activity, unrelated_activity.id)
+ unrelated_activity = Activity.get_by_id(unrelated_activity.id)
refute user.follower_address in unrelated_activity.recipients
- user_two = Repo.get(User, user_two.id)
+ user_two = User.get_cached_by_id(user_two.id)
assert user.follower_address in user_two.following
refute "..." in user_two.following
end
@@ -1021,10 +1116,6 @@ test "it fixes the actor URL property to be a proper URI" do
end
describe "actor origin containment" do
- test "it rejects objects with a bogus origin" do
- {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity.json")
- end
-
test "it rejects activities which reference objects with bogus origins" do
data = %{
"@context" => "https://www.w3.org/ns/activitystreams",
@@ -1038,10 +1129,6 @@ test "it rejects activities which reference objects with bogus origins" do
:error = Transmogrifier.handle_incoming(data)
end
- test "it rejects objects when attributedTo is wrong (variant 1)" do
- {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity2.json")
- end
-
test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
data = %{
"@context" => "https://www.w3.org/ns/activitystreams",
@@ -1055,10 +1142,6 @@ test "it rejects activities which reference objects that have an incorrect attri
:error = Transmogrifier.handle_incoming(data)
end
- test "it rejects objects when attributedTo is wrong (variant 2)" do
- {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity3.json")
- end
-
test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
data = %{
"@context" => "https://www.w3.org/ns/activitystreams",
@@ -1073,62 +1156,6 @@ test "it rejects activities which reference objects that have an incorrect attri
end
end
- describe "general origin containment" do
- test "contain_origin_from_id() catches obvious spoofing attempts" do
- data = %{
- "id" => "http://example.com/~alyssa/activities/1234.json"
- }
-
- :error =
- Transmogrifier.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 =
- Transmogrifier.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 =
- Transmogrifier.contain_origin_from_id(
- "http://example.com/~alyssa/activities/1234.json",
- data
- )
- end
-
- test "users cannot be collided through fake direction spoofing attempts" do
- 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"})
- })
-
- {:error, _} = User.get_or_fetch_by_ap_id("https://n1u.moe/users/rye")
- end
-
- test "all objects with fake directions are rejected by the object fetcher" do
- {:error, _} =
- ActivityPub.fetch_and_contain_remote_object_from_id(
- "https://info.pleroma.site/activity4.json"
- )
- end
- end
-
describe "reserialization" do
test "successfully reserializes a message with inReplyTo == nil" do
user = insert(:user)
diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs
index 2bd3ddf93..c57fae437 100644
--- a/test/web/activity_pub/utils_test.exs
+++ b/test/web/activity_pub/utils_test.exs
@@ -1,10 +1,33 @@
defmodule Pleroma.Web.ActivityPub.UtilsTest do
use Pleroma.DataCase
+ alias Pleroma.Activity
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
+ 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 "fetch the latest Block" do
+ test "fetches the latest Block activity" do
+ blocker = insert(:user)
+ blocked = insert(:user)
+ {:ok, activity} = ActivityPub.block(blocker, blocked)
+
+ assert activity == Utils.fetch_latest_block(blocker, blocked)
+ end
+ end
+
describe "determine_explicit_mentions()" do
test "works with an object that has mentions" do
object = %{
@@ -169,4 +192,16 @@ test "returns the what was collected if there are less pages than specified" do
assert Utils.fetch_ordered_collection("http://example.com/outbox", 5) == [0, 1]
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
end
diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs
index 0bc1d4728..9fb9455d2 100644
--- a/test/web/activity_pub/views/user_view_test.exs
+++ b/test/web/activity_pub/views/user_view_test.exs
@@ -16,6 +16,29 @@ test "Renders a user, including the public key" do
assert String.contains?(result["publicKey"]["publicKeyPem"], "BEGIN PUBLIC KEY")
end
+ test "Does not add an avatar image if the user hasn't set one" do
+ user = insert(:user)
+ {:ok, user} = Pleroma.Web.WebFinger.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"}]},
+ info: %{
+ banner: %{"url" => [%{"href" => "https://somebanner"}]}
+ }
+ )
+
+ {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user)
+
+ result = UserView.render("user.json", %{user: user})
+ assert result["icon"]["url"] == "https://someurl"
+ assert result["image"]["url"] == "https://somebanner"
+ end
+
describe "endpoints" do
test "local users have a usable endpoints structure" do
user = insert(:user)
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index e50f0edde..b89c42327 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -5,8 +5,8 @@
defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
use Pleroma.Web.ConnCase
- alias Pleroma.Repo
alias Pleroma.User
+ alias Pleroma.UserInviteToken
import Pleroma.Factory
describe "/api/pleroma/admin/user" do
@@ -40,6 +40,85 @@ test "Create" do
end
end
+ describe "/api/pleroma/admin/users/:nickname" do
+ test "Show", %{conn: conn} do
+ admin = insert(:user, info: %{is_admin: true})
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, admin)
+ |> get("/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" => []
+ }
+
+ assert expected == json_response(conn, 200)
+ end
+
+ test "when the user doesn't exist", %{conn: conn} do
+ admin = insert(:user, info: %{is_admin: true})
+ user = build(:user)
+
+ conn =
+ conn
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/users/#{user.nickname}")
+
+ assert "Not found" == json_response(conn, 404)
+ end
+ end
+
+ describe "/api/pleroma/admin/user/follow" do
+ test "allows to force-follow another user" do
+ admin = insert(:user, info: %{is_admin: true})
+ user = insert(:user)
+ follower = insert(:user)
+
+ build_conn()
+ |> assign(:user, admin)
+ |> put_req_header("accept", "application/json")
+ |> post("/api/pleroma/admin/user/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)
+ end
+ end
+
+ describe "/api/pleroma/admin/user/unfollow" do
+ test "allows to force-unfollow another user" do
+ admin = insert(:user, info: %{is_admin: true})
+ user = insert(:user)
+ follower = insert(:user)
+
+ User.follow(follower, user)
+
+ build_conn()
+ |> assign(:user, admin)
+ |> put_req_header("accept", "application/json")
+ |> post("/api/pleroma/admin/user/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)
+ end
+ end
+
describe "PUT /api/pleroma/admin/users/tag" do
setup do
admin = insert(:user, info: %{is_admin: true})
@@ -66,13 +145,13 @@ test "it appends specified tags to users with specified nicknames", %{
user2: user2
} do
assert json_response(conn, :no_content)
- assert Repo.get(User, user1.id).tags == ["x", "foo", "bar"]
- assert Repo.get(User, user2.id).tags == ["y", "foo", "bar"]
+ assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
+ assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
end
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
assert json_response(conn, :no_content)
- assert Repo.get(User, user3.id).tags == ["unchanged"]
+ assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
end
end
@@ -102,13 +181,13 @@ test "it removes specified tags from users with specified nicknames", %{
user2: user2
} do
assert json_response(conn, :no_content)
- assert Repo.get(User, user1.id).tags == []
- assert Repo.get(User, user2.id).tags == ["y"]
+ assert User.get_cached_by_id(user1.id).tags == []
+ assert User.get_cached_by_id(user2.id).tags == ["y"]
end
test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
assert json_response(conn, :no_content)
- assert Repo.get(User, user3.id).tags == ["unchanged"]
+ assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
end
end
@@ -178,7 +257,7 @@ test "deactivates the user", %{conn: conn} do
conn
|> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: false})
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert user.info.deactivated == true
assert json_response(conn, :no_content)
end
@@ -190,7 +269,7 @@ test "activates the user", %{conn: conn} do
conn
|> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: true})
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
assert user.info.deactivated == false
assert json_response(conn, :no_content)
end
@@ -238,13 +317,21 @@ test "sends invitation and returns 204", %{conn: conn, user: user} do
assert token_record
refute token_record.used
- Swoosh.TestAssertions.assert_email_sent(
- Pleroma.UserEmail.user_invitation_email(
+ notify_email = Pleroma.Config.get([:instance, :notify_email])
+ instance_name = Pleroma.Config.get([:instance, :name])
+
+ email =
+ Pleroma.Emails.UserEmail.user_invitation_email(
user,
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
@@ -408,13 +495,13 @@ test "regular search" do
test "regular search with page size" do
admin = insert(:user, info: %{is_admin: true})
- user = insert(:user, nickname: "bob")
- user2 = insert(:user, nickname: "bo")
+ user = insert(:user, nickname: "aalice")
+ user2 = insert(:user, nickname: "alice")
conn =
build_conn()
|> assign(:user, admin)
- |> get("/api/pleroma/admin/users?query=bo&page_size=1&page=1")
+ |> get("/api/pleroma/admin/users?query=a&page_size=1&page=1")
assert json_response(conn, 200) == %{
"count" => 2,
@@ -434,7 +521,7 @@ test "regular search with page size" do
conn =
build_conn()
|> assign(:user, admin)
- |> get("/api/pleroma/admin/users?query=bo&page_size=1&page=2")
+ |> get("/api/pleroma/admin/users?query=a&page_size=1&page=2")
assert json_response(conn, 200) == %{
"count" => 2,
@@ -461,7 +548,7 @@ test "only local users" do
conn =
build_conn()
|> assign(:user, admin)
- |> get("/api/pleroma/admin/users?query=bo&local_only=true")
+ |> get("/api/pleroma/admin/users?query=bo&filters=local")
assert json_response(conn, 200) == %{
"count" => 1,
@@ -488,12 +575,20 @@ test "only local users with no query" do
conn =
build_conn()
|> assign(:user, admin)
- |> get("/api/pleroma/admin/users?local_only=true")
+ |> get("/api/pleroma/admin/users?filters=local")
assert json_response(conn, 200) == %{
"count" => 2,
"page_size" => 50,
"users" => [
+ %{
+ "deactivated" => user.info.deactivated,
+ "id" => user.id,
+ "nickname" => user.nickname,
+ "roles" => %{"admin" => false, "moderator" => false},
+ "local" => true,
+ "tags" => []
+ },
%{
"deactivated" => admin.info.deactivated,
"id" => admin.id,
@@ -501,13 +596,33 @@ test "only local users with no query" do
"roles" => %{"admin" => true, "moderator" => false},
"local" => true,
"tags" => []
- },
+ }
+ ]
+ }
+ end
+
+ test "it works with multiple filters" do
+ admin = insert(:user, nickname: "john", info: %{is_admin: true})
+ user = insert(:user, nickname: "bob", local: false, info: %{deactivated: true})
+
+ insert(:user, nickname: "ken", local: true, info: %{deactivated: true})
+ insert(:user, nickname: "bobb", local: false, info: %{deactivated: false})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/users?filters=deactivated,external")
+
+ assert json_response(conn, 200) == %{
+ "count" => 1,
+ "page_size" => 50,
+ "users" => [
%{
"deactivated" => user.info.deactivated,
"id" => user.id,
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
- "local" => true,
+ "local" => user.local,
"tags" => []
}
]
@@ -534,4 +649,136 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
"tags" => []
}
end
+
+ describe "GET /api/pleroma/admin/invite_token" do
+ test "without options" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invite_token")
+
+ token = json_response(conn, 200)
+ invite = UserInviteToken.find_by_token!(token)
+ refute invite.used
+ refute invite.expires_at
+ refute invite.max_use
+ assert invite.invite_type == "one_time"
+ end
+
+ test "with expires_at" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invite_token", %{
+ "invite" => %{"expires_at" => Date.to_string(Date.utc_today())}
+ })
+
+ token = json_response(conn, 200)
+ invite = UserInviteToken.find_by_token!(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" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invite_token", %{
+ "invite" => %{"max_use" => 150}
+ })
+
+ token = json_response(conn, 200)
+ invite = UserInviteToken.find_by_token!(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" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invite_token", %{
+ "invite" => %{"max_use" => 150, "expires_at" => Date.to_string(Date.utc_today())}
+ })
+
+ token = json_response(conn, 200)
+ invite = UserInviteToken.find_by_token!(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/invites" do
+ test "no invites" do
+ admin = insert(:user, info: %{is_admin: true})
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invites")
+
+ assert json_response(conn, 200) == %{"invites" => []}
+ end
+
+ test "with invite" do
+ admin = insert(:user, info: %{is_admin: true})
+ {:ok, invite} = UserInviteToken.create_invite()
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/invites")
+
+ assert json_response(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/revoke_invite" do
+ test "with token" do
+ admin = insert(:user, info: %{is_admin: true})
+ {:ok, invite} = UserInviteToken.create_invite()
+
+ conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> post("/api/pleroma/admin/revoke_invite", %{"token" => invite.token})
+
+ assert json_response(conn, 200) == %{
+ "expires_at" => nil,
+ "id" => invite.id,
+ "invite_type" => "one_time",
+ "max_use" => nil,
+ "token" => invite.token,
+ "used" => true,
+ "uses" => 0
+ }
+ end
+ end
end
diff --git a/test/web/admin_api/search_test.exs b/test/web/admin_api/search_test.exs
new file mode 100644
index 000000000..3950996ed
--- /dev/null
+++ b/test/web/admin_api/search_test.exs
@@ -0,0 +1,88 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 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, info: %{deactivated: true})
+ insert(:user, info: %{deactivated: true})
+ insert(:user, info: %{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)
+ insert(:user, nickname: "bob", local: true, info: %{deactivated: false})
+
+ {:ok, _results, total_count} = Search.user(%{query: ""})
+
+ {:ok, _results, count} =
+ Search.user(%{
+ query: "Bo",
+ active: true,
+ local: true
+ })
+
+ assert total_count == 3
+ assert count == 1
+ end
+ end
+end
diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs
index 181813c76..a5b07c446 100644
--- a/test/web/common_api/common_api_test.exs
+++ b/test/web/common_api/common_api_test.exs
@@ -5,33 +5,54 @@
defmodule Pleroma.Web.CommonAPITest do
use Pleroma.DataCase
alias Pleroma.Activity
+ alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
+ 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)
+ option = Pleroma.Config.get([:instance, :safe_dm_mentions])
+ 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
+ Pleroma.Config.put([:instance, :safe_dm_mentions], option)
+ end
+
test "it de-duplicates tags" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu #2HU"})
- assert activity.data["object"]["tag"] == ["2hu"]
+ object = Object.normalize(activity.data["object"])
+
+ assert object.data["tag"] == ["2hu"]
end
test "it adds emoji in the object" do
user = insert(:user)
- {:ok, activity} = CommonAPI.post(user, %{"status" => ":moominmamma:"})
+ {:ok, activity} = CommonAPI.post(user, %{"status" => ":firefox:"})
- assert activity.data["object"]["emoji"]["moominmamma"]
+ assert Object.normalize(activity).data["emoji"]["firefox"]
end
test "it adds emoji when updating profiles" do
- user = insert(:user, %{name: ":karjalanpiirakka:"})
+ user = insert(:user, %{name: ":firefox:"})
CommonAPI.update(user)
user = User.get_cached_by_ap_id(user.ap_id)
- [karjalanpiirakka] = user.info.source_data["tag"]
+ [firefox] = user.info.source_data["tag"]
- assert karjalanpiirakka["name"] == ":karjalanpiirakka:"
+ assert firefox["name"] == ":firefox:"
end
describe "posting" do
@@ -46,8 +67,9 @@ test "it filters out obviously bad tags when accepting a post as HTML" do
"content_type" => "text/html"
})
- content = activity.data["object"]["content"]
- assert content == "
2hu
alert('xss')"
+ object = Object.normalize(activity.data["object"])
+
+ assert object.data["content"] == "
2hu
alert('xss')"
end
test "it filters out obviously bad tags when accepting a post as Markdown" do
@@ -61,8 +83,9 @@ test "it filters out obviously bad tags when accepting a post as Markdown" do
"content_type" => "text/markdown"
})
- content = activity.data["object"]["content"]
- assert content == "
2hu
alert('xss')"
+ object = Object.normalize(activity.data["object"])
+
+ assert object.data["content"] == "
2hu
alert('xss')"
end
end
@@ -221,4 +244,27 @@ test "creates a report" do
} = flag_activity
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, muter} = CommonAPI.hide_reblogs(muter, muted)
+
+ assert Pleroma.User.showing_reblogs?(muter, muted) == false
+ end
+
+ test "remove a reblog mute", %{muter: muter, muted: muted} do
+ {:ok, muter} = CommonAPI.hide_reblogs(muter, muted)
+ {:ok, muter} = CommonAPI.show_reblogs(muter, muted)
+
+ assert Pleroma.User.showing_reblogs?(muter, muted) == true
+ 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
index 4c97b0d62..ab4c62b35 100644
--- a/test/web/common_api/common_api_utils_test.exs
+++ b/test/web/common_api/common_api_utils_test.exs
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.CommonAPI.UtilsTest do
alias Pleroma.Builders.UserBuilder
+ alias Pleroma.Object
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.Endpoint
use Pleroma.DataCase
@@ -36,21 +37,21 @@ test "correct password given" do
end
test "parses emoji from name and bio" do
- {:ok, user} = UserBuilder.insert(%{name: ":karjalanpiirakka:", bio: ":perkele:"})
+ {:ok, user} = UserBuilder.insert(%{name: ":blank:", bio: ":firefox:"})
expected = [
%{
"type" => "Emoji",
- "icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}/finmoji/128px/perkele-128.png"},
- "name" => ":perkele:"
+ "icon" => %{"type" => "Image", "url" => "#{Endpoint.url()}/emoji/Firefox.gif"},
+ "name" => ":firefox:"
},
%{
"type" => "Emoji",
"icon" => %{
"type" => "Image",
- "url" => "#{Endpoint.url()}/finmoji/128px/karjalanpiirakka-128.png"
+ "url" => "#{Endpoint.url()}/emoji/blank.png"
},
- "name" => ":karjalanpiirakka:"
+ "name" => ":blank:"
}
]
@@ -118,6 +119,31 @@ test "works for bare text/markdown" do
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! \n
\nsecond paragraph!"
+
+ {output, [], []} = Utils.format_input(text, "text/bbcode")
+
+ assert output == expected
+
+ text = "[b]hello world![/b]\n\n
second paragraph! "
+
+ expected =
+ "
hello world! \n
\n<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"})
@@ -136,4 +162,56 @@ test "works for text/markdown with mentions" do
assert output == expected
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 Utils.date_to_asctime(date) == expected
+ end
+
+ test "when date is a Unix timestamp" do
+ date = DateTime.utc_now() |> DateTime.to_unix()
+
+ expected = ""
+
+ assert Utils.date_to_asctime(date) == expected
+ end
+
+ test "when date is nil" do
+ expected = ""
+
+ assert Utils.date_to_asctime(nil) == expected
+ end
+ end
end
diff --git a/test/web/mastodon_api/account_view_test.exs b/test/web/mastodon_api/account_view_test.exs
index c2ffc21da..a24f2a050 100644
--- a/test/web/mastodon_api/account_view_test.exs
+++ b/test/web/mastodon_api/account_view_test.exs
@@ -56,14 +56,17 @@ test "Represent a user account" do
bot: false,
source: %{
note: "",
- privacy: "public",
- sensitive: false
+ sensitive: false,
+ pleroma: %{}
},
pleroma: %{
confirmation_pending: false,
tags: [],
is_admin: false,
is_moderator: false,
+ hide_favorites: true,
+ hide_followers: false,
+ hide_follows: false,
relationship: %{}
}
}
@@ -71,6 +74,24 @@ test "Represent a user account" do
assert expected == AccountView.render("account.json", %{user: user})
end
+ test "Represent the user account for the account owner" do
+ user = insert(:user)
+
+ notification_settings = %{
+ "remote" => true,
+ "local" => true,
+ "followers" => true,
+ "follows" => true
+ }
+
+ privacy = user.info.default_scope
+
+ assert %{
+ pleroma: %{notification_settings: ^notification_settings},
+ source: %{privacy: ^privacy}
+ } = AccountView.render("account.json", %{user: user, for: user})
+ end
+
test "Represent a Service(bot) account" do
user =
insert(:user, %{
@@ -100,14 +121,17 @@ test "Represent a Service(bot) account" do
bot: true,
source: %{
note: "",
- privacy: "public",
- sensitive: false
+ sensitive: false,
+ pleroma: %{}
},
pleroma: %{
confirmation_pending: false,
tags: [],
is_admin: false,
is_moderator: false,
+ hide_favorites: true,
+ hide_followers: false,
+ hide_follows: false,
relationship: %{}
}
}
@@ -142,9 +166,10 @@ test "represent a relationship" do
blocking: true,
muting: false,
muting_notifications: false,
+ subscribing: false,
requested: false,
domain_blocking: false,
- showing_reblogs: false,
+ showing_reblogs: true,
endorsed: false
}
@@ -154,15 +179,15 @@ test "represent a relationship" do
test "represent an embedded relationship" do
user =
insert(:user, %{
- info: %{note_count: 5, follower_count: 3, source_data: %{"type" => "Service"}},
+ info: %{note_count: 5, follower_count: 0, source_data: %{"type" => "Service"}},
nickname: "shp@shitposter.club",
inserted_at: ~N[2017-08-15 15:47:06.597036]
})
other_user = insert(:user)
-
{:ok, other_user} = User.follow(other_user, user)
{:ok, other_user} = User.block(other_user, user)
+ {:ok, _} = User.follow(insert(:user), user)
expected = %{
id: to_string(user.id),
@@ -171,7 +196,7 @@ test "represent an embedded relationship" do
display_name: user.name,
locked: false,
created_at: "2017-08-15T15:47:06.000Z",
- followers_count: 3,
+ followers_count: 1,
following_count: 0,
statuses_count: 5,
note: user.bio,
@@ -185,24 +210,28 @@ test "represent an embedded relationship" do
bot: true,
source: %{
note: "",
- privacy: "public",
- sensitive: false
+ sensitive: false,
+ pleroma: %{}
},
pleroma: %{
confirmation_pending: false,
tags: [],
is_admin: false,
is_moderator: false,
+ hide_favorites: true,
+ hide_followers: false,
+ hide_follows: false,
relationship: %{
id: to_string(user.id),
following: false,
followed_by: false,
blocking: true,
+ subscribing: false,
muting: false,
muting_notifications: false,
requested: false,
domain_blocking: false,
- showing_reblogs: false,
+ showing_reblogs: true,
endorsed: false
}
}
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index 059d5237d..c2a12d3c7 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -10,11 +10,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
+ alias Pleroma.ScheduledActivity
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.FilterView
+ alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OStatus
+ alias Pleroma.Web.Push
alias Pleroma.Web.TwitterAPI.TwitterAPI
import Pleroma.Factory
import ExUnit.CaptureLog
@@ -99,7 +102,7 @@ test "posting a status", %{conn: conn} do
assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
json_response(conn_one, 200)
- assert Repo.get(Activity, id)
+ assert Activity.get_by_id(id)
conn_two =
conn
@@ -138,7 +141,56 @@ test "posting a sensitive status", %{conn: conn} do
|> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
- assert Repo.get(Activity, id)
+ assert Activity.get_by_id(id)
+ end
+
+ test "posting a fake status", %{conn: conn} do
+ user = insert(:user)
+
+ real_conn =
+ conn
+ |> assign(:user, user)
+ |> 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(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
+ |> assign(:user, user)
+ |> 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(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 "posting a status with OGP link preview", %{conn: conn} do
@@ -153,7 +205,7 @@ test "posting a status with OGP link preview", %{conn: conn} do
})
assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
- assert Repo.get(Activity, id)
+ assert Activity.get_by_id(id)
Pleroma.Config.put([:rich_media, :enabled], false)
end
@@ -168,7 +220,7 @@ test "posting a direct status", %{conn: conn} do
|> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
- assert activity = Repo.get(Activity, id)
+ assert activity = Activity.get_by_id(id)
assert activity.recipients == [user2.ap_id, user1.ap_id]
assert activity.data["to"] == [user2.ap_id]
assert activity.data["cc"] == []
@@ -287,10 +339,10 @@ test "replying to a status", %{conn: conn} do
assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
- activity = Repo.get(Activity, id)
+ activity = Activity.get_by_id(id)
assert activity.data["context"] == replied_to.data["context"]
- assert activity.data["object"]["inReplyToStatusId"] == replied_to.id
+ assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
end
test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
@@ -303,7 +355,7 @@ test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
- activity = Repo.get(Activity, id)
+ activity = Activity.get_by_id(id)
assert activity
end
@@ -332,6 +384,53 @@ test "verify_credentials default scope unlisted", %{conn: conn} do
assert id == to_string(user.id)
end
+ test "apps/verify_credentials", %{conn: conn} do
+ token = insert(:oauth_token)
+
+ conn =
+ conn
+ |> assign(:user, token.user)
+ |> assign(: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(conn, 200)
+ end
+
+ test "creates an oauth app", %{conn: conn} do
+ user = insert(:user)
+ app_attrs = build(:oauth_app)
+
+ conn =
+ conn
+ |> 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(conn, 200)
+ end
+
test "get a status", %{conn: conn} do
activity = insert(:note_activity)
@@ -346,7 +445,7 @@ test "get a status", %{conn: conn} do
describe "deleting a status" do
test "when you created it", %{conn: conn} do
activity = insert(:note_activity)
- author = User.get_by_ap_id(activity.data["actor"])
+ author = User.get_cached_by_ap_id(activity.data["actor"])
conn =
conn
@@ -355,7 +454,7 @@ test "when you created it", %{conn: conn} do
assert %{} = json_response(conn, 200)
- refute Repo.get(Activity, activity.id)
+ refute Activity.get_by_id(activity.id)
end
test "when you didn't create it", %{conn: conn} do
@@ -369,7 +468,7 @@ test "when you didn't create it", %{conn: conn} do
assert %{"error" => _} = json_response(conn, 403)
- assert Repo.get(Activity, activity.id) == activity
+ assert Activity.get_by_id(activity.id) == activity
end
test "when you're an admin or moderator", %{conn: conn} do
@@ -392,8 +491,8 @@ test "when you're an admin or moderator", %{conn: conn} do
assert %{} = json_response(res_conn, 200)
- refute Repo.get(Activity, activity1.id)
- refute Repo.get(Activity, activity2.id)
+ refute Activity.get_by_id(activity1.id)
+ refute Activity.get_by_id(activity2.id)
end
end
@@ -755,6 +854,148 @@ test "clearing all notifications", %{conn: conn} do
assert all = json_response(conn, 200)
assert all == []
end
+
+ test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
+ user = insert(:user)
+ 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 = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
+ notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
+ notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
+ notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
+
+ conn =
+ conn
+ |> assign(:user, user)
+
+ # min_id
+ conn_res =
+ conn
+ |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
+
+ result = json_response(conn_res, 200)
+ assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
+
+ # since_id
+ conn_res =
+ conn
+ |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
+
+ result = json_response(conn_res, 200)
+ assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
+
+ # max_id
+ conn_res =
+ conn
+ |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
+
+ result = json_response(conn_res, 200)
+ assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
+ end
+
+ test "filters notifications using exclude_types", %{conn: conn} do
+ user = insert(:user)
+ 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(create_activity.id, other_user)
+ {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
+ {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
+
+ mention_notification_id =
+ Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
+
+ favorite_notification_id =
+ Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
+
+ reblog_notification_id =
+ Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
+
+ follow_notification_id =
+ Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
+
+ conn =
+ conn
+ |> assign(:user, user)
+
+ conn_res =
+ get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
+
+ assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
+
+ conn_res =
+ get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
+
+ assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
+
+ conn_res =
+ get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
+
+ assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
+
+ conn_res =
+ get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
+
+ assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
+ end
+
+ test "destroy multiple", %{conn: conn} do
+ user = insert(:user)
+ 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 = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
+ notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
+ notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
+ notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
+
+ conn =
+ conn
+ |> assign(:user, user)
+
+ conn_res =
+ conn
+ |> get("/api/v1/notifications")
+
+ result = json_response(conn_res, 200)
+ assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
+
+ conn2 =
+ conn
+ |> assign(:user, other_user)
+
+ conn_res =
+ conn2
+ |> get("/api/v1/notifications")
+
+ result = json_response(conn_res, 200)
+ assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
+
+ conn_destroy =
+ conn
+ |> delete("/api/v1/notifications/destroy_multiple", %{
+ "ids" => [notification1_id, notification2_id]
+ })
+
+ assert json_response(conn_destroy, 200) == %{}
+
+ conn_res =
+ conn2
+ |> get("/api/v1/notifications")
+
+ result = json_response(conn_res, 200)
+ assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
+ end
end
describe "reblogging" do
@@ -767,8 +1008,47 @@ test "reblogs and returns the reblogged status", %{conn: conn} do
|> assign(:user, user)
|> post("/api/v1/statuses/#{activity.id}/reblog")
- assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
- json_response(conn, 200)
+ assert %{
+ "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
+ "reblogged" => true
+ } = json_response(conn, 200)
+
+ assert to_string(activity.id) == id
+ end
+
+ test "reblogged status for another user", %{conn: conn} do
+ activity = insert(:note_activity)
+ user1 = insert(:user)
+ user2 = insert(:user)
+ user3 = insert(:user)
+ CommonAPI.favorite(activity.id, user2)
+ {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
+ {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
+ {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
+
+ conn_res =
+ conn
+ |> assign(:user, user3)
+ |> get("/api/v1/statuses/#{reblog_activity1.id}")
+
+ assert %{
+ "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
+ "reblogged" => false,
+ "favourited" => false,
+ "bookmarked" => false
+ } = json_response(conn_res, 200)
+
+ conn_res =
+ conn
+ |> assign(:user, user2)
+ |> get("/api/v1/statuses/#{reblog_activity1.id}")
+
+ assert %{
+ "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
+ "reblogged" => true,
+ "favourited" => true,
+ "bookmarked" => true
+ } = json_response(conn_res, 200)
assert to_string(activity.id) == id
end
@@ -887,7 +1167,7 @@ test "gets a users statuses", %{conn: conn} do
test "unimplemented pinned statuses feature", %{conn: conn} do
note = insert(:note_activity)
- user = User.get_by_ap_id(note.data["actor"])
+ user = User.get_cached_by_ap_id(note.data["actor"])
conn =
conn
@@ -898,7 +1178,7 @@ test "unimplemented pinned statuses feature", %{conn: conn} do
test "gets an users media", %{conn: conn} do
note = insert(:note_activity)
- user = User.get_by_ap_id(note.data["actor"])
+ user = User.get_cached_by_ap_id(note.data["actor"])
file = %Plug.Upload{
content_type: "image/jpg",
@@ -973,8 +1253,8 @@ test "/api/v1/follow_requests works" do
{:ok, _activity} = ActivityPub.follow(other_user, user)
- user = Repo.get(User, user.id)
- other_user = Repo.get(User, other_user.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
@@ -993,8 +1273,8 @@ test "/api/v1/follow_requests/:id/authorize works" do
{:ok, _activity} = ActivityPub.follow(other_user, user)
- user = Repo.get(User, user.id)
- other_user = Repo.get(User, other_user.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
@@ -1006,8 +1286,8 @@ test "/api/v1/follow_requests/:id/authorize works" do
assert relationship = json_response(conn, 200)
assert to_string(other_user.id) == relationship["id"]
- user = Repo.get(User, user.id)
- other_user = Repo.get(User, other_user.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
@@ -1030,7 +1310,7 @@ test "/api/v1/follow_requests/:id/reject works" do
{:ok, _activity} = ActivityPub.follow(other_user, user)
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
conn =
build_conn()
@@ -1040,8 +1320,8 @@ test "/api/v1/follow_requests/:id/reject works" do
assert relationship = json_response(conn, 200)
assert to_string(other_user.id) == relationship["id"]
- user = Repo.get(User, user.id)
- other_user = Repo.get(User, other_user.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
@@ -1232,7 +1512,7 @@ test "getting followers, pagination", %{conn: conn} do
assert id2 == follower2.id
assert [link_header] = get_resp_header(res_conn, "link")
- assert link_header =~ ~r/since_id=#{follower2.id}/
+ assert link_header =~ ~r/min_id=#{follower2.id}/
assert link_header =~ ~r/max_id=#{follower2.id}/
end
@@ -1311,7 +1591,7 @@ test "getting following, pagination", %{conn: conn} do
assert id2 == following2.id
assert [link_header] = get_resp_header(res_conn, "link")
- assert link_header =~ ~r/since_id=#{following2.id}/
+ assert link_header =~ ~r/min_id=#{following2.id}/
assert link_header =~ ~r/max_id=#{following2.id}/
end
@@ -1326,7 +1606,7 @@ test "following / unfollowing a user", %{conn: conn} do
assert %{"id" => _id, "following" => true} = json_response(conn, 200)
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
conn =
build_conn()
@@ -1335,7 +1615,7 @@ test "following / unfollowing a user", %{conn: conn} do
assert %{"id" => _id, "following" => false} = json_response(conn, 200)
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
conn =
build_conn()
@@ -1346,6 +1626,78 @@ test "following / unfollowing a user", %{conn: conn} do
assert id == to_string(other_user.id)
end
+ test "following without reblogs" do
+ follower = insert(:user)
+ followed = insert(:user)
+ other_user = insert(:user)
+
+ conn =
+ build_conn()
+ |> assign(:user, follower)
+ |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
+
+ assert %{"showing_reblogs" => false} = json_response(conn, 200)
+
+ {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
+ {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
+
+ conn =
+ build_conn()
+ |> assign(:user, User.get_cached_by_id(follower.id))
+ |> get("/api/v1/timelines/home")
+
+ assert [] == json_response(conn, 200)
+
+ conn =
+ build_conn()
+ |> assign(:user, follower)
+ |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
+
+ assert %{"showing_reblogs" => true} = json_response(conn, 200)
+
+ conn =
+ build_conn()
+ |> assign(:user, User.get_cached_by_id(follower.id))
+ |> get("/api/v1/timelines/home")
+
+ expected_activity_id = reblog.id
+ assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
+ end
+
+ test "following / unfollowing errors" do
+ user = insert(:user)
+
+ conn =
+ build_conn()
+ |> assign(:user, user)
+
+ # self follow
+ conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
+ assert %{"error" => "Record not found"} = json_response(conn_res, 404)
+
+ # self unfollow
+ user = User.get_cached_by_id(user.id)
+ conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
+ assert %{"error" => "Record not found"} = json_response(conn_res, 404)
+
+ # self follow via uri
+ user = User.get_cached_by_id(user.id)
+ conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
+ assert %{"error" => "Record not found"} = json_response(conn_res, 404)
+
+ # follow non existing user
+ conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
+ assert %{"error" => "Record not found"} = json_response(conn_res, 404)
+
+ # follow non existing user via uri
+ conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
+ assert %{"error" => "Record not found"} = json_response(conn_res, 404)
+
+ # unfollow non existing user
+ conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
+ assert %{"error" => "Record not found"} = json_response(conn_res, 404)
+ end
+
test "muting / unmuting a user", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)
@@ -1357,7 +1709,7 @@ test "muting / unmuting a user", %{conn: conn} do
assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
conn =
build_conn()
@@ -1367,6 +1719,25 @@ test "muting / unmuting a user", %{conn: conn} do
assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
end
+ test "subscribing / unsubscribing to a user", %{conn: conn} do
+ user = insert(:user)
+ subscription_target = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
+
+ assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
+
+ conn =
+ build_conn()
+ |> assign(:user, user)
+ |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
+
+ assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
+ end
+
test "getting a list of mutes", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)
@@ -1393,7 +1764,7 @@ test "blocking / unblocking a user", %{conn: conn} do
assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
conn =
build_conn()
@@ -1552,7 +1923,7 @@ test "search doesn't show statuses that it shouldn't", %{conn: conn} do
capture_log(fn ->
conn =
conn
- |> get("/api/v1/search", %{"q" => activity.data["object"]["id"]})
+ |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
assert results = json_response(conn, 200)
@@ -1617,6 +1988,199 @@ test "returns the favorites of a user", %{conn: conn} do
assert [] = json_response(third_conn, 200)
end
+ describe "getting favorites timeline of specified user" do
+ setup do
+ [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
+ [current_user: current_user, user: user]
+ end
+
+ test "returns list of statuses favorited by specified user", %{
+ conn: conn,
+ current_user: current_user,
+ user: user
+ } do
+ [activity | _] = insert_pair(:note_activity)
+ CommonAPI.favorite(activity.id, user)
+
+ response =
+ conn
+ |> assign(:user, current_user)
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response(:ok)
+
+ [like] = response
+
+ assert length(response) == 1
+ assert like["id"] == activity.id
+ end
+
+ test "returns favorites for specified user_id when user is not logged in", %{
+ conn: conn,
+ user: user
+ } do
+ activity = insert(:note_activity)
+ CommonAPI.favorite(activity.id, user)
+
+ response =
+ conn
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response(:ok)
+
+ assert length(response) == 1
+ end
+
+ test "returns favorited DM only when user is logged in and he is one of recipients", %{
+ conn: conn,
+ current_user: current_user,
+ user: user
+ } do
+ {:ok, direct} =
+ CommonAPI.post(current_user, %{
+ "status" => "Hi @#{user.nickname}!",
+ "visibility" => "direct"
+ })
+
+ CommonAPI.favorite(direct.id, user)
+
+ response =
+ conn
+ |> assign(:user, current_user)
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response(:ok)
+
+ assert length(response) == 1
+
+ anonymous_response =
+ conn
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response(:ok)
+
+ assert length(anonymous_response) == 0
+ end
+
+ test "does not return others' favorited DM when user is not one of recipients", %{
+ conn: conn,
+ current_user: current_user,
+ user: user
+ } do
+ user_two = insert(:user)
+
+ {:ok, direct} =
+ CommonAPI.post(user_two, %{
+ "status" => "Hi @#{user.nickname}!",
+ "visibility" => "direct"
+ })
+
+ CommonAPI.favorite(direct.id, user)
+
+ response =
+ conn
+ |> assign(:user, current_user)
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response(:ok)
+
+ assert length(response) == 0
+ end
+
+ test "paginates favorites using since_id and max_id", %{
+ conn: conn,
+ current_user: current_user,
+ user: user
+ } do
+ activities = insert_list(10, :note_activity)
+
+ Enum.each(activities, fn activity ->
+ CommonAPI.favorite(activity.id, user)
+ end)
+
+ third_activity = Enum.at(activities, 2)
+ seventh_activity = Enum.at(activities, 6)
+
+ response =
+ conn
+ |> assign(:user, current_user)
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
+ since_id: third_activity.id,
+ max_id: seventh_activity.id
+ })
+ |> json_response(:ok)
+
+ assert length(response) == 3
+ refute third_activity in response
+ refute seventh_activity in response
+ end
+
+ test "limits favorites using limit parameter", %{
+ conn: conn,
+ current_user: current_user,
+ user: user
+ } do
+ 7
+ |> insert_list(:note_activity)
+ |> Enum.each(fn activity ->
+ CommonAPI.favorite(activity.id, user)
+ end)
+
+ response =
+ conn
+ |> assign(:user, current_user)
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
+ |> json_response(:ok)
+
+ assert length(response) == 3
+ end
+
+ test "returns empty response when user does not have any favorited statuses", %{
+ conn: conn,
+ current_user: current_user,
+ user: user
+ } do
+ response =
+ conn
+ |> assign(:user, current_user)
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response(: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(conn, 404) == %{"error" => "Record not found"}
+ end
+
+ test "returns 403 error when user has hidden own favorites", %{
+ conn: conn,
+ current_user: current_user
+ } do
+ user = insert(:user, %{info: %{hide_favorites: true}})
+ activity = insert(:note_activity)
+ CommonAPI.favorite(activity.id, user)
+
+ conn =
+ conn
+ |> assign(:user, current_user)
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+
+ assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
+ end
+
+ test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
+ user = insert(:user)
+ activity = insert(:note_activity)
+ CommonAPI.favorite(activity.id, user)
+
+ conn =
+ conn
+ |> assign(:user, current_user)
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+
+ assert user.info.hide_favorites
+ assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
+ end
+ end
+
describe "updating credentials" do
test "updates the user's bio", %{conn: conn} do
user = insert(:user)
@@ -1632,7 +2196,7 @@ test "updates the user's bio", %{conn: conn} do
assert user = json_response(conn, 200)
assert user["note"] ==
- ~s(I drink
#cofe with
#cofe with @) <> user2.nickname <> ~s( )
@@ -1650,6 +2214,78 @@ test "updates the user's locking status", %{conn: conn} do
assert user["locked"] == true
end
+ test "updates the user's default scope", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
+
+ assert user = json_response(conn, 200)
+ assert user["source"]["privacy"] == "cofe"
+ end
+
+ test "updates the user's hide_followers status", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
+
+ assert user = json_response(conn, 200)
+ assert user["pleroma"]["hide_followers"] == true
+ end
+
+ test "updates the user's hide_follows status", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
+
+ assert user = json_response(conn, 200)
+ assert user["pleroma"]["hide_follows"] == true
+ end
+
+ test "updates the user's hide_favorites status", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
+
+ assert user = json_response(conn, 200)
+ assert user["pleroma"]["hide_favorites"] == true
+ end
+
+ test "updates the user's show_role status", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
+
+ assert user = json_response(conn, 200)
+ assert user["source"]["pleroma"]["show_role"] == false
+ end
+
+ test "updates the user's no_rich_text status", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
+
+ assert user = json_response(conn, 200)
+ assert user["source"]["pleroma"]["no_rich_text"] == true
+ end
+
test "updates the user's name", %{conn: conn} do
user = insert(:user)
@@ -1718,6 +2354,30 @@ test "requires 'write' permission", %{conn: conn} do
end
test "get instance information", %{conn: conn} do
+ conn = get(conn, "/api/v1/instance")
+ assert result = json_response(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" => _
+ } = result
+
+ assert email == from_config_email
+ end
+
+ test "get instance stats", %{conn: conn} do
user = insert(:user, %{local: true})
user2 = insert(:user, %{local: true})
@@ -1729,7 +2389,7 @@ test "get instance information", %{conn: conn} do
{:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
# Stats should count users with missing or nil `info.deactivated` value
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
info_change = Changeset.change(user.info, %{deactivated: nil})
{:ok, _user} =
@@ -2101,8 +2761,372 @@ test "preserves parameters in link headers", %{conn: conn} do
assert [link_header] = get_resp_header(conn, "link")
assert link_header =~ ~r/media_only=true/
- assert link_header =~ ~r/since_id=#{notification2.id}/
+ assert link_header =~ ~r/min_id=#{notification2.id}/
assert link_header =~ ~r/max_id=#{notification1.id}/
end
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"})
+
+ resp_one =
+ conn
+ |> get("/api/v1/accounts/#{user_one.id}")
+
+ resp_two =
+ conn
+ |> get("/api/v1/accounts/#{user_two.nickname}")
+
+ resp_three =
+ conn
+ |> get("/api/v1/accounts/#{user_two.id}")
+
+ acc_one = json_response(resp_one, 200)
+ acc_two = json_response(resp_two, 200)
+ acc_three = json_response(resp_three, 200)
+ refute acc_one == acc_two
+ assert acc_two == acc_three
+ end
+
+ describe "custom emoji" do
+ test "with tags", %{conn: conn} do
+ [emoji | _body] =
+ conn
+ |> get("/api/v1/custom_emojis")
+ |> json_response(200)
+
+ 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, "url")
+ assert Map.has_key?(emoji, "visible_in_picker")
+ end
+ 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 "does not redirect logged in users to the login page", %{conn: conn, path: path} do
+ token = insert(:oauth_token)
+
+ conn =
+ conn
+ |> assign(:user, token.user)
+ |> put_session(:oauth_token, 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
+
+ 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 "scheduled activities" do
+ test "creates a scheduled activity", %{conn: conn} do
+ user = insert(:user)
+ scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> post("/api/v1/statuses", %{
+ "status" => "scheduled",
+ "scheduled_at" => scheduled_at
+ })
+
+ assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
+ assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
+ assert [] == Repo.all(Activity)
+ end
+
+ test "creates a scheduled activity with a media attachment", %{conn: conn} do
+ user = insert(:user)
+ scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
+
+ 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
+ |> assign(:user, user)
+ |> post("/api/v1/statuses", %{
+ "media_ids" => [to_string(upload.id)],
+ "status" => "scheduled",
+ "scheduled_at" => scheduled_at
+ })
+
+ assert %{"media_attachments" => [media_attachment]} = json_response(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
+ user = insert(:user)
+
+ scheduled_at =
+ NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> post("/api/v1/statuses", %{
+ "status" => "not scheduled",
+ "scheduled_at" => scheduled_at
+ })
+
+ assert %{"content" => "not scheduled"} = json_response(conn, 200)
+ assert [] == Repo.all(ScheduledActivity)
+ end
+
+ test "returns error when daily user limit is exceeded", %{conn: conn} 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)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
+
+ assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
+ end
+
+ test "returns error when total user limit is exceeded", %{conn: conn} 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()
+
+ 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
+ |> assign(:user, user)
+ |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
+
+ assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
+ end
+
+ test "shows scheduled activities", %{conn: conn} do
+ user = insert(:user)
+ 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()
+
+ conn =
+ conn
+ |> assign(:user, user)
+
+ # min_id
+ conn_res =
+ conn
+ |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
+
+ result = json_response(conn_res, 200)
+ assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
+
+ # since_id
+ conn_res =
+ conn
+ |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
+
+ result = json_response(conn_res, 200)
+ assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
+
+ # max_id
+ conn_res =
+ conn
+ |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
+
+ result = json_response(conn_res, 200)
+ assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
+ end
+
+ test "shows a scheduled activity", %{conn: conn} do
+ user = insert(:user)
+ scheduled_activity = insert(:scheduled_activity, user: user)
+
+ res_conn =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
+
+ assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
+ assert scheduled_activity_id == scheduled_activity.id |> to_string()
+
+ res_conn =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/scheduled_statuses/404")
+
+ assert %{"error" => "Record not found"} = json_response(res_conn, 404)
+ end
+
+ test "updates a scheduled activity", %{conn: conn} do
+ user = insert(:user)
+ scheduled_activity = insert(:scheduled_activity, user: user)
+
+ new_scheduled_at =
+ NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
+
+ res_conn =
+ conn
+ |> assign(:user, user)
+ |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
+ scheduled_at: new_scheduled_at
+ })
+
+ assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
+ assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
+
+ res_conn =
+ conn
+ |> assign(:user, user)
+ |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
+
+ assert %{"error" => "Record not found"} = json_response(res_conn, 404)
+ end
+
+ test "deletes a scheduled activity", %{conn: conn} do
+ user = insert(:user)
+ scheduled_activity = insert(:scheduled_activity, user: user)
+
+ res_conn =
+ conn
+ |> assign(:user, user)
+ |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
+
+ assert %{} = json_response(res_conn, 200)
+ assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
+
+ res_conn =
+ conn
+ |> assign(:user, user)
+ |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
+
+ assert %{"error" => "Record not found"} = json_response(res_conn, 404)
+ 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} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
+
+ # Reply to status from another user
+ conn1 =
+ conn
+ |> assign(:user, user2)
+ |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
+
+ assert %{"content" => "xD", "id" => id} = json_response(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)
+ |> post("/api/v1/statuses/#{activity.id}/reblog")
+
+ assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
+ json_response(conn2, 200)
+
+ assert to_string(activity.id) == id
+
+ # Getting third user status
+ conn3 =
+ conn
+ |> assign(:user, user3)
+ |> 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
end
diff --git a/test/web/mastodon_api/notification_view_test.exs b/test/web/mastodon_api/notification_view_test.exs
new file mode 100644
index 000000000..977ea1e87
--- /dev/null
+++ b/test/web/mastodon_api/notification_view_test.exs
@@ -0,0 +1,104 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Activity
+ alias Pleroma.Notification
+ 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
+ import Pleroma.Factory
+
+ 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},
+ type: "mention",
+ account: AccountView.render("account.json", %{user: user, for: mentioned_user}),
+ status: StatusView.render("status.json", %{activity: activity, for: mentioned_user}),
+ created_at: Utils.to_masto_date(notification.inserted_at)
+ }
+
+ result =
+ NotificationView.render("index.json", %{notifications: [notification], for: mentioned_user})
+
+ assert [expected] == result
+ end
+
+ test "Favourite notification" do
+ user = insert(:user)
+ another_user = insert(:user)
+ {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
+ {:ok, favorite_activity, _object} = CommonAPI.favorite(create_activity.id, another_user)
+ {: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},
+ type: "favourite",
+ account: AccountView.render("account.json", %{user: another_user, for: user}),
+ status: StatusView.render("status.json", %{activity: create_activity, for: user}),
+ created_at: Utils.to_masto_date(notification.inserted_at)
+ }
+
+ result = NotificationView.render("index.json", %{notifications: [notification], for: user})
+
+ assert [expected] == result
+ end
+
+ test "Reblog notification" do
+ user = insert(:user)
+ another_user = insert(:user)
+ {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
+ {:ok, reblog_activity, _object} = 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},
+ type: "reblog",
+ account: AccountView.render("account.json", %{user: another_user, for: user}),
+ status: StatusView.render("status.json", %{activity: reblog_activity, for: user}),
+ created_at: Utils.to_masto_date(notification.inserted_at)
+ }
+
+ result = NotificationView.render("index.json", %{notifications: [notification], for: user})
+
+ assert [expected] == result
+ 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},
+ type: "follow",
+ account: AccountView.render("account.json", %{user: follower, for: followed}),
+ created_at: Utils.to_masto_date(notification.inserted_at)
+ }
+
+ result =
+ NotificationView.render("index.json", %{notifications: [notification], for: followed})
+
+ assert [expected] == result
+ end
+end
diff --git a/test/web/mastodon_api/scheduled_activity_view_test.exs b/test/web/mastodon_api/scheduled_activity_view_test.exs
new file mode 100644
index 000000000..ecbb855d4
--- /dev/null
+++ b/test/web/mastodon_api/scheduled_activity_view_test.exs
@@ -0,0 +1,68 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 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/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs
index 3eec2cb5b..5fddc6c58 100644
--- a/test/web/mastodon_api/status_view_test.exs
+++ b/test/web/mastodon_api/status_view_test.exs
@@ -6,9 +6,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
use Pleroma.DataCase
alias Pleroma.Activity
+ alias Pleroma.Bookmark
+ alias Pleroma.Object
+ alias Pleroma.Repo
alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.OStatus
@@ -52,14 +55,14 @@ test "tries to get a user by nickname if fetching by ap_id doesn't work" do
test "a note with null content" do
note = insert(:note_activity)
+ note_object = Object.normalize(note.data["object"])
data =
- note.data
- |> put_in(["object", "content"], nil)
+ note_object.data
+ |> Map.put("content", nil)
- note =
- note
- |> Map.put(:data, data)
+ Object.change(note_object, %{data: data})
+ |> Object.update_and_set_cache()
User.get_cached_by_ap_id(note.data["actor"])
@@ -72,6 +75,8 @@ test "a note activity" do
note = insert(:note_activity)
user = User.get_cached_by_ap_id(note.data["actor"])
+ convo_id = Utils.context_to_conversation_id(note.data["object"]["context"])
+
status = StatusView.render("status.json", %{activity: note})
created_at =
@@ -98,7 +103,7 @@ test "a note activity" do
muted: false,
pinned: false,
sensitive: false,
- spoiler_text: note.data["object"]["summary"],
+ spoiler_text: HtmlSanitizeEx.basic_html(note.data["object"]["summary"]),
visibility: "public",
media_attachments: [],
mentions: [],
@@ -122,7 +127,11 @@ test "a note activity" do
}
],
pleroma: %{
- local: true
+ local: true,
+ conversation_id: convo_id,
+ in_reply_to_account_acct: nil,
+ content: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["content"])},
+ spoiler_text: %{"text/plain" => HtmlSanitizeEx.strip_tags(note.data["object"]["summary"])}
}
}
@@ -145,6 +154,25 @@ test "tells if the message is muted for some reason" do
assert status.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("status.json", %{activity: activity})
+
+ assert status.bookmarked == false
+
+ status = StatusView.render("status.json", %{activity: activity, for: user})
+
+ assert status.bookmarked == false
+
+ {:ok, _bookmark} = Bookmark.create(user.id, activity.id)
+
+ status = StatusView.render("status.json", %{activity: activity, for: user})
+
+ assert status.bookmarked == true
+ end
+
test "a reply" do
note = insert(:note_activity)
user = insert(:user)
@@ -171,7 +199,7 @@ test "contains mentions" do
status = StatusView.render("status.json", %{activity: activity})
- actor = Repo.get_by(User, ap_id: activity.actor)
+ actor = User.get_cached_by_ap_id(activity.actor)
assert status.mentions ==
Enum.map([user, actor], fn u -> AccountView.render("mention.json", %{user: u}) end)
@@ -196,7 +224,8 @@ test "attachments" do
remote_url: "someurl",
preview_url: "someurl",
text_url: "someurl",
- description: nil
+ description: nil,
+ pleroma: %{mime_type: "image/png"}
}
assert expected == StatusView.render("attachment.json", %{attachment: object})
@@ -223,7 +252,7 @@ test "a peertube video" do
user = insert(:user)
{:ok, object} =
- ActivityPub.fetch_object_from_id(
+ Pleroma.Object.Fetcher.fetch_object_from_id(
"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
)
diff --git a/test/web/metadata/rel_me_test.exs b/test/web/metadata/rel_me_test.exs
new file mode 100644
index 000000000..f66bf7834
--- /dev/null
+++ b/test/web/metadata/rel_me_test.exs
@@ -0,0 +1,18 @@
+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
+ "], []},
+ {:link, [rel: "me", href: "https://another-link.com"], []}
+ ]
+ end
+end
diff --git a/test/web/node_info_test.exs b/test/web/node_info_test.exs
index 038feecc1..2fc42b7cc 100644
--- a/test/web/node_info_test.exs
+++ b/test/web/node_info_test.exs
@@ -108,4 +108,27 @@ test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do
assert result = json_response(conn, 200)
assert Pleroma.Application.repository() == result["software"]["repository"]
end
+
+ test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
+ option = Pleroma.Config.get([:instance, :safe_dm_mentions])
+ Pleroma.Config.put([:instance, :safe_dm_mentions], true)
+
+ response =
+ conn
+ |> get("/nodeinfo/2.1.json")
+ |> json_response(:ok)
+
+ assert "safe_dm_mentions" in response["metadata"]["features"]
+
+ Pleroma.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"]
+
+ Pleroma.Config.put([:instance, :safe_dm_mentions], option)
+ end
end
diff --git a/test/web/oauth/ldap_authorization_test.exs b/test/web/oauth/ldap_authorization_test.exs
new file mode 100644
index 000000000..0eb191c76
--- /dev/null
+++ b/test/web/oauth/ldap_authorization_test.exs
@@ -0,0 +1,195 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 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 ExUnit.CaptureLog
+ import Mock
+
+ @skip if !Code.ensure_loaded?(:eldap), do: :skip
+
+ setup_all do
+ ldap_authenticator =
+ Pleroma.Config.get(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.PleromaAuthenticator)
+
+ ldap_enabled = Pleroma.Config.get([:ldap, :enabled])
+
+ on_exit(fn ->
+ Pleroma.Config.put(Pleroma.Web.Auth.Authenticator, ldap_authenticator)
+ Pleroma.Config.put([:ldap, :enabled], ldap_enabled)
+ end)
+
+ Pleroma.Config.put(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.LDAPAuthenticator)
+ Pleroma.Config.put([:ldap, :enabled], true)
+
+ :ok
+ end
+
+ @tag @skip
+ test "authorizes the existing user using LDAP credentials" do
+ password = "testpassword"
+ user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(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, '', [{'mail', [to_charlist(user.email)]}]}],
+ []}}
+ 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 "falls back to the default authorization when LDAP is unavailable" do
+ password = "testpassword"
+ user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(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} | _] -> {:error, 'connect failed'} end,
+ simple_bind: fn _connection, _dn, ^password -> :ok end,
+ close: fn _connection ->
+ send(self(), :close_connection)
+ :ok
+ end
+ ]}
+ ] do
+ log =
+ capture_log(fn ->
+ 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
+ end)
+
+ assert log =~ "Could not open LDAP connection: 'connect failed'"
+ refute_received :close_connection
+ end
+ end
+
+ @tag @skip
+ test "disallow authorization for wrong LDAP credentials" do
+ password = "testpassword"
+ user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(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/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs
index ed94416ff..6e96537ec 100644
--- a/test/web/oauth/oauth_controller_test.exs
+++ b/test/web/oauth/oauth_controller_test.exs
@@ -5,265 +5,713 @@
defmodule Pleroma.Web.OAuth.OAuthControllerTest do
use Pleroma.Web.ConnCase
import Pleroma.Factory
+ import Mock
+ alias Pleroma.Registration
alias Pleroma.Repo
alias Pleroma.Web.OAuth.Authorization
alias Pleroma.Web.OAuth.Token
- test "redirects with oauth authorization" do
- user = insert(:user)
- app = insert(:oauth_app, scopes: ["read", "write", "follow"])
+ @session_opts [
+ store: :cookie,
+ key: "_test",
+ signing_salt: "cooldude"
+ ]
- 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"
- }
- })
+ describe "in OAuth consumer mode, " do
+ setup do
+ oauth_consumer_strategies_path = [:auth, :oauth_consumer_strategies]
+ oauth_consumer_strategies = Pleroma.Config.get(oauth_consumer_strategies_path)
+ Pleroma.Config.put(oauth_consumer_strategies_path, ~w(twitter facebook))
- target = redirected_to(conn)
- assert target =~ app.redirect_uris
+ on_exit(fn ->
+ Pleroma.Config.put(oauth_consumer_strategies_path, oauth_consumer_strategies)
+ end)
- 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 == ["read", "write"]
- end
-
- test "returns 401 for wrong credentials", %{conn: conn} do
- user = insert(:user)
- app = insert(:oauth_app)
-
- result =
- conn
- |> post("/oauth/authorize", %{
- "authorization" => %{
- "name" => user.nickname,
- "password" => "wrong",
- "client_id" => app.client_id,
- "redirect_uri" => app.redirect_uris,
- "state" => "statepassed",
- "scope" => Enum.join(app.scopes, " ")
- }
- })
- |> html_response(:unauthorized)
-
- # Keep the details
- assert result =~ app.client_id
- assert result =~ app.redirect_uris
-
- # Error message
- assert result =~ "Invalid Username/Password"
- end
-
- test "returns 401 for missing scopes", %{conn: conn} do
- user = insert(:user)
- app = insert(:oauth_app)
-
- result =
- conn
- |> post("/oauth/authorize", %{
- "authorization" => %{
- "name" => user.nickname,
- "password" => "test",
- "client_id" => app.client_id,
- "redirect_uri" => app.redirect_uris,
- "state" => "statepassed",
- "scope" => ""
- }
- })
- |> html_response(:unauthorized)
-
- # Keep the details
- assert result =~ app.client_id
- assert result =~ app.redirect_uris
-
- # Error message
- assert result =~ "Permissions not specified"
- end
-
- test "returns 401 for scopes beyond app scopes", %{conn: conn} do
- user = insert(:user)
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- result =
- conn
- |> post("/oauth/authorize", %{
- "authorization" => %{
- "name" => user.nickname,
- "password" => "test",
- "client_id" => app.client_id,
- "redirect_uri" => app.redirect_uris,
- "state" => "statepassed",
- "scope" => "read write follow"
- }
- })
- |> html_response(:unauthorized)
-
- # Keep the details
- assert result =~ app.client_id
- assert result =~ app.redirect_uris
-
- # Error message
- assert result =~ "Permissions not specified"
- end
-
- 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" => app.redirect_uris,
- "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 == auth.scopes
- 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: Comeonin.Pbkdf2.hashpwsalt(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 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" => app.redirect_uris
- })
-
- 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 "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" => app.redirect_uris
- })
-
- 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
- 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)
+ [
+ app: insert(:oauth_app),
+ conn:
+ build_conn()
+ |> Plug.Session.call(Plug.Session.init(@session_opts))
+ |> fetch_session()
+ ]
end
- password = "testpassword"
- user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
- info_change = Pleroma.User.Info.confirmation_changeset(user.info, :unconfirmed)
+ 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" => app.redirect_uris,
+ "scope" => "read"
+ }
+ )
- {:ok, user} =
- user
- |> Ecto.Changeset.change()
- |> Ecto.Changeset.put_embed(:info, info_change)
- |> Repo.update()
+ assert response = html_response(conn, 200)
+ assert response =~ "Sign in with Twitter"
+ assert response =~ o_auth_path(conn, :prepare_request)
+ end
- refute Pleroma.User.auth_active?(user)
+ 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" => app.redirect_uris,
+ "state" => "a_state"
+ }
+ }
+ )
- app = insert(:oauth_app)
+ assert response = html_response(conn, 302)
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
+ 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)
+
+ state_params = %{
+ "scope" => Enum.join(app.scopes, " "),
"client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
+ "redirect_uri" => app.redirect_uris,
+ "state" => ""
+ }
- assert resp = json_response(conn, 403)
- assert %{"error" => _} = resp
- refute Map.has_key?(resp, "access_token")
+ with_mock Pleroma.Web.Auth.Authenticator,
+ get_registration: fn _ -> {:ok, registration} end do
+ conn =
+ get(
+ conn,
+ "/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/#{app.redirect_uris}\?code=.+/
+ end
+ end
+
+ test "with user-unbound registration, GET /oauth//callback renders registration_details page",
+ %{app: app, conn: conn} do
+ registration = insert(:registration, user: nil)
+
+ state_params = %{
+ "scope" => "read write",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state"
+ }
+
+ with_mock Pleroma.Web.Auth.Authenticator,
+ get_registration: fn _ -> {:ok, registration} end do
+ conn =
+ get(
+ conn,
+ "/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 =~ Registration.email(registration)
+ assert response =~ Registration.nickname(registration)
+ end
+ 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" => app.redirect_uris,
+ "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" => app.redirect_uris,
+ "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})
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post(
+ "/oauth/register",
+ %{
+ "op" => "register",
+ "authorization" => %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state",
+ "nickname" => "availablenick",
+ "email" => "available@email.com"
+ }
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/
+ 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" => app.redirect_uris,
+ "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: Comeonin.Pbkdf2.hashpwsalt("testpassword"))
+ registration = insert(:registration, user: nil)
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post(
+ "/oauth/register",
+ %{
+ "op" => "connect",
+ "authorization" => %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "a_state",
+ "name" => user.nickname,
+ "password" => "testpassword"
+ }
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/
+ 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" => app.redirect_uris,
+ "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
- test "rejects an invalid authorization code" do
- app = insert(:oauth_app)
+ 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
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "authorization_code",
- "code" => "Imobviouslyinvalid",
- "redirect_uri" => app.redirect_uris,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
+ test "renders authentication page", %{app: app, conn: conn} do
+ conn =
+ get(
+ conn,
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "scope" => "read"
+ }
+ )
- assert resp = json_response(conn, 400)
- assert %{"error" => _} = json_response(conn, 400)
- refute Map.has_key?(resp, "access_token")
+ 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" => app.redirect_uris,
+ "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_id: app.id)
+
+ conn =
+ conn
+ |> put_session(:oauth_token, token.token)
+ |> get(
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "scope" => "read",
+ "force_login" => "true"
+ }
+ )
+
+ assert html_response(conn, 200) =~ ~s(type="submit")
+ end
+
+ test "redirects to app if user is already authenticated", %{app: app, conn: conn} do
+ token = insert(:oauth_token, app_id: app.id)
+
+ conn =
+ conn
+ |> put_session(:oauth_token, token.token)
+ |> get(
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "scope" => "read"
+ }
+ )
+
+ assert redirected_to(conn) == "https://redirect.url"
+ end
+ end
+
+ describe "POST /oauth/authorize" do
+ test "redirects with oauth authorization" do
+ user = insert(:user)
+ 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"
+ }
+ })
+
+ target = redirected_to(conn)
+ assert target =~ app.redirect_uris
+
+ 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 == ["read", "write"]
+ end
+
+ test "returns 401 for wrong credentials", %{conn: conn} do
+ user = insert(:user)
+ app = insert(:oauth_app)
+
+ result =
+ conn
+ |> post("/oauth/authorize", %{
+ "authorization" => %{
+ "name" => user.nickname,
+ "password" => "wrong",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "statepassed",
+ "scope" => Enum.join(app.scopes, " ")
+ }
+ })
+ |> html_response(:unauthorized)
+
+ # Keep the details
+ assert result =~ app.client_id
+ assert result =~ app.redirect_uris
+
+ # Error message
+ assert result =~ "Invalid Username/Password"
+ end
+
+ test "returns 401 for missing scopes", %{conn: conn} do
+ user = insert(:user)
+ app = insert(:oauth_app)
+
+ result =
+ conn
+ |> post("/oauth/authorize", %{
+ "authorization" => %{
+ "name" => user.nickname,
+ "password" => "test",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "statepassed",
+ "scope" => ""
+ }
+ })
+ |> html_response(:unauthorized)
+
+ # Keep the details
+ assert result =~ app.client_id
+ assert result =~ app.redirect_uris
+
+ # Error message
+ assert result =~ "This action is outside the authorized scopes"
+ end
+
+ test "returns 401 for scopes beyond app scopes", %{conn: conn} do
+ user = insert(:user)
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ result =
+ conn
+ |> post("/oauth/authorize", %{
+ "authorization" => %{
+ "name" => user.nickname,
+ "password" => "test",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "state" => "statepassed",
+ "scope" => "read write follow"
+ }
+ })
+ |> html_response(:unauthorized)
+
+ # Keep the details
+ assert result =~ app.client_id
+ assert result =~ app.redirect_uris
+
+ # 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" => app.redirect_uris,
+ "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: Comeonin.Pbkdf2.hashpwsalt(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 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" => app.redirect_uris
+ })
+
+ 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 "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" => app.redirect_uris
+ })
+
+ 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
+ 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
+
+ password = "testpassword"
+ user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
+ info_change = Pleroma.User.Info.confirmation_changeset(user.info, :unconfirmed)
+
+ {:ok, user} =
+ user
+ |> Ecto.Changeset.change()
+ |> Ecto.Changeset.put_embed(:info, info_change)
+ |> Repo.update()
+
+ refute Pleroma.User.auth_active?(user)
+
+ 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: Comeonin.Pbkdf2.hashpwsalt(password),
+ info: %{deactivated: true}
+ )
+
+ 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" => app.redirect_uris,
+ "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
end
diff --git a/test/web/ostatus/activity_representer_test.exs b/test/web/ostatus/activity_representer_test.exs
index 5cb135b4c..a4bb68c4d 100644
--- a/test/web/ostatus/activity_representer_test.exs
+++ b/test/web/ostatus/activity_representer_test.exs
@@ -116,10 +116,10 @@ test "an announce activity" do
{:ok, announce, _object} = ActivityPub.announce(user, object)
- announce = Repo.get(Activity, announce.id)
+ announce = Activity.get_by_id(announce.id)
note_user = User.get_cached_by_ap_id(note.data["actor"])
- note = Repo.get(Activity, note.id)
+ note = Activity.get_by_id(note.id)
note_xml =
ActivityRepresenter.to_simple_form(note, note_user, true)
diff --git a/test/web/ostatus/incoming_documents/delete_handling_test.exs b/test/web/ostatus/incoming_documents/delete_handling_test.exs
index 412d894fd..ca6e61339 100644
--- a/test/web/ostatus/incoming_documents/delete_handling_test.exs
+++ b/test/web/ostatus/incoming_documents/delete_handling_test.exs
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.OStatus.DeleteHandlingTest do
alias Pleroma.Activity
alias Pleroma.Object
- alias Pleroma.Repo
alias Pleroma.Web.OStatus
setup do
@@ -32,10 +31,10 @@ test "it removes the mentioned activity" do
{:ok, [delete]} = OStatus.handle_incoming(incoming)
- refute Repo.get(Activity, note.id)
- refute Repo.get(Activity, like.id)
+ refute Activity.get_by_id(note.id)
+ refute Activity.get_by_id(like.id)
assert Object.get_by_ap_id(note.data["object"]["id"]).data["type"] == "Tombstone"
- assert Repo.get(Activity, second_note.id)
+ assert Activity.get_by_id(second_note.id)
assert Object.get_by_ap_id(second_note.data["object"]["id"])
assert delete.data["type"] == "Delete"
diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs
index 2950f11c0..7441e5fce 100644
--- a/test/web/ostatus/ostatus_controller_test.exs
+++ b/test/web/ostatus/ostatus_controller_test.exs
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
use Pleroma.Web.ConnCase
import Pleroma.Factory
alias Pleroma.Object
- alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.OStatus.ActivityRepresenter
@@ -41,7 +40,8 @@ test "decodes a salmon with a changed magic key", %{conn: conn} do
assert response(conn, 200)
# Set a wrong magic-key for a user so it has to refetch
- salmon_user = User.get_by_ap_id("http://gs.example.org:4040/index.php/user/1")
+ salmon_user = User.get_cached_by_ap_id("http://gs.example.org:4040/index.php/user/1")
+
# Wrong key
info_cng =
User.Info.remote_user_creation(salmon_user.info, %{
@@ -52,7 +52,7 @@ test "decodes a salmon with a changed magic key", %{conn: conn} do
salmon_user
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_embed(:info, info_cng)
- |> Repo.update()
+ |> User.update_and_set_cache()
conn =
build_conn()
@@ -86,7 +86,7 @@ test "returns 404 for a missing feed", %{conn: conn} do
test "gets an object", %{conn: conn} do
note_activity = insert(:note_activity)
- user = User.get_by_ap_id(note_activity.data["actor"])
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]))
url = "/objects/#{uuid}"
diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs
index 76b90e186..2916caf8d 100644
--- a/test/web/ostatus/ostatus_test.exs
+++ b/test/web/ostatus/ostatus_test.exs
@@ -28,34 +28,35 @@ test "don't insert create notes twice" do
test "handle incoming note - GS, Salmon" do
incoming = File.read!("test/fixtures/incoming_note_activity.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"])
- user = User.get_by_ap_id(activity.data["actor"])
+ user = User.get_cached_by_ap_id(activity.data["actor"])
assert user.info.note_count == 1
assert activity.data["type"] == "Create"
- assert activity.data["object"]["type"] == "Note"
+ assert object.data["type"] == "Note"
- assert activity.data["object"]["id"] ==
- "tag:gs.example.org:4040,2017-04-23:noticeId=29:objectType=note"
+ assert object.data["id"] == "tag:gs.example.org:4040,2017-04-23:noticeId=29:objectType=note"
assert activity.data["published"] == "2017-04-23T14:51:03+00:00"
- assert activity.data["object"]["published"] == "2017-04-23T14:51:03+00:00"
+ assert object.data["published"] == "2017-04-23T14:51:03+00:00"
assert activity.data["context"] ==
"tag:gs.example.org:4040,2017-04-23:objectType=thread:nonce=f09e22f58abd5c7b"
assert "http://pleroma.example.org:4000/users/lain3" in activity.data["to"]
- assert activity.data["object"]["emoji"] == %{"marko" => "marko.png", "reimu" => "reimu.png"}
+ assert object.data["emoji"] == %{"marko" => "marko.png", "reimu" => "reimu.png"}
assert activity.local == false
end
test "handle incoming notes - GS, subscription" do
incoming = File.read!("test/fixtures/ostatus_incoming_post.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"])
assert activity.data["type"] == "Create"
- assert activity.data["object"]["type"] == "Note"
- assert activity.data["object"]["actor"] == "https://social.heldscal.la/user/23211"
- assert activity.data["object"]["content"] == "Will it blend?"
+ assert object.data["type"] == "Note"
+ assert object.data["actor"] == "https://social.heldscal.la/user/23211"
+ assert object.data["content"] == "Will it blend?"
user = User.get_cached_by_ap_id(activity.data["actor"])
assert User.ap_followers(user) in activity.data["to"]
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
@@ -64,20 +65,22 @@ test "handle incoming notes - GS, subscription" do
test "handle incoming notes with attachments - GS, subscription" do
incoming = File.read!("test/fixtures/incoming_websub_gnusocial_attachments.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"])
assert activity.data["type"] == "Create"
- assert activity.data["object"]["type"] == "Note"
- assert activity.data["object"]["actor"] == "https://social.heldscal.la/user/23211"
- assert activity.data["object"]["attachment"] |> length == 2
- assert activity.data["object"]["external_url"] == "https://social.heldscal.la/notice/2020923"
+ assert object.data["type"] == "Note"
+ assert object.data["actor"] == "https://social.heldscal.la/user/23211"
+ assert object.data["attachment"] |> length == 2
+ assert object.data["external_url"] == "https://social.heldscal.la/notice/2020923"
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
end
test "handle incoming notes with tags" do
incoming = File.read!("test/fixtures/ostatus_incoming_post_tag.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"])
- assert activity.data["object"]["tag"] == ["nsfw"]
+ assert object.data["tag"] == ["nsfw"]
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
end
@@ -92,10 +95,11 @@ test "handle incoming notes - Mastodon, salmon, reply" do
incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"])
assert activity.data["type"] == "Create"
- assert activity.data["object"]["type"] == "Note"
- assert activity.data["object"]["actor"] == "https://mastodon.social/users/lambadalambda"
+ assert object.data["type"] == "Note"
+ assert object.data["actor"] == "https://mastodon.social/users/lambadalambda"
assert activity.data["context"] == "2hu"
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
end
@@ -103,42 +107,47 @@ test "handle incoming notes - Mastodon, salmon, reply" do
test "handle incoming notes - Mastodon, with CW" do
incoming = File.read!("test/fixtures/mastodon-note-cw.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"])
assert activity.data["type"] == "Create"
- assert activity.data["object"]["type"] == "Note"
- assert activity.data["object"]["actor"] == "https://mastodon.social/users/lambadalambda"
- assert activity.data["object"]["summary"] == "technologic"
+ assert object.data["type"] == "Note"
+ assert object.data["actor"] == "https://mastodon.social/users/lambadalambda"
+ assert object.data["summary"] == "technologic"
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
end
test "handle incoming unlisted messages, put public into cc" do
incoming = File.read!("test/fixtures/mastodon-note-unlisted.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"])
+
refute "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["cc"]
- refute "https://www.w3.org/ns/activitystreams#Public" in activity.data["object"]["to"]
- assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["object"]["cc"]
+ refute "https://www.w3.org/ns/activitystreams#Public" in object.data["to"]
+ assert "https://www.w3.org/ns/activitystreams#Public" in object.data["cc"]
end
test "handle incoming retweets - Mastodon, with CW" do
incoming = File.read!("test/fixtures/cw_retweet.xml")
{:ok, [[_activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
+ retweeted_object = Object.normalize(retweeted_activity.data["object"])
- assert retweeted_activity.data["object"]["summary"] == "Hey."
+ assert retweeted_object.data["summary"] == "Hey."
end
test "handle incoming notes - GS, subscription, reply" do
incoming = File.read!("test/fixtures/ostatus_incoming_reply.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"])
assert activity.data["type"] == "Create"
- assert activity.data["object"]["type"] == "Note"
- assert activity.data["object"]["actor"] == "https://social.heldscal.la/user/23211"
+ assert object.data["type"] == "Note"
+ assert object.data["actor"] == "https://social.heldscal.la/user/23211"
- assert activity.data["object"]["content"] ==
+ assert object.data["content"] ==
"@shpbot why not indeed."
- assert activity.data["object"]["inReplyTo"] ==
+ assert object.data["inReplyTo"] ==
"tag:gs.archae.me,2017-04-30:noticeId=778260:objectType=note"
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
@@ -150,17 +159,18 @@ test "handle incoming retweets - GS, subscription" do
assert activity.data["type"] == "Announce"
assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
- assert activity.data["object"] == retweeted_activity.data["object"]["id"]
+ assert activity.data["object"] == retweeted_activity.data["object"]
assert "https://pleroma.soykaf.com/users/lain" in activity.data["to"]
refute activity.local
- retweeted_activity = Repo.get(Activity, retweeted_activity.id)
+ retweeted_activity = Activity.get_by_id(retweeted_activity.id)
+ retweeted_object = Object.normalize(retweeted_activity.data["object"])
assert retweeted_activity.data["type"] == "Create"
assert retweeted_activity.data["actor"] == "https://pleroma.soykaf.com/users/lain"
refute retweeted_activity.local
- assert retweeted_activity.data["object"]["announcement_count"] == 1
- assert String.contains?(retweeted_activity.data["object"]["content"], "mastodon")
- refute String.contains?(retweeted_activity.data["object"]["content"], "Test account")
+ assert retweeted_object.data["announcement_count"] == 1
+ assert String.contains?(retweeted_object.data["content"], "mastodon")
+ refute String.contains?(retweeted_object.data["content"], "Test account")
end
test "handle incoming retweets - GS, subscription - local message" do
@@ -181,7 +191,7 @@ test "handle incoming retweets - GS, subscription - local message" do
assert user.ap_id in activity.data["to"]
refute activity.local
- retweeted_activity = Repo.get(Activity, retweeted_activity.id)
+ retweeted_activity = Activity.get_by_id(retweeted_activity.id)
assert note_activity.id == retweeted_activity.id
assert retweeted_activity.data["type"] == "Create"
assert retweeted_activity.data["actor"] == user.ap_id
@@ -192,10 +202,11 @@ test "handle incoming retweets - GS, subscription - local message" do
test "handle incoming retweets - Mastodon, salmon" do
incoming = File.read!("test/fixtures/share.xml")
{:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming)
+ retweeted_object = Object.normalize(retweeted_activity.data["object"])
assert activity.data["type"] == "Announce"
assert activity.data["actor"] == "https://mastodon.social/users/lambadalambda"
- assert activity.data["object"] == retweeted_activity.data["object"]["id"]
+ assert activity.data["object"] == retweeted_activity.data["object"]
assert activity.data["id"] ==
"tag:mastodon.social,2017-05-03:objectId=4934452:objectType=Status"
@@ -204,7 +215,7 @@ test "handle incoming retweets - Mastodon, salmon" do
assert retweeted_activity.data["type"] == "Create"
assert retweeted_activity.data["actor"] == "https://pleroma.soykaf.com/users/lain"
refute retweeted_activity.local
- refute String.contains?(retweeted_activity.data["object"]["content"], "Test account")
+ refute String.contains?(retweeted_object.data["content"], "Test account")
end
test "handle incoming favorites - GS, websub" do
@@ -214,7 +225,7 @@ test "handle incoming favorites - GS, websub" do
assert activity.data["type"] == "Like"
assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
- assert activity.data["object"] == favorited_activity.data["object"]["id"]
+ assert activity.data["object"] == favorited_activity.data["object"]
assert activity.data["id"] ==
"tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061643:2017-05-05T09:12:50+00:00"
@@ -223,7 +234,7 @@ test "handle incoming favorites - GS, websub" do
assert favorited_activity.data["type"] == "Create"
assert favorited_activity.data["actor"] == "https://shitposter.club/user/1"
- assert favorited_activity.data["object"]["id"] ==
+ assert favorited_activity.data["object"] ==
"tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
refute favorited_activity.local
@@ -258,17 +269,17 @@ test "handle incoming favorites with locally available object - GS, websub" do
test "handle incoming replies" do
incoming = File.read!("test/fixtures/incoming_note_activity_answer.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"])
assert activity.data["type"] == "Create"
- assert activity.data["object"]["type"] == "Note"
+ assert object.data["type"] == "Note"
- assert activity.data["object"]["inReplyTo"] ==
+ assert object.data["inReplyTo"] ==
"http://pleroma.example.org:4000/objects/55bce8fc-b423-46b1-af71-3759ab4670bc"
assert "http://pleroma.example.org:4000/users/lain5" in activity.data["to"]
- assert activity.data["object"]["id"] ==
- "tag:gs.example.org:4040,2017-04-25:noticeId=55:objectType=note"
+ assert object.data["id"] == "tag:gs.example.org:4040,2017-04-25:noticeId=55:objectType=note"
assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
end
@@ -285,8 +296,8 @@ test "handle incoming follows" do
assert activity.data["object"] == "https://pawoo.net/users/pekorino"
refute activity.local
- follower = User.get_by_ap_id(activity.data["actor"])
- followed = User.get_by_ap_id(activity.data["object"])
+ follower = User.get_cached_by_ap_id(activity.data["actor"])
+ followed = User.get_cached_by_ap_id(activity.data["object"])
assert User.following?(follower, followed)
end
@@ -309,8 +320,8 @@ test "handle incoming unfollows with existing follow" do
assert activity.data["object"]["object"] == "https://pawoo.net/users/pekorino"
refute activity.local
- follower = User.get_by_ap_id(activity.data["actor"])
- followed = User.get_by_ap_id(activity.data["object"]["object"])
+ follower = User.get_cached_by_ap_id(activity.data["actor"])
+ followed = User.get_cached_by_ap_id(activity.data["object"]["object"])
refute User.following?(follower, followed)
end
@@ -344,7 +355,7 @@ test "tries to use the information in poco fields" do
{:ok, user} = OStatus.find_or_make_user(uri)
- user = Repo.get(Pleroma.User, user.id)
+ user = Pleroma.User.get_cached_by_id(user.id)
assert user.name == "Constance Variable"
assert user.nickname == "lambadalambda@social.heldscal.la"
assert user.local == false
@@ -495,7 +506,7 @@ test "it builds a missing status from an html url" do
assert activity.data["actor"] == "https://shitposter.club/user/1"
- assert activity.data["object"]["id"] ==
+ assert activity.data["object"] ==
"tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
end)
end
@@ -504,7 +515,7 @@ test "it works for atom notes, too" do
url = "https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056"
{:ok, [activity]} = OStatus.fetch_activity_from_url(url)
assert activity.data["actor"] == "https://social.sakamoto.gq/users/eal"
- assert activity.data["object"]["id"] == url
+ assert activity.data["object"] == url
end
end
diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs
index 3f9f3d809..1e948086a 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
+ alias Pleroma.Object
+ alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Push.Impl
alias Pleroma.Web.Push.Subscription
@@ -52,29 +54,27 @@ test "performs sending notifications" do
data: %{alerts: %{"follow" => true, "mention" => false}}
)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => " "Create",
- "actor" => user.ap_id,
- "object" => %{"content" => "
+ "Lorem ipsum dolor sit amet , consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
+ })
+
+ object = Object.normalize(activity)
+
assert Impl.format_body(
%{
- activity: %{
- data: %{
- "type" => "Create",
- "object" => %{
- "content" =>
- "Lorem ipsum dolor sit amet , consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis."
- }
- }
- }
+ activity: activity
},
- %{nickname: "Bob"}
+ user,
+ object
) ==
"@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
end
test "renders body for follow activity" do
- assert Impl.format_body(%{activity: %{data: %{"type" => "Follow"}}}, %{nickname: "Bob"}) ==
+ user = insert(:user, nickname: "Bob")
+ other_user = insert(:user)
+ {:ok, _, _, activity} = CommonAPI.follow(user, other_user)
+ object = Object.normalize(activity)
+
+ assert Impl.format_body(%{activity: activity}, user, object) ==
"@Bob has followed you"
end
test "renders body for announce activity" do
user = insert(:user)
- note =
- insert(:note, %{
- data: %{
- "content" =>
- "Lorem ipsum dolor sit amet , consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis."
- }
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ "status" =>
+ "Lorem ipsum dolor sit amet , consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
})
- note_activity = insert(:note_activity, %{note: note})
- announce_activity = insert(:announce_activity, %{user: user, note_activity: note_activity})
+ {:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user)
+ object = Object.normalize(activity)
- assert Impl.format_body(%{activity: announce_activity}, user) ==
+ assert Impl.format_body(%{activity: announce_activity}, user, object) ==
"@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
end
test "renders body for like activity" do
- assert Impl.format_body(%{activity: %{data: %{"type" => "Like"}}}, %{nickname: "Bob"}) ==
+ 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(activity.id, user)
+ object = Object.normalize(activity)
+
+ assert Impl.format_body(%{activity: activity}, user, object) ==
"@Bob has favorited your post"
end
end
diff --git a/test/web/salmon/salmon_test.exs b/test/web/salmon/salmon_test.exs
index 265e1abbd..7532578ca 100644
--- a/test/web/salmon/salmon_test.exs
+++ b/test/web/salmon/salmon_test.exs
@@ -99,7 +99,7 @@ test "it pushes an activity to remote accounts it's addressed to" do
}
{:ok, activity} = Repo.insert(%Activity{data: activity_data, recipients: activity_data["to"]})
- user = Repo.get_by(User, ap_id: activity.data["actor"])
+ user = User.get_cached_by_ap_id(activity.data["actor"])
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user)
poster = fn url, _data, _headers ->
diff --git a/test/web/streamer_test.exs b/test/web/streamer_test.exs
index 0a2e91298..bfe18cb7f 100644
--- a/test/web/streamer_test.exs
+++ b/test/web/streamer_test.exs
@@ -202,4 +202,34 @@ test "it send wanted private posts to list" do
Task.await(task)
end
+
+ test "it doesn't send muted reblogs" do
+ user1 = insert(:user)
+ user2 = insert(:user)
+ user3 = insert(:user)
+ CommonAPI.hide_reblogs(user1, user2)
+
+ task =
+ Task.async(fn ->
+ refute_receive {:text, _}, 1_000
+ end)
+
+ fake_socket = %{
+ transport_pid: task.pid,
+ assigns: %{
+ user: user1
+ }
+ }
+
+ {:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
+ {:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2)
+
+ topics = %{
+ "public" => [fake_socket]
+ }
+
+ Streamer.push_to_socket(topics, "public", announce_activity)
+
+ Task.await(task)
+ end
end
diff --git a/test/web/twitter_api/representers/activity_representer_test.exs b/test/web/twitter_api/representers/activity_representer_test.exs
deleted file mode 100644
index d154385a0..000000000
--- a/test/web/twitter_api/representers/activity_representer_test.exs
+++ /dev/null
@@ -1,170 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do
- use Pleroma.DataCase
- alias Pleroma.Activity
- alias Pleroma.Object
- alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter
- alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter
- alias Pleroma.Web.TwitterAPI.UserView
- import Pleroma.Factory
-
- test "a like activity" do
- user = insert(:user)
- note_activity = insert(:note_activity)
- object = Object.get_by_ap_id(note_activity.data["object"]["id"])
-
- {:ok, like_activity, _object} = ActivityPub.like(user, object)
-
- status =
- ActivityRepresenter.to_map(like_activity, %{user: user, liked_activity: note_activity})
-
- assert status["id"] == like_activity.id
- assert status["in_reply_to_status_id"] == note_activity.id
-
- note_activity = Activity.get_by_ap_id(note_activity.data["id"])
- activity_actor = Repo.get_by(User, ap_id: note_activity.data["actor"])
- liked_status = ActivityRepresenter.to_map(note_activity, %{user: activity_actor, for: user})
- assert liked_status["favorited"] == true
- assert status["activity_type"] == "like"
- end
-
- test "an activity" do
- user = insert(:user)
- # {:ok, mentioned_user } = UserBuilder.insert(%{nickname: "shp", ap_id: "shp"})
- mentioned_user = insert(:user, %{nickname: "shp"})
-
- # {:ok, follower} = UserBuilder.insert(%{following: [User.ap_followers(user)]})
- follower = insert(:user, %{following: [User.ap_followers(user)]})
-
- object = %Object{
- data: %{
- "type" => "Image",
- "url" => [
- %{
- "type" => "Link",
- "mediaType" => "image/jpg",
- "href" => "http://example.org/image.jpg"
- }
- ],
- "uuid" => 1
- }
- }
-
- content_html =
- "Some :2hu: content mentioning @shp"
-
- content = HtmlSanitizeEx.strip_tags(content_html)
- date = DateTime.from_naive!(~N[2016-05-24 13:26:08.003], "Etc/UTC") |> DateTime.to_iso8601()
-
- {:ok, convo_object} = Object.context_mapping("2hu") |> Repo.insert()
-
- to = [
- User.ap_followers(user),
- "https://www.w3.org/ns/activitystreams#Public",
- mentioned_user.ap_id
- ]
-
- activity = %Activity{
- id: 1,
- data: %{
- "type" => "Create",
- "id" => "id",
- "to" => to,
- "actor" => User.ap_id(user),
- "object" => %{
- "published" => date,
- "type" => "Note",
- "content" => content_html,
- "summary" => "2hu :2hu:",
- "inReplyToStatusId" => 213_123,
- "attachment" => [
- object
- ],
- "external_url" => "some url",
- "like_count" => 5,
- "announcement_count" => 3,
- "context" => "2hu",
- "tag" => ["content", "mentioning", "nsfw"],
- "emoji" => %{
- "2hu" => "corndog.png"
- }
- },
- "published" => date,
- "context" => "2hu"
- },
- local: false,
- recipients: to
- }
-
- corndog_emojo = ~s( )
-
- expected_html =
- ~s(2hu ) <>
- corndog_emojo <>
- ~s(
alert\('YAY'\)Some ) <>
- corndog_emojo <>
- ~s( content mentioning @shp )
-
- expected_status = %{
- "id" => activity.id,
- "user" => UserView.render("show.json", %{user: user, for: follower}),
- "is_local" => false,
- "statusnet_html" => expected_html,
- "text" => "2hu :2hu:" <> content,
- "is_post_verb" => true,
- "created_at" => "Tue May 24 13:26:08 +0000 2016",
- "in_reply_to_status_id" => 213_123,
- "in_reply_to_screen_name" => nil,
- "in_reply_to_user_id" => nil,
- "in_reply_to_profileurl" => nil,
- "in_reply_to_ostatus_uri" => nil,
- "statusnet_conversation_id" => convo_object.id,
- "attachments" => [
- ObjectRepresenter.to_map(object)
- ],
- "attentions" => [
- UserView.render("show.json", %{user: mentioned_user, for: follower})
- ],
- "fave_num" => 5,
- "repeat_num" => 3,
- "favorited" => false,
- "repeated" => false,
- "pinned" => false,
- "external_url" => "some url",
- "tags" => ["nsfw", "content", "mentioning"],
- "activity_type" => "post",
- "possibly_sensitive" => true,
- "uri" => activity.data["object"]["id"],
- "visibility" => "direct",
- "card" => nil,
- "muted" => false,
- "summary" => "2hu :2hu:",
- "summary_html" =>
- "2hu "
- }
-
- assert ActivityRepresenter.to_map(activity, %{
- user: user,
- for: follower,
- mentioned: [mentioned_user]
- }) == expected_status
- end
-
- test "a delete activity" do
- object = insert(:note)
- user = User.get_by_ap_id(object.data["actor"])
-
- {:ok, delete} = ActivityPub.delete(object)
-
- map = ActivityRepresenter.to_map(delete, %{user: user})
-
- assert map["is_post_verb"] == false
- assert map["activity_type"] == "delete"
- assert map["uri"] == object.data["id"]
- end
-end
diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs
index 1b810c9a0..43ad71a16 100644
--- a/test/web/twitter_api/twitter_api_controller_test.exs
+++ b/test/web/twitter_api/twitter_api_controller_test.exs
@@ -16,14 +16,15 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.OAuth.Token
+ alias Pleroma.Web.TwitterAPI.ActivityView
alias Pleroma.Web.TwitterAPI.Controller
alias Pleroma.Web.TwitterAPI.NotificationView
- alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter
alias Pleroma.Web.TwitterAPI.TwitterAPI
alias Pleroma.Web.TwitterAPI.UserView
- import Pleroma.Factory
import Mock
+ import Pleroma.Factory
+ import Swoosh.TestAssertions
@banner "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
@@ -116,7 +117,11 @@ test "with credentials", %{conn: conn, user: user} do
|> post(request_path, %{status: "Nice meme.", visibility: "private"})
assert json_response(conn, 200) ==
- ActivityRepresenter.to_map(Repo.one(Activity), %{user: user, for: user})
+ ActivityView.render("activity.json", %{
+ activity: Repo.one(Activity),
+ user: user,
+ for: user
+ })
end
end
@@ -265,7 +270,7 @@ test "returns 200 to authenticated request when the instance is public",
test "returns one status", %{conn: conn} do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey!"})
- actor = Repo.get_by!(User, ap_id: activity.data["actor"])
+ actor = User.get_cached_by_ap_id(activity.data["actor"])
conn =
conn
@@ -273,7 +278,7 @@ test "returns one status", %{conn: conn} do
response = json_response(conn, 200)
- assert response == ActivityRepresenter.to_map(activity, %{user: actor})
+ assert response == ActivityView.render("activity.json", %{activity: activity, user: actor})
end
end
@@ -372,7 +377,8 @@ test "with credentials", %{conn: conn, user: current_user} do
assert response ==
Enum.map(returned_activities, fn activity ->
- ActivityRepresenter.to_map(activity, %{
+ ActivityView.render("activity.json", %{
+ activity: activity,
user: User.get_cached_by_ap_id(activity.data["actor"]),
for: current_user
})
@@ -469,10 +475,10 @@ test "with credentials", %{conn: conn, user: current_user} do
assert length(response) == 1
assert Enum.at(response, 0) ==
- ActivityRepresenter.to_map(activity, %{
+ ActivityView.render("activity.json", %{
user: current_user,
for: current_user,
- mentioned: [current_user]
+ activity: activity
})
end
@@ -594,7 +600,9 @@ test "with user_id", %{conn: conn} do
conn = get(conn, "/api/statuses/user_timeline.json", %{"user_id" => user.id})
response = json_response(conn, 200)
assert length(response) == 1
- assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
+
+ assert Enum.at(response, 0) ==
+ ActivityView.render("activity.json", %{user: user, activity: activity})
end
test "with screen_name", %{conn: conn} do
@@ -604,7 +612,9 @@ test "with screen_name", %{conn: conn} do
conn = get(conn, "/api/statuses/user_timeline.json", %{"screen_name" => user.nickname})
response = json_response(conn, 200)
assert length(response) == 1
- assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
+
+ assert Enum.at(response, 0) ==
+ ActivityView.render("activity.json", %{user: user, activity: activity})
end
test "with credentials", %{conn: conn, user: current_user} do
@@ -620,7 +630,11 @@ test "with credentials", %{conn: conn, user: current_user} do
assert length(response) == 1
assert Enum.at(response, 0) ==
- ActivityRepresenter.to_map(activity, %{user: current_user, for: current_user})
+ ActivityView.render("activity.json", %{
+ user: current_user,
+ for: current_user,
+ activity: activity
+ })
end
test "with credentials with user_id", %{conn: conn, user: current_user} do
@@ -635,7 +649,9 @@ test "with credentials with user_id", %{conn: conn, user: current_user} do
response = json_response(conn, 200)
assert length(response) == 1
- assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
+
+ assert Enum.at(response, 0) ==
+ ActivityView.render("activity.json", %{user: user, activity: activity})
end
test "with credentials screen_name", %{conn: conn, user: current_user} do
@@ -650,7 +666,9 @@ test "with credentials screen_name", %{conn: conn, user: current_user} do
response = json_response(conn, 200)
assert length(response) == 1
- assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
+
+ assert Enum.at(response, 0) ==
+ ActivityView.render("activity.json", %{user: user, activity: activity})
end
test "with credentials with user_id, excluding RTs", %{conn: conn, user: current_user} do
@@ -669,7 +687,9 @@ test "with credentials with user_id, excluding RTs", %{conn: conn, user: current
response = json_response(conn, 200)
assert length(response) == 1
- assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
+
+ assert Enum.at(response, 0) ==
+ ActivityView.render("activity.json", %{user: user, activity: activity})
conn =
conn
@@ -678,7 +698,9 @@ test "with credentials with user_id, excluding RTs", %{conn: conn, user: current
response = json_response(conn, 200)
assert length(response) == 1
- assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user})
+
+ assert Enum.at(response, 0) ==
+ ActivityView.render("activity.json", %{user: user, activity: activity})
end
end
@@ -698,7 +720,7 @@ test "with credentials", %{conn: conn, user: current_user} do
|> with_credentials(current_user.nickname, "test")
|> post("/api/friendships/create.json", %{user_id: followed.id})
- current_user = Repo.get(User, current_user.id)
+ current_user = User.get_cached_by_id(current_user.id)
assert User.ap_followers(followed) in current_user.following
assert json_response(conn, 200) ==
@@ -713,8 +735,8 @@ test "for restricted account", %{conn: conn, user: current_user} do
|> with_credentials(current_user.nickname, "test")
|> post("/api/friendships/create.json", %{user_id: followed.id})
- current_user = Repo.get(User, current_user.id)
- followed = Repo.get(User, followed.id)
+ current_user = User.get_cached_by_id(current_user.id)
+ followed = User.get_cached_by_id(followed.id)
refute User.ap_followers(followed) in current_user.following
@@ -743,7 +765,7 @@ test "with credentials", %{conn: conn, user: current_user} do
|> with_credentials(current_user.nickname, "test")
|> post("/api/friendships/destroy.json", %{user_id: followed.id})
- current_user = Repo.get(User, current_user.id)
+ current_user = User.get_cached_by_id(current_user.id)
assert current_user.following == [current_user.ap_id]
assert json_response(conn, 200) ==
@@ -767,7 +789,7 @@ test "with credentials", %{conn: conn, user: current_user} do
|> with_credentials(current_user.nickname, "test")
|> post("/api/blocks/create.json", %{user_id: blocked.id})
- current_user = Repo.get(User, current_user.id)
+ current_user = User.get_cached_by_id(current_user.id)
assert User.blocks?(current_user, blocked)
assert json_response(conn, 200) ==
@@ -794,7 +816,7 @@ test "with credentials", %{conn: conn, user: current_user} do
|> with_credentials(current_user.nickname, "test")
|> post("/api/blocks/destroy.json", %{user_id: blocked.id})
- current_user = Repo.get(User, current_user.id)
+ current_user = User.get_cached_by_id(current_user.id)
assert current_user.info.blocks == []
assert json_response(conn, 200) ==
@@ -825,7 +847,7 @@ test "with credentials", %{conn: conn, user: current_user} do
|> with_credentials(current_user.nickname, "test")
|> post("/api/qvitter/update_avatar.json", %{img: avatar_image})
- current_user = Repo.get(User, current_user.id)
+ current_user = User.get_cached_by_id(current_user.id)
assert is_map(current_user.avatar)
assert json_response(conn, 200) ==
@@ -933,11 +955,15 @@ test "with credentials", %{conn: conn, user: current_user} do
|> with_credentials(current_user.nickname, "test")
|> post(request_path)
- activity = Repo.get(Activity, note_activity.id)
- activity_user = Repo.get_by(User, ap_id: note_activity.data["actor"])
+ activity = Activity.get_by_id(note_activity.id)
+ activity_user = User.get_cached_by_ap_id(note_activity.data["actor"])
assert json_response(response, 200) ==
- ActivityRepresenter.to_map(activity, %{user: activity_user, for: current_user})
+ ActivityView.render("activity.json", %{
+ user: activity_user,
+ for: current_user,
+ activity: activity
+ })
end
end
@@ -967,11 +993,15 @@ test "with credentials", %{conn: conn, user: current_user} do
|> with_credentials(current_user.nickname, "test")
|> post(request_path)
- activity = Repo.get(Activity, note_activity.id)
- activity_user = Repo.get_by(User, ap_id: note_activity.data["actor"])
+ activity = Activity.get_by_id(note_activity.id)
+ activity_user = User.get_cached_by_ap_id(note_activity.data["actor"])
assert json_response(response, 200) ==
- ActivityRepresenter.to_map(activity, %{user: activity_user, for: current_user})
+ ActivityView.render("activity.json", %{
+ user: activity_user,
+ for: current_user,
+ activity: activity
+ })
end
end
@@ -992,7 +1022,7 @@ test "it creates a new user", %{conn: conn} do
user = json_response(conn, 200)
- fetched_user = Repo.get_by(User, nickname: "lain")
+ fetched_user = User.get_cached_by_nickname("lain")
assert user == UserView.render("show.json", %{user: fetched_user})
end
@@ -1034,8 +1064,14 @@ test "it creates a PasswordResetToken record for user", %{user: user} do
test "it sends an email to user", %{user: user} do
token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
- Swoosh.TestAssertions.assert_email_sent(
- Pleroma.UserEmail.password_reset_email(user, token_record.token)
+ email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
+ notify_email = Pleroma.Config.get([:instance, :notify_email])
+ instance_name = Pleroma.Config.get([:instance, :name])
+
+ assert_email_sent(
+ from: {instance_name, notify_email},
+ to: {user.name, user.email},
+ html_body: email.html_body
)
end
end
@@ -1080,7 +1116,7 @@ test "it redirects to root url", %{conn: conn, user: user} do
test "it confirms the user account", %{conn: conn, user: user} do
get(conn, "/api/account/confirm_email/#{user.id}/#{user.info.confirmation_token}")
- user = Repo.get(User, user.id)
+ user = User.get_cached_by_id(user.id)
refute user.info.confirmation_pending
refute user.info.confirmation_token
@@ -1134,7 +1170,15 @@ test "it sends confirmation email", %{conn: conn, user: user} do
|> assign(:user, user)
|> post("/api/account/resend_confirmation_email?email=#{user.email}")
- Swoosh.TestAssertions.assert_email_sent(Pleroma.UserEmail.account_confirmation_email(user))
+ email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
+ notify_email = Pleroma.Config.get([:instance, :notify_email])
+ instance_name = Pleroma.Config.get([:instance, :name])
+
+ assert_email_sent(
+ from: {instance_name, notify_email},
+ to: {user.name, user.email},
+ html_body: email.html_body
+ )
end
end
@@ -1698,7 +1742,7 @@ test "with credentials, valid password and matching new password and confirmatio
})
assert json_response(conn, 200) == %{"status" => "success"}
- fetched_user = Repo.get(User, current_user.id)
+ fetched_user = User.get_cached_by_id(current_user.id)
assert Pbkdf2.checkpw("newpass", fetched_user.password_hash) == true
end
end
@@ -1739,8 +1783,8 @@ test "it lists friend requests" do
{:ok, _activity} = ActivityPub.follow(other_user, user)
- user = Repo.get(User, user.id)
- other_user = Repo.get(User, other_user.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
@@ -1779,8 +1823,8 @@ test "it approves a friend request" do
{:ok, _activity} = ActivityPub.follow(other_user, user)
- user = Repo.get(User, user.id)
- other_user = Repo.get(User, other_user.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
@@ -1802,8 +1846,8 @@ test "it denies a friend request" do
{:ok, _activity} = ActivityPub.follow(other_user, user)
- user = Repo.get(User, user.id)
- other_user = Repo.get(User, other_user.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
@@ -1872,7 +1916,7 @@ test "it performs the upload and sets `data[actor]` with AP id of uploader user"
describe "POST /api/media/metadata/create" do
setup do
object = insert(:note)
- user = User.get_by_ap_id(object.data["actor"])
+ user = User.get_cached_by_ap_id(object.data["actor"])
%{object: object, user: user}
end
@@ -1955,7 +1999,7 @@ test "with credentials", %{conn: conn, user: user} do
user = refresh_record(user)
assert json_response(response, 200) ==
- ActivityRepresenter.to_map(activity, %{user: user, for: user})
+ ActivityView.render("activity.json", %{user: user, for: user, activity: activity})
end
end
@@ -1985,7 +2029,7 @@ test "with credentials", %{conn: conn, user: user} do
user = refresh_record(user)
assert json_response(response, 200) ==
- ActivityRepresenter.to_map(activity, %{user: user, for: user})
+ ActivityView.render("activity.json", %{user: user, for: user, activity: activity})
end
end
diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs
index c8dd3fd7a..d601c8f1f 100644
--- a/test/web/twitter_api/twitter_api_test.exs
+++ b/test/web/twitter_api/twitter_api_test.exs
@@ -16,6 +16,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
import Pleroma.Factory
+ setup_all do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
test "create a status" do
user = insert(:user)
mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"})
@@ -36,18 +41,19 @@ test "create a status" do
input = %{
"status" =>
- "Hello again, @shp.\nThis is on another :moominmamma: line. #2hu #epic #phantasmagoric",
+ "Hello again, @shp.\nThis is on another :firefox: line. #2hu #epic #phantasmagoric",
"media_ids" => [object.id]
}
{:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)
+ object = Object.normalize(activity.data["object"])
expected_text =
- "Hello again, @shp .<script></script> This is on another :moominmamma: line. #2hu #epic #phantasmagoric image.jpg "
+ "Hello again, @shp .<script></script> This is on another :firefox: line. #2hu #epic #phantasmagoric image.jpg "
- assert get_in(activity.data, ["object", "content"]) == expected_text
- assert get_in(activity.data, ["object", "type"]) == "Note"
- assert get_in(activity.data, ["object", "actor"]) == user.ap_id
+ assert get_in(object.data, ["content"]) == expected_text
+ assert get_in(object.data, ["type"]) == "Note"
+ assert get_in(object.data, ["actor"]) == user.ap_id
assert get_in(activity.data, ["actor"]) == user.ap_id
assert Enum.member?(get_in(activity.data, ["cc"]), User.ap_followers(user))
@@ -59,21 +65,20 @@ test "create a status" do
assert Enum.member?(get_in(activity.data, ["to"]), "shp")
assert activity.local == true
- assert %{"moominmamma" => "http://localhost:4001/finmoji/128px/moominmamma-128.png"} =
- activity.data["object"]["emoji"]
+ assert %{"firefox" => "http://localhost:4001/emoji/Firefox.gif"} = object.data["emoji"]
# hashtags
- assert activity.data["object"]["tag"] == ["2hu", "epic", "phantasmagoric"]
+ assert object.data["tag"] == ["2hu", "epic", "phantasmagoric"]
# Add a context
assert is_binary(get_in(activity.data, ["context"]))
- assert is_binary(get_in(activity.data, ["object", "context"]))
+ assert is_binary(get_in(object.data, ["context"]))
- assert is_list(activity.data["object"]["attachment"])
+ assert is_list(object.data["attachment"])
- assert activity.data["object"] == Object.get_by_ap_id(activity.data["object"]["id"]).data
+ assert activity.data["object"] == object.data["id"]
- user = User.get_by_ap_id(user.ap_id)
+ user = User.get_cached_by_ap_id(user.ap_id)
assert user.info.note_count == 1
end
@@ -86,6 +91,7 @@ test "create a status that is a reply" do
}
{:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)
+ object = Object.normalize(activity.data["object"])
input = %{
"status" => "Here's your (you).",
@@ -93,14 +99,14 @@ test "create a status that is a reply" do
}
{:ok, reply = %Activity{}} = TwitterAPI.create_status(user, input)
+ reply_object = Object.normalize(reply.data["object"])
assert get_in(reply.data, ["context"]) == get_in(activity.data, ["context"])
- assert get_in(reply.data, ["object", "context"]) ==
- get_in(activity.data, ["object", "context"])
+ assert get_in(reply_object.data, ["context"]) == get_in(object.data, ["context"])
- assert get_in(reply.data, ["object", "inReplyTo"]) == get_in(activity.data, ["object", "id"])
- assert get_in(reply.data, ["object", "inReplyToStatusId"]) == activity.id
+ assert get_in(reply_object.data, ["inReplyTo"]) == get_in(activity.data, ["object"])
+ assert Activity.get_in_reply_to_activity(reply).id == activity.id
end
test "Follow another user using user_id" do
@@ -123,7 +129,7 @@ test "Follow another user using screen_name" do
assert User.ap_followers(followed) in user.following
- followed = User.get_by_ap_id(followed.ap_id)
+ followed = User.get_cached_by_ap_id(followed.ap_id)
assert followed.info.follower_count == 1
{:error, msg} = TwitterAPI.follow(user, %{"screen_name" => followed.nickname})
@@ -275,7 +281,7 @@ test "it registers a new user and returns the user." do
{:ok, user} = TwitterAPI.register_user(data)
- fetched_user = Repo.get_by(User, nickname: "lain")
+ fetched_user = User.get_cached_by_nickname("lain")
assert UserView.render("show.json", %{user: user}) ==
UserView.render("show.json", %{user: fetched_user})
@@ -293,13 +299,12 @@ test "it registers a new user with empty string in bio and returns the user." do
{:ok, user} = TwitterAPI.register_user(data)
- fetched_user = Repo.get_by(User, nickname: "lain")
+ fetched_user = User.get_cached_by_nickname("lain")
assert UserView.render("show.json", %{user: user}) ==
UserView.render("show.json", %{user: fetched_user})
end
- @moduletag skip: "needs 'account_activation_required: true' in config"
test "it sends confirmation email if :account_activation_required is specified in instance config" do
setting = Pleroma.Config.get([:instance, :account_activation_required])
@@ -321,7 +326,16 @@ test "it sends confirmation email if :account_activation_required is specified i
assert user.info.confirmation_pending
- Swoosh.TestAssertions.assert_email_sent(Pleroma.UserEmail.account_confirmation_email(user))
+ 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 registers a new user and parses mentions in the bio" do
@@ -353,68 +367,313 @@ test "it registers a new user and parses mentions in the bio" do
assert user2.bio == expected_text
end
- @moduletag skip: "needs 'registrations_open: false' in config"
- test "it registers a new user via invite token and returns the user." do
- {:ok, token} = UserInviteToken.create_token()
+ describe "register with one time token" do
+ setup do
+ setting = Pleroma.Config.get([:instance, :registrations_open])
- data = %{
- "nickname" => "vinny",
- "email" => "pasta@pizza.vs",
- "fullname" => "Vinny Vinesauce",
- "bio" => "streamer",
- "password" => "hiptofbees",
- "confirm" => "hiptofbees",
- "token" => token.token
- }
+ if setting do
+ Pleroma.Config.put([:instance, :registrations_open], false)
+ on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
+ end
- {:ok, user} = TwitterAPI.register_user(data)
+ :ok
+ end
- fetched_user = Repo.get_by(User, nickname: "vinny")
- token = Repo.get_by(UserInviteToken, token: token.token)
+ test "returns user on success" do
+ {:ok, invite} = UserInviteToken.create_invite()
- assert token.used == true
+ data = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees",
+ "token" => invite.token
+ }
- assert UserView.render("show.json", %{user: user}) ==
- UserView.render("show.json", %{user: fetched_user})
+ {:ok, user} = TwitterAPI.register_user(data)
+
+ fetched_user = User.get_cached_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ assert invite.used == true
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+ end
+
+ test "returns error on invalid token" do
+ data = %{
+ "nickname" => "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 = %{
+ "nickname" => "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
- @moduletag skip: "needs 'registrations_open: false' in config"
- test "it returns an error if invalid token submitted" do
- data = %{
- "nickname" => "GrimReaper",
- "email" => "death@reapers.afterlife",
- "fullname" => "Reaper Grim",
- "bio" => "Your time has come",
- "password" => "scythe",
- "confirm" => "scythe",
- "token" => "DudeLetMeInImAFairy"
- }
+ describe "registers with date limited token" do
+ setup do
+ setting = Pleroma.Config.get([:instance, :registrations_open])
- {:error, msg} = TwitterAPI.register_user(data)
+ if setting do
+ Pleroma.Config.put([:instance, :registrations_open], false)
+ on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
+ end
- assert msg == "Invalid token"
- refute Repo.get_by(User, nickname: "GrimReaper")
+ data = %{
+ "nickname" => "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)
+ fetched_user = User.get_cached_by_nickname("vinny")
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+ 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
- @moduletag skip: "needs 'registrations_open: false' in config"
- test "it returns an error if expired token submitted" do
- {:ok, token} = UserInviteToken.create_token()
- UserInviteToken.mark_as_used(token.token)
+ describe "registers with reusable token" do
+ setup do
+ setting = Pleroma.Config.get([:instance, :registrations_open])
- data = %{
- "nickname" => "GrimReaper",
- "email" => "death@reapers.afterlife",
- "fullname" => "Reaper Grim",
- "bio" => "Your time has come",
- "password" => "scythe",
- "confirm" => "scythe",
- "token" => token.token
- }
+ if setting do
+ Pleroma.Config.put([:instance, :registrations_open], false)
+ on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
+ end
- {:error, msg} = TwitterAPI.register_user(data)
+ :ok
+ end
- assert msg == "Expired token"
- refute Repo.get_by(User, nickname: "GrimReaper")
+ 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 = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees",
+ "token" => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ fetched_user = User.get_cached_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ assert invite.used == true
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+
+ data = %{
+ "nickname" => "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
+ setting = Pleroma.Config.get([:instance, :registrations_open])
+
+ if setting do
+ Pleroma.Config.put([:instance, :registrations_open], false)
+ on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
+ end
+
+ :ok
+ end
+
+ test "returns user on success" do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
+
+ data = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees",
+ "token" => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ fetched_user = User.get_cached_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ refute invite.used
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+ 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 = %{
+ "nickname" => "vinny",
+ "email" => "pasta@pizza.vs",
+ "fullname" => "Vinny Vinesauce",
+ "bio" => "streamer",
+ "password" => "hiptofbees",
+ "confirm" => "hiptofbees",
+ "token" => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ fetched_user = User.get_cached_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+ assert invite.used == true
+
+ assert UserView.render("show.json", %{user: user}) ==
+ UserView.render("show.json", %{user: fetched_user})
+
+ data = %{
+ "nickname" => "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 = %{
+ "nickname" => "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 = %{
+ "nickname" => "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
@@ -429,7 +688,7 @@ test "it returns the error on registration problems" do
{:error, error_object} = TwitterAPI.register_user(data)
assert is_binary(error_object[:error])
- refute Repo.get_by(User, nickname: "lain")
+ refute User.get_cached_by_nickname("lain")
end
test "it assigns an integer conversation_id" do
@@ -445,28 +704,12 @@ test "it assigns an integer conversation_id" do
:ok
end
- describe "context_to_conversation_id" do
- test "creates a mapping object" do
- conversation_id = TwitterAPI.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 = TwitterAPI.context_to_conversation_id("random context")
-
- assert conversation_id == object.id
- end
- end
-
describe "fetching a user by uri" do
test "fetches a user by uri" do
id = "https://mastodon.social/users/lambadalambda"
user = insert(:user)
{:ok, represented} = TwitterAPI.get_external_profile(user, id)
- remote = User.get_by_ap_id(id)
+ remote = User.get_cached_by_ap_id(id)
assert represented["id"] == UserView.render("show.json", %{user: remote, for: user})["id"]
diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs
index fc762ab18..56474447b 100644
--- a/test/web/twitter_api/util_controller_test.exs
+++ b/test/web/twitter_api/util_controller_test.exs
@@ -1,8 +1,17 @@
defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
use Pleroma.Web.ConnCase
+ alias Pleroma.Notification
+ alias Pleroma.Repo
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
import Pleroma.Factory
+ setup do
+ Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
describe "POST /api/pleroma/follow_import" do
test "it returns HTTP 200", %{conn: conn} do
user1 = insert(:user)
@@ -17,6 +26,21 @@ test "it returns HTTP 200", %{conn: conn} do
assert response == "job started"
end
+ test "it imports new-style mastodon follow lists", %{conn: conn} do
+ user1 = insert(:user)
+ user2 = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user1)
+ |> 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' permission", %{conn: conn} do
token1 = insert(:oauth_token, scopes: ["read", "write"])
token2 = insert(:oauth_token, scopes: ["follow"])
@@ -52,7 +76,69 @@ test "it returns HTTP 200", %{conn: conn} do
end
end
+ describe "POST /api/pleroma/notifications/read" do
+ test "it marks a single notification as read", %{conn: conn} do
+ user1 = insert(:user)
+ 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)
+
+ conn
+ |> assign(:user, user1)
+ |> post("/api/pleroma/notifications/read", %{"id" => "#{notification1.id}"})
+ |> json_response(:ok)
+
+ assert Repo.get(Notification, notification1.id).seen
+ refute Repo.get(Notification, notification2.id).seen
+ end
+ end
+
+ describe "PUT /api/pleroma/notification_settings" do
+ test "it updates notification settings", %{conn: conn} do
+ user = insert(:user)
+
+ conn
+ |> assign(:user, user)
+ |> put("/api/pleroma/notification_settings", %{
+ "remote" => false,
+ "followers" => false,
+ "bar" => 1
+ })
+ |> json_response(:ok)
+
+ user = Repo.get(User, user.id)
+
+ assert %{"remote" => false, "local" => true, "followers" => false, "follows" => true} ==
+ user.info.notification_settings
+ end
+ end
+
describe "GET /api/statusnet/config.json" do
+ test "returns the state of safe_dm_mentions flag", %{conn: conn} do
+ option = Pleroma.Config.get([:instance, :safe_dm_mentions])
+ Pleroma.Config.put([:instance, :safe_dm_mentions], true)
+
+ response =
+ conn
+ |> get("/api/statusnet/config.json")
+ |> json_response(:ok)
+
+ assert response["site"]["safeDMMentionsEnabled"] == "1"
+
+ Pleroma.Config.put([:instance, :safe_dm_mentions], false)
+
+ response =
+ conn
+ |> get("/api/statusnet/config.json")
+ |> json_response(:ok)
+
+ assert response["site"]["safeDMMentionsEnabled"] == "0"
+
+ Pleroma.Config.put([:instance, :safe_dm_mentions], option)
+ end
+
test "it returns the managed config", %{conn: conn} do
Pleroma.Config.put([:instance, :managed_config], false)
Pleroma.Config.put([:fe], theme: "rei-ayanami-towel")
@@ -119,4 +205,50 @@ test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} d
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 /ostatus_subscribe?acct=...." do
+ test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do
+ conn =
+ get(
+ conn,
+ "/ostatus_subscribe?acct=https://mastodon.social/users/emelie/statuses/101849165031453009"
+ )
+
+ assert redirected_to(conn) =~ "/notice/"
+ end
+
+ test "show follow account page if the `acct` is a account link", %{conn: conn} do
+ response =
+ get(
+ conn,
+ "/ostatus_subscribe?acct=https://mastodon.social/users/emelie"
+ )
+
+ assert html_response(response, 200) =~ "Log in to follow"
+ end
+ end
+
+ test "GET /api/pleroma/healthcheck", %{conn: conn} do
+ conn = get(conn, "/api/pleroma/healthcheck")
+
+ assert conn.status in [200, 503]
+ end
end
diff --git a/test/web/twitter_api/views/activity_view_test.exs b/test/web/twitter_api/views/activity_view_test.exs
index 6f0786b1c..d84ab7420 100644
--- a/test/web/twitter_api/views/activity_view_test.exs
+++ b/test/web/twitter_api/views/activity_view_test.exs
@@ -6,13 +6,13 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do
use Pleroma.DataCase
alias Pleroma.Activity
+ alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.TwitterAPI.ActivityView
- alias Pleroma.Web.TwitterAPI.TwitterAPI
alias Pleroma.Web.TwitterAPI.UserView
import Pleroma.Factory
@@ -82,7 +82,7 @@ test "a create activity with a html status" do
result = ActivityView.render("activity.json", activity: activity)
assert result["statusnet_html"] ==
- "#Bike log - Commute Tuesdayhttps://pla.bike/posts/20181211/ #cycling #CHScycling #commute MVIMG_20181211_054020.jpg"
+ "#Bike log - Commute Tuesdayhttps://pla.bike/posts/20181211/ #cycling #CHScycling #commute MVIMG_20181211_054020.jpg"
assert result["text"] ==
"#Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg"
@@ -91,16 +91,16 @@ test "a create activity with a html status" do
test "a create activity with a summary containing emoji" do
{:ok, activity} =
CommonAPI.post(insert(:user), %{
- "spoiler_text" => ":woollysocks: meow",
+ "spoiler_text" => ":firefox: meow",
"status" => "."
})
result = ActivityView.render("activity.json", activity: activity)
- expected = ":woollysocks: meow"
+ expected = ":firefox: meow"
expected_html =
- " meow"
+ " meow"
assert result["summary"] == expected
assert result["summary_html"] == expected_html
@@ -126,10 +126,11 @@ test "a create activity with a note" do
other_user = insert(:user, %{nickname: "shp"})
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
+ object = Object.normalize(activity.data["object"])
result = ActivityView.render("activity.json", activity: activity)
- convo_id = TwitterAPI.context_to_conversation_id(activity.data["object"]["context"])
+ convo_id = Utils.context_to_conversation_id(object.data["context"])
expected = %{
"activity_type" => "post",
@@ -137,8 +138,8 @@ test "a create activity with a note" do
"attentions" => [
UserView.render("show.json", %{user: other_user})
],
- "created_at" => activity.data["object"]["published"] |> Utils.date_to_asctime(),
- "external_url" => activity.data["object"]["id"],
+ "created_at" => object.data["published"] |> Utils.date_to_asctime(),
+ "external_url" => object.data["id"],
"fave_num" => 0,
"favorited" => false,
"id" => activity.id,
@@ -162,7 +163,7 @@ test "a create activity with a note" do
}\">@shp !",
"tags" => [],
"text" => "Hey @shp!",
- "uri" => activity.data["object"]["id"],
+ "uri" => object.data["id"],
"user" => UserView.render("show.json", %{user: user}),
"visibility" => "direct",
"card" => nil,
@@ -176,13 +177,14 @@ test "a list of activities" do
user = insert(:user)
other_user = insert(:user, %{nickname: "shp"})
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
+ object = Object.normalize(activity.data["object"])
- convo_id = TwitterAPI.context_to_conversation_id(activity.data["object"]["context"])
+ convo_id = Utils.context_to_conversation_id(object.data["context"])
mocks = [
{
- TwitterAPI,
- [],
+ Utils,
+ [:passthrough],
[context_to_conversation_id: fn _ -> false end]
},
{
@@ -197,7 +199,7 @@ test "a list of activities" do
assert result["statusnet_conversation_id"] == convo_id
assert result["user"]
- refute called(TwitterAPI.context_to_conversation_id(:_))
+ refute called(Utils.context_to_conversation_id(:_))
refute called(User.get_cached_by_ap_id(user.ap_id))
refute called(User.get_cached_by_ap_id(other_user.ap_id))
end
@@ -278,11 +280,11 @@ test "an announce activity" do
other_user = insert(:user, %{nickname: "shp"})
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
- {:ok, announce, _object} = CommonAPI.repeat(activity.id, other_user)
+ {:ok, announce, object} = CommonAPI.repeat(activity.id, other_user)
- convo_id = TwitterAPI.context_to_conversation_id(activity.data["object"]["context"])
+ convo_id = Utils.context_to_conversation_id(object.data["context"])
- activity = Repo.get(Activity, activity.id)
+ activity = Activity.get_by_id(activity.id)
result = ActivityView.render("activity.json", activity: announce)
@@ -358,7 +360,7 @@ test "a delete activity" do
test "a peertube video" do
{:ok, object} =
- ActivityPub.fetch_object_from_id(
+ Pleroma.Object.Fetcher.fetch_object_from_id(
"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
)
diff --git a/test/web/twitter_api/views/user_view_test.exs b/test/web/twitter_api/views/user_view_test.exs
index 4e7f94795..c99dbddeb 100644
--- a/test/web/twitter_api/views/user_view_test.exs
+++ b/test/web/twitter_api/views/user_view_test.exs
@@ -89,29 +89,34 @@ test "A user" do
"following" => false,
"follows_you" => false,
"statusnet_blocking" => false,
- "rights" => %{
- "delete_others_notice" => false,
- "admin" => false
- },
"statusnet_profile_url" => user.ap_id,
"cover_photo" => banner,
"background_image" => nil,
"is_local" => true,
"locked" => false,
- "default_scope" => "public",
- "no_rich_text" => false,
"hide_follows" => false,
"hide_followers" => false,
"fields" => [],
"pleroma" => %{
"confirmation_pending" => false,
"tags" => []
- }
+ },
+ "rights" => %{"admin" => false, "delete_others_notice" => false},
+ "role" => "member"
}
assert represented == UserView.render("show.json", %{user: user})
end
+ test "User exposes settings for themselves and only for themselves", %{user: user} do
+ as_user = UserView.render("show.json", %{user: user, for: user})
+ assert as_user["default_scope"] == user.info.default_scope
+ assert as_user["no_rich_text"] == user.info.no_rich_text
+ as_stranger = UserView.render("show.json", %{user: user})
+ refute as_stranger["default_scope"]
+ refute as_stranger["no_rich_text"]
+ end
+
test "A user for a given other follower", %{user: user} do
follower = insert(:user, %{following: [User.ap_followers(user)]})
{:ok, user} = User.update_follower_count(user)
@@ -137,24 +142,20 @@ test "A user for a given other follower", %{user: user} do
"following" => true,
"follows_you" => false,
"statusnet_blocking" => false,
- "rights" => %{
- "delete_others_notice" => false,
- "admin" => false
- },
"statusnet_profile_url" => user.ap_id,
"cover_photo" => banner,
"background_image" => nil,
"is_local" => true,
"locked" => false,
- "default_scope" => "public",
- "no_rich_text" => false,
"hide_follows" => false,
"hide_followers" => false,
"fields" => [],
"pleroma" => %{
"confirmation_pending" => false,
"tags" => []
- }
+ },
+ "rights" => %{"admin" => false, "delete_others_notice" => false},
+ "role" => "member"
}
assert represented == UserView.render("show.json", %{user: user, for: follower})
@@ -186,24 +187,20 @@ test "A user that follows you", %{user: user} do
"following" => false,
"follows_you" => true,
"statusnet_blocking" => false,
- "rights" => %{
- "delete_others_notice" => false,
- "admin" => false
- },
"statusnet_profile_url" => follower.ap_id,
"cover_photo" => banner,
"background_image" => nil,
"is_local" => true,
"locked" => false,
- "default_scope" => "public",
- "no_rich_text" => false,
"hide_follows" => false,
"hide_followers" => false,
"fields" => [],
"pleroma" => %{
"confirmation_pending" => false,
"tags" => []
- }
+ },
+ "rights" => %{"admin" => false, "delete_others_notice" => false},
+ "role" => "member"
}
assert represented == UserView.render("show.json", %{user: follower, for: user})
@@ -272,27 +269,23 @@ test "A blocked user for the blocker" do
"following" => false,
"follows_you" => false,
"statusnet_blocking" => true,
- "rights" => %{
- "delete_others_notice" => false,
- "admin" => false
- },
"statusnet_profile_url" => user.ap_id,
"cover_photo" => banner,
"background_image" => nil,
"is_local" => true,
"locked" => false,
- "default_scope" => "public",
- "no_rich_text" => false,
"hide_follows" => false,
"hide_followers" => false,
"fields" => [],
"pleroma" => %{
"confirmation_pending" => false,
"tags" => []
- }
+ },
+ "rights" => %{"admin" => false, "delete_others_notice" => false},
+ "role" => "member"
}
- blocker = Repo.get(User, blocker.id)
+ blocker = User.get_cached_by_id(blocker.id)
assert represented == UserView.render("show.json", %{user: user, for: blocker})
end
diff --git a/uploads/.gitignore b/uploads/.gitignore
new file mode 100644
index 000000000..523e584a7
--- /dev/null
+++ b/uploads/.gitignore
@@ -0,0 +1,3 @@
+# Git will ignore everything in this directory except this file.
+*
+!.gitignore