diff options
Diffstat (limited to 'test')
61 files changed, 5556 insertions, 1108 deletions
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 @@ +<?xml version="1.0"?> +<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0"> + <id>https://mastodon.social/users/emelie.atom</id> + <title>emelie 🎨</title> + <subtitle>23 / #Sweden / #Artist / #Equestrian / #GameDev + +If I ain't spending time with my pets, I'm probably drawing. 🐴 🐱 🐰</subtitle> + <updated>2019-02-04T20:22:19Z</updated> + <logo>https://files.mastodon.social/accounts/avatars/000/015/657/original/e7163f98280da1a4.png</logo> + <author> + <id>https://mastodon.social/users/emelie</id> + <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type> + <uri>https://mastodon.social/users/emelie</uri> + <name>emelie</name> + <email>emelie@mastodon.social</email> + <summary type="html"><p>23 / <a href="https://mastodon.social/tags/sweden" class="mention hashtag" rel="tag">#<span>Sweden</span></a> / <a href="https://mastodon.social/tags/artist" class="mention hashtag" rel="tag">#<span>Artist</span></a> / <a href="https://mastodon.social/tags/equestrian" class="mention hashtag" rel="tag">#<span>Equestrian</span></a> / <a href="https://mastodon.social/tags/gamedev" class="mention hashtag" rel="tag">#<span>GameDev</span></a></p><p>If I ain&apos;t spending time with my pets, I&apos;m probably drawing. 🐴 🐱 🐰</p></summary> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie"/> + <link rel="avatar" type="image/png" media:width="120" media:height="120" href="https://files.mastodon.social/accounts/avatars/000/015/657/original/e7163f98280da1a4.png"/> + <link rel="header" type="image/png" media:width="700" media:height="335" href="https://files.mastodon.social/accounts/headers/000/015/657/original/847f331f3dd9e38b.png"/> + <poco:preferredUsername>emelie</poco:preferredUsername> + <poco:displayName>emelie 🎨</poco:displayName> + <poco:note>23 / #Sweden / #Artist / #Equestrian / #GameDev + +If I ain't spending time with my pets, I'm probably drawing. 🐴 🐱 🐰</poco:note> + <mastodon:scope>public</mastodon:scope> + </author> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie.atom"/> + <link rel="hub" href="https://mastodon.social/api/push"/> + <link rel="salmon" href="https://mastodon.social/api/salmon/15657"/> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101850331907006641</id> + <published>2019-04-01T09:58:50Z</published> + <updated>2019-04-01T09:58:50Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101850331907006641"/> + <content type="html" xml:lang="en"><p>Me: I&apos;m going to make this vital change to my world building in the morning, no way I&apos;ll forget this, it&apos;s too big of a deal<br />Also me: forgets</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101850331907006641"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17854598.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94383214:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101849626603073336</id> + <published>2019-04-01T06:59:28Z</published> + <updated>2019-04-01T06:59:28Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849626603073336"/> + <content type="html" xml:lang="sv"><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></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="https://mastodon.social/users/Fergant"/> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849626603073336"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17852590.atom"/> + <thr:in-reply-to ref="https://mastodon.social/users/Fergant/statuses/101849606513357387" href="https://mastodon.social/@Fergant/101849606513357387"/> + <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94362529:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101849580030237068</id> + <published>2019-04-01T06:47:37Z</published> + <updated>2019-04-01T06:47:37Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849580030237068"/> + <content type="html" xml:lang="en"><p>What&apos;s you people&apos;s favourite fantasy books? Give me some hot tips 🌞</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849580030237068"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17852464.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94362529:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101849550599949363</id> + <published>2019-04-01T06:40:08Z</published> + <updated>2019-04-01T06:40:08Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849550599949363"/> + <content type="html" xml:lang="en"><p>Stick them legs out 💃 <a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag">#<span>mastocats</span></a></p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <category term="mastocats"/> + <link rel="enclosure" type="image/jpeg" length="516384" href="https://files.mastodon.social/media_attachments/files/013/051/707/original/125a310abe9a34aa.jpeg"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849550599949363"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17852407.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94361580:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101849191533152720</id> + <published>2019-04-01T05:08:49Z</published> + <updated>2019-04-01T05:08:49Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849191533152720"/> + <content type="html" xml:lang="en"><p>long 🐱 <a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag">#<span>mastocats</span></a></p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <category term="mastocats"/> + <link rel="enclosure" type="image/jpeg" length="305208" href="https://files.mastodon.social/media_attachments/files/013/049/940/original/f2dbbfe7de3a17d2.jpeg"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849191533152720"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17851663.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94351141:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101849165031453009</id> + <published>2019-04-01T05:02:05Z</published> + <updated>2019-04-01T05:02:05Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849165031453009"/> + <content type="html" xml:lang="en"><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></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <category term="mastocats"/> + <link rel="enclosure" type="video/mp4" length="9838915" href="https://files.mastodon.social/media_attachments/files/013/049/816/original/e7831178a5e0d6d4.mp4"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849165031453009"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17851558.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94350309:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101846512530748693</id> + <published>2019-03-31T17:47:31Z</published> + <updated>2019-03-31T17:47:31Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101846512530748693"/> + <content type="html" xml:lang="en"><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></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <category term="equestrian"/> + <category term="mastohorses"/> + <link rel="enclosure" type="image/jpeg" length="461632" href="https://files.mastodon.social/media_attachments/files/013/033/387/original/301e8ab668cd61d2.jpeg"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101846512530748693"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17842424.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-31:objectId=94256415:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101846181093805500</id> + <published>2019-03-31T16:23:14Z</published> + <updated>2019-03-31T16:23:14Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101846181093805500"/> + <content type="html" xml:lang="en"><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></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <category term="mastocats"/> + <link rel="enclosure" type="image/jpeg" length="211384" href="https://files.mastodon.social/media_attachments/files/013/030/725/original/5b4886730cbbd25c.jpeg"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101846181093805500"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17841108.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-31:objectId=94245239:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101845897513133849</id> + <published>2019-03-31T15:11:07Z</published> + <updated>2019-03-31T15:11:07Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101845897513133849"/> + <summary xml:lang="en">more earthsea ramblings</summary> + <content type="html" xml:lang="en"><p>I&apos;m re-watching Tales from Earthsea for the first time since I read the books, and that Therru doesn&apos;t squash Cob like a spider, as Orm Embar did is a wasted opportunity tbh</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101845897513133849"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17840088.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-31:objectId=94232455:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101841219051533307</id> + <published>2019-03-30T19:21:19Z</published> + <updated>2019-03-30T19:21:19Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101841219051533307"/> + <content type="html" xml:lang="en"><p>I gave my cats some mackerel and they ate it all in 0.3 seconds, and now they won&apos;t stop meowing for more, and I&apos;m tired plz shut up</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101841219051533307"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17826587.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-30:objectId=94075000:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101839949762341381</id> + <published>2019-03-30T13:58:31Z</published> + <updated>2019-03-30T13:58:31Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101839949762341381"/> + <content type="html" xml:lang="en"><p>yet I&apos;m confused about this american dude with a gun, like the heck r ya doin in mah ghibli</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101839949762341381"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17821757.atom"/> + <thr:in-reply-to ref="https://mastodon.social/users/emelie/statuses/101839928677863590" href="https://mastodon.social/@emelie/101839928677863590"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-30:objectId=94026360:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101839928677863590</id> + <published>2019-03-30T13:53:09Z</published> + <updated>2019-03-30T13:53:09Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101839928677863590"/> + <content type="html" xml:lang="en"><p>2 hours into Ni no Kuni 2 and I&apos;ve already sold my soul to this game</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101839928677863590"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17821713.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-30:objectId=94026360:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101836329521599438</id> + <published>2019-03-29T22:37:51Z</published> + <updated>2019-03-29T22:37:51Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101836329521599438"/> + <content type="html" xml:lang="en"><p>Pippi Longstocking the original one-punch /man</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101836329521599438"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17811608.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-29:objectId=93907854:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101835905282948341</id> + <published>2019-03-29T20:49:57Z</published> + <updated>2019-03-29T20:49:57Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101835905282948341"/> + <content type="html" xml:lang="en"><p>I&apos;ve had so much wine I thought I had a 3rd brother</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101835905282948341"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17809862.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-29:objectId=93892966:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101835878059204660</id> + <published>2019-03-29T20:43:02Z</published> + <updated>2019-03-29T20:43:02Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101835878059204660"/> + <content type="html" xml:lang="en"><p>ååååhhh booi</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101835878059204660"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17809734.atom"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-29:objectId=93892010:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101835848050598939</id> + <published>2019-03-29T20:35:24Z</published> + <updated>2019-03-29T20:35:24Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101835848050598939"/> + <content type="html" xml:lang="en"><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></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="https://thraeryn.net/users/thraeryn"/> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101835848050598939"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17809591.atom"/> + <thr:in-reply-to ref="https://thraeryn.net/users/thraeryn/statuses/101835839202826007" href="https://thraeryn.net/@thraeryn/101835839202826007"/> + <ostatus:conversation ref="tag:mastodon.social,2019-03-29:objectId=93888827:objectType=Conversation"/> + </entry> + <entry> + <id>https://mastodon.social/users/emelie/statuses/101835823138262290</id> + <published>2019-03-29T20:29:04Z</published> + <updated>2019-03-29T20:29:04Z</updated> + <title>New status by emelie</title> + <activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type> + <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> + <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101835823138262290"/> + <summary xml:lang="en">medical, fluids mention</summary> + <content type="html" xml:lang="en"><p><span class="h-card"><a href="https://icosahedron.website/@Trev" class="u-url mention">@<span>Trev</span></a></span> *hugs* ✨</p></content> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="https://icosahedron.website/users/Trev"/> + <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/> + <mastodon:scope>public</mastodon:scope> + <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101835823138262290"/> + <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17809468.atom"/> + <thr:in-reply-to ref="https://icosahedron.website/users/Trev/statuses/101835812250051801" href="https://icosahedron.website/@Trev/101835812250051801"/> + <ostatus:conversation ref="tag:icosahedron.website,2019-03-29:objectId=12220882:objectType=Conversation"/> + </entry> +</feed> 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": "<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>", + "contentMap": { + "en": "<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>" + }, + "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 Binary files differindex 09834bb5c..edff6246b 100644 --- a/test/fixtures/image.jpg +++ b/test/fixtures/image.jpg 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 @@ defmodule Pleroma.FormatterTest do 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 == + "<span class='h-card'><a data-user='#{user.id}' class='u-url mention' href='#{ + user.ap_id + }'>@<span>#{user.nickname}</span></a></span> hey dude i hate <span class='h-card'><a data-user='#{ + third_user.id + }' class='u-url mention' href='#{third_user.ap_id}'>@<span>#{third_user.nickname}</span></a></span>" + 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 @@ defmodule Pleroma.FormatterTest do end test "it adds cool emoji" do - text = "I love :moominmamma:" + text = "I love :firefox:" expected_result = - "I love <img height=\"32px\" width=\"32px\" alt=\"moominmamma\" title=\"moominmamma\" src=\"/finmoji/128px/moominmamma-128.png\" />" + "I love <img height=\"32px\" width=\"32px\" alt=\"firefox\" title=\"firefox\" src=\"/emoji/Firefox.gif\" />" assert Formatter.emojify(text) == expected_result end @@ -244,9 +269,11 @@ defmodule Pleroma.FormatterTest 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 <b>this is in bold</b> <p>this is a paragraph</p> this is a linebreak<br /> + this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a> + this is a link with not allowed "rel" attribute: <a href="http://example.com/" rel="tag noallowed">example.com</a> this is an image: <img src="http://example.com/image.jpg"><br /> <script>alert('hacked')</script> """ @@ -18,12 +20,26 @@ defmodule Pleroma.HTMLTest do <img src="http://example.com/image.jpg" onerror="alert('hacked')"> """ + @html_span_class_sample """ + <span class="animate-spin">hi</span> + """ + + @html_span_microformats_sample """ + <span class="h-card"><a class="u-url mention">@<span>foo</span></a></span> + """ + + @html_span_invalid_microformats_sample """ + <span class="h-card"><a class="u-url mention animate-spin">@<span>foo</span></a></span> + """ + 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 @@ defmodule Pleroma.HTMLTest do this is in bold <p>this is a paragraph</p> this is a linebreak<br /> + this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a> + this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a> this is an image: <img src="http://example.com/image.jpg" /><br /> alert('hacked') """ @@ -58,6 +76,36 @@ defmodule Pleroma.HTMLTest do assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.TwitterText) end + + test "does not allow spans with invalid classes" do + expected = """ + <span>hi</span> + """ + + assert expected == + HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.TwitterText) + end + + test "does allow microformats" do + expected = """ + <span class="h-card"><a class="u-url mention">@<span>foo</span></a></span> + """ + + assert expected == + HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.TwitterText) + end + + test "filters invalid microformats markup" do + expected = """ + <span class="h-card"><a>@<span>foo</span></a></span> + """ + + assert expected == + HTML.filter_tags( + @html_span_invalid_microformats_sample, + Pleroma.HTML.Scrubber.TwitterText + ) + end end describe "default scrubber" do @@ -66,6 +114,8 @@ defmodule Pleroma.HTMLTest do <b>this is in bold</b> <p>this is a paragraph</p> this is a linebreak<br /> + this is a link with allowed "rel" attribute: <a href="http://example.com/" rel="tag">example.com</a> + this is a link with not allowed "rel" attribute: <a href="http://example.com/">example.com</a> this is an image: <img src="http://example.com/image.jpg" /><br /> alert('hacked') """ @@ -80,5 +130,34 @@ defmodule Pleroma.HTMLTest do assert expected == HTML.filter_tags(@html_onerror_sample, Pleroma.HTML.Scrubber.Default) end + + test "does not allow spans with invalid classes" do + expected = """ + <span>hi</span> + """ + + assert expected == HTML.filter_tags(@html_span_class_sample, Pleroma.HTML.Scrubber.Default) + end + + test "does allow microformats" do + expected = """ + <span class="h-card"><a class="u-url mention">@<span>foo</span></a></span> + """ + + assert expected == + HTML.filter_tags(@html_span_microformats_sample, Pleroma.HTML.Scrubber.Default) + end + + test "filters invalid microformats markup" do + expected = """ + <span class="h-card"><a>@<span>foo</span></a></span> + """ + + 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 <https://pleroma.social/> -# 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 @@ defmodule Pleroma.MediaProxyTest 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 @@ defmodule Pleroma.NotificationTest 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 @@ defmodule Pleroma.NotificationTest 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 @@ defmodule Pleroma.ObjectTest 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 @@ defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do |> 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 <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.UploadedMediaPlugTest do + use Pleroma.Web.ConnCase + alias Pleroma.Upload + + defp upload_file(context) do + Pleroma.DataCase.ensure_local_uploader(context) + File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg") + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image_tmp.jpg"), + filename: "nice_tf.jpg" + } + + {:ok, data} = Upload.store(file) + [%{"href" => attachment_url} | _] = data["url"] + [attachment_url: attachment_url] + end + + setup_all :upload_file + + test "does not send Content-Disposition header when name param is not set", %{ + attachment_url: attachment_url + } do + conn = get(build_conn(), attachment_url) + refute Enum.any?(conn.resp_headers, &(elem(&1, 0) == "content-disposition")) + end + + test "sends Content-Disposition header when name param is set", %{ + attachment_url: attachment_url + } do + conn = get(build_conn(), attachment_url <> "?name=\"cofe\".gif") + + assert Enum.any?( + conn.resp_headers, + &(&1 == {"content-disposition", "filename=\"\\\"cofe\\\".gif\""}) + ) + end +end diff --git a/test/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 <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.RegistrationTest do + use Pleroma.DataCase + + import Pleroma.Factory + + alias Pleroma.Registration + alias Pleroma.Repo + + describe "generic changeset" do + test "requires :provider, :uid" do + registration = build(:registration, provider: nil, uid: nil) + + cs = Registration.changeset(registration, %{}) + refute cs.valid? + + assert [ + provider: {"can't be blank", [validation: :required]}, + uid: {"can't be blank", [validation: :required]} + ] == cs.errors + end + + test "ensures uniqueness of [:provider, :uid]" do + registration = insert(:registration) + registration2 = build(:registration, provider: registration.provider, uid: registration.uid) + + cs = Registration.changeset(registration2, %{}) + assert cs.valid? + + assert {:error, + %Ecto.Changeset{ + errors: [ + uid: + {"has already been taken", + [constraint: :unique, constraint_name: "registrations_provider_uid_index"]} + ] + }} = Repo.insert(cs) + + # Note: multiple :uid values per [:user_id, :provider] are intentionally allowed + cs2 = Registration.changeset(registration2, %{uid: "available.uid"}) + assert cs2.valid? + assert {:ok, _} = Repo.insert(cs2) + + cs3 = Registration.changeset(registration2, %{provider: "provider2"}) + assert cs3.valid? + assert {:ok, _} = Repo.insert(cs3) + end + + test "allows `nil` :user_id (user-unbound registration)" do + registration = build(:registration, user_id: nil) + cs = Registration.changeset(registration, %{}) + assert cs.valid? + assert {:ok, _} = Repo.insert(cs) + end + end +end diff --git a/test/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 <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ScheduledActivityTest do + use Pleroma.DataCase + alias Pleroma.DataCase + alias Pleroma.ScheduledActivity + import Pleroma.Factory + + setup 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 <https://pleroma.social/> +# 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 @@ defmodule Pleroma.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 @@ defmodule Pleroma.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 @@ defmodule Pleroma.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 @@ defmodule HttpRequestMock 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 @@ defmodule HttpRequestMock 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 <https://pleroma.social/> -# 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 @@ defmodule Mix.Tasks.Pleroma.RelayTest 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 @@ defmodule Mix.Tasks.Pleroma.RelayTest 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 @@ defmodule Mix.Tasks.Pleroma.RelayTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Mix.Tasks.Pleroma.UserTest 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 @@ defmodule Pleroma.UploadTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest do {: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 @@ defmodule Pleroma.UserTest do # 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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 @@ defmodule Pleroma.UserTest 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!" - }) - - id1 = activity1.data["object"]["id"] - - {:ok, activity2} = - CommonAPI.post(user, %{ - "status" => "heweoo!" - }) - - id2 = activity2.data["object"]["id"] + {:ok, follower} = Pleroma.User.follow(follower, user) + {:ok, _follower2} = Pleroma.User.follow(follower2, user) + {:ok, _follower3} = Pleroma.User.follow(follower3, user) - assert {:ok, user_state1} = User.bookmark(user, id1) - assert user_state1.bookmarks == [id1] + {:ok, _} = Pleroma.User.block(user, follower) - assert {:ok, user_state2} = User.unbookmark(user, id1) - assert user_state2.bookmarks == [] + user_show = Pleroma.Web.TwitterAPI.UserView.render("show.json", %{user: user}) - 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do |> 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do |> 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do ) |> 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do } {: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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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"] == "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>" end @@ -125,8 +128,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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"] == "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>" end @@ -142,24 +146,27 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do object = data["object"] - |> Map.put("id", activity.data["object"]["id"]) + |> Map.put("id", activity.data["object"]) data = data @@ -413,7 +487,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do object = data["object"] - |> Map.put("id", activity.data["object"]["id"]) + |> Map.put("id", activity.data["object"]) data = data @@ -433,7 +507,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest 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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do :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 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do 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 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest 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 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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,26 +575,54 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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, "nickname" => admin.nickname, "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 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest 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 <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.SearchTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.AdminAPI.Search + + import Pleroma.Factory + + describe "search for admin" do + test "it ignores case" do + insert(:user, nickname: "papercoach") + insert(:user, nickname: "CanadaPaperCoach") + + {:ok, _results, count} = + Search.user(%{ + query: "paper", + local: false, + page: 1, + page_size: 50 + }) + + assert count == 2 + end + + test "it returns local/external users" do + insert(:user, local: true) + insert(:user, local: false) + insert(:user, local: false) + + {:ok, _results, local_count} = + Search.user(%{ + query: "", + local: true + }) + + {:ok, _results, external_count} = + Search.user(%{ + query: "", + external: true + }) + + assert local_count == 1 + assert external_count == 2 + end + + test "it returns active/deactivated users" do + insert(:user, 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 @@ defmodule Pleroma.Web.CommonAPITest do "content_type" => "text/html" }) - content = activity.data["object"]["content"] - assert content == "<p><b>2hu</b></p>alert('xss')" + object = Object.normalize(activity.data["object"]) + + assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')" end test "it filters out obviously bad tags when accepting a post as Markdown" do @@ -61,8 +83,9 @@ defmodule Pleroma.Web.CommonAPITest do "content_type" => "text/markdown" }) - content = activity.data["object"]["content"] - assert content == "<p><b>2hu</b></p>alert('xss')" + object = Object.normalize(activity.data["object"]) + + assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')" end end @@ -221,4 +244,27 @@ defmodule Pleroma.Web.CommonAPITest 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 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest 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 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do assert output == expected end + test "works for bare text/bbcode" do + text = "[b]hello world[/b]" + expected = "<strong>hello world</strong>" + + {output, [], []} = Utils.format_input(text, "text/bbcode") + + assert output == expected + + text = "[b]hello world![/b]\n\nsecond paragraph!" + expected = "<strong>hello world!</strong><br>\n<br>\nsecond paragraph!" + + {output, [], []} = Utils.format_input(text, "text/bbcode") + + assert output == expected + + text = "[b]hello world![/b]\n\n<strong>second paragraph!</strong>" + + expected = + "<strong>hello world!</strong><br>\n<br>\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 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do assert user = json_response(conn, 200) assert user["note"] == - ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a data-user=") <> + ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe" rel="tag">#cofe</a> with <span class="h-card"><a data-user=") <> user2.id <> ~s(" class="u-url mention" href=") <> user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>) @@ -1650,6 +2214,78 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest 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 <https://pleroma.social/> +# 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 <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.ScheduledActivityViewTest do + use Pleroma.DataCase + alias Pleroma.ScheduledActivity + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.CommonAPI.Utils + alias Pleroma.Web.MastodonAPI.ScheduledActivityView + alias Pleroma.Web.MastodonAPI.StatusView + import Pleroma.Factory + + test "A scheduled activity with a media attachment" do + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "hi"}) + + scheduled_at = + NaiveDateTime.utc_now() + |> NaiveDateTime.add(:timer.minutes(10), :millisecond) + |> NaiveDateTime.to_iso8601() + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id) + + attrs = %{ + params: %{ + "media_ids" => [upload.id], + "status" => "hi", + "sensitive" => true, + "spoiler_text" => "spoiler", + "visibility" => "unlisted", + "in_reply_to_id" => to_string(activity.id) + }, + scheduled_at: scheduled_at + } + + {:ok, scheduled_activity} = ScheduledActivity.create(user, attrs) + result = ScheduledActivityView.render("show.json", %{scheduled_activity: scheduled_activity}) + + expected = %{ + id: to_string(scheduled_activity.id), + media_attachments: + %{"media_ids" => [upload.id]} + |> Utils.attachments_from_ids() + |> Enum.map(&StatusView.render("attachment.json", %{attachment: &1})), + params: %{ + in_reply_to_id: to_string(activity.id), + media_ids: [upload.id], + poll: nil, + scheduled_at: nil, + sensitive: true, + spoiler_text: "spoiler", + text: "hi", + visibility: "unlisted" + }, + scheduled_at: Utils.to_masto_date(scheduled_activity.scheduled_at) + } + + assert expected == result + end +end diff --git a/test/web/mastodon_api/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 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest 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 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest 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(<a href="https://some-link.com">https://some-link.com</a> <a rel="me" href="https://another-link.com">https://another-link.com</a> + <link href="http://some.com"> <link rel="me" href="http://some3.com>") + + user = insert(:user, %{bio: bio}) + + assert RelMe.build_tags(%{user: user}) == [ + {:link, [rel: "me", href: "http://some3.com>"], []}, + {:link, [rel: "me", href: "https://another-link.com"], []} + ] + end +end diff --git a/test/web/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 @@ defmodule Pleroma.Web.NodeInfoTest 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 <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do + use Pleroma.Web.ConnCase + alias Pleroma.Repo + alias Pleroma.Web.OAuth.Token + import Pleroma.Factory + import 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" + ] + + 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)) + + on_exit(fn -> + Pleroma.Config.put(oauth_consumer_strategies_path, oauth_consumer_strategies) + end) + + [ + app: insert(:oauth_app), + conn: + build_conn() + |> Plug.Session.call(Plug.Session.init(@session_opts)) + |> fetch_session() + ] + end - 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" - } - }) + 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" + } + ) + + assert response = html_response(conn, 200) + assert response =~ "Sign in with Twitter" + assert response =~ o_auth_path(conn, :prepare_request) + end - target = redirected_to(conn) - assert target =~ app.redirect_uris + 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" + } + } + ) + + assert response = html_response(conn, 302) + + redirect_query = URI.parse(redirected_to(conn)).query + assert %{"state" => state_param} = URI.decode_query(redirect_query) + assert {:ok, state_components} = Poison.decode(state_param) + + expected_client_id = app.client_id + expected_redirect_uri = app.redirect_uris + + assert %{ + "scope" => "read follow", + "client_id" => ^expected_client_id, + "redirect_uri" => ^expected_redirect_uri, + "state" => "a_state" + } = state_components + end - query = URI.parse(target).query |> URI.query_decoder() |> Map.new() + test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`", + %{app: app, conn: conn} do + registration = insert(:registration) - assert %{"state" => "statepassed", "code" => code} = query - auth = Repo.get_by(Authorization, token: code) - assert auth - assert auth.scopes == ["read", "write"] - end + state_params = %{ + "scope" => Enum.join(app.scopes, " "), + "client_id" => app.client_id, + "redirect_uri" => app.redirect_uris, + "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, 302) + assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/ + end + end - test "returns 401 for wrong credentials", %{conn: conn} do - user = insert(:user) - app = insert(:oauth_app) + test "with user-unbound registration, GET /oauth/<provider>/callback renders registration_details page", + %{app: app, conn: conn} do + registration = insert(:registration, user: nil) - 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) + 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 - # Keep the details - assert result =~ app.client_id - assert result =~ app.redirect_uris + test "on authentication error, GET /oauth/<provider>/callback redirects to `redirect_uri`", %{ + app: app, + conn: conn + } do + state_params = %{ + "scope" => Enum.join(app.scopes, " "), + "client_id" => app.client_id, + "redirect_uri" => 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 - # Error message - assert result =~ "Invalid Username/Password" - 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 "returns 401 for missing scopes", %{conn: conn} do - user = insert(:user) - app = insert(:oauth_app) + 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}) - result = - conn - |> post("/oauth/authorize", %{ + params = %{ + "op" => "register", "authorization" => %{ - "name" => user.nickname, - "password" => "test", + "scopes" => app.scopes, "client_id" => app.client_id, "redirect_uri" => app.redirect_uris, - "state" => "statepassed", - "scope" => "" + "state" => "a_state", + "nickname" => "availablenickname", + "email" => "available@email.com" } - }) - |> html_response(:unauthorized) + } - # Keep the details - assert result =~ app.client_id - assert result =~ app.redirect_uris + 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) + } - # Error message - assert result =~ "Permissions not specified" - end + bad_params = Map.merge(params, bad_registration_attrs) - test "returns 401 for scopes beyond app scopes", %{conn: conn} do - user = insert(:user) - app = insert(:oauth_app, scopes: ["read", "write"]) + conn = + conn + |> put_session(:registration_id, registration.id) + |> post("/oauth/register", bad_params) - result = - conn - |> post("/oauth/authorize", %{ + 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" => %{ - "name" => user.nickname, - "password" => "test", + "scopes" => app.scopes, "client_id" => app.client_id, "redirect_uri" => app.redirect_uris, - "state" => "statepassed", - "scope" => "read write follow" + "state" => "a_state", + "name" => user.nickname, + "password" => "wrong password" } - }) - |> html_response(:unauthorized) + } - # Keep the details - assert result =~ app.client_id - assert result =~ app.redirect_uris + conn = + conn + |> put_session(:registration_id, registration.id) + |> post("/oauth/register", params) - # Error message - assert result =~ "Permissions not specified" + assert html_response(conn, 401) =~ ~r/name="op" type="submit" value="connect"/ + assert get_flash(conn, :error) == "Invalid Username/Password" + end end - test "issues a token for an all-body request" do - user = insert(:user) - app = insert(:oauth_app, scopes: ["read", "write"]) + 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 - {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) + 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 html_response(conn, 200) =~ ~s(type="submit") + end - 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 - }) + 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 - assert %{"access_token" => token} = json_response(conn, 200) + 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 - token = Repo.get_by(Token, token: token) - assert token - assert token.scopes == auth.scopes + 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 - 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"]) + 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 - # 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 - }) + 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 - assert %{"access_token" => token} = json_response(conn, 200) + 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 - token = Repo.get_by(Token, token: token) - assert token - assert token.scopes == app.scopes + 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 - test "issues a token for request with HTTP basic auth client credentials" do - user = insert(:user) - app = insert(:oauth_app, scopes: ["scope1", "scope2", "scope3"]) + 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, ["scope1", "scope2"]) - assert auth.scopes == ["scope1", "scope2"] + {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) - app_encoded = - (URI.encode_www_form(app.client_id) <> ":" <> URI.encode_www_form(app.client_secret)) - |> Base.encode64() + 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 + }) - 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, "me" => ap_id} = json_response(conn, 200) - assert %{"access_token" => token, "scope" => scope} = 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 - assert scope == "scope1 scope2" + 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)) - token = Repo.get_by(Token, token: token) - assert token - assert token.scopes == ["scope1", "scope2"] - end + app = insert(:oauth_app, scopes: ["read", "write"]) - test "rejects token exchange with invalid client credentials" do - user = insert(:user) - app = insert(:oauth_app) + # 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 + }) - {:ok, auth} = Authorization.create_authorization(app, user) + assert %{"access_token" => token} = json_response(conn, 200) - conn = - build_conn() - |> put_req_header("authorization", "Basic JTIxOiVGMCU5RiVBNCVCNwo=") - |> post("/oauth/token", %{ - "grant_type" => "authorization_code", - "code" => auth.token, - "redirect_uri" => app.redirect_uris - }) + token = Repo.get_by(Token, token: token) + assert token + assert token.scopes == app.scopes + end - assert resp = json_response(conn, 400) - assert %{"error" => _} = resp - refute Map.has_key?(resp, "access_token") - 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"] - test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do - setting = Pleroma.Config.get([:instance, :account_activation_required]) + app_encoded = + (URI.encode_www_form(app.client_id) <> ":" <> URI.encode_www_form(app.client_secret)) + |> Base.encode64() - unless setting do - Pleroma.Config.put([:instance, :account_activation_required], true) - on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end) + 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 - password = "testpassword" - user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) - info_change = Pleroma.User.Info.confirmation_changeset(user.info, :unconfirmed) + test "rejects token exchange with invalid client credentials" do + user = insert(:user) + app = insert(:oauth_app) - {:ok, user} = - user - |> Ecto.Changeset.change() - |> Ecto.Changeset.put_embed(:info, info_change) - |> Repo.update() + {:ok, auth} = Authorization.create_authorization(app, user) - refute Pleroma.User.auth_active?(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 + }) - app = insert(:oauth_app) + assert resp = json_response(conn, 400) + assert %{"error" => _} = resp + refute Map.has_key?(resp, "access_token") + end - conn = - build_conn() - |> post("/oauth/token", %{ - "grant_type" => "password", - "username" => user.nickname, - "password" => password, - "client_id" => app.client_id, - "client_secret" => app.client_secret - }) + test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do + setting = Pleroma.Config.get([:instance, :account_activation_required]) - assert resp = json_response(conn, 403) - assert %{"error" => _} = resp - refute Map.has_key?(resp, "access_token") - end + unless setting do + Pleroma.Config.put([:instance, :account_activation_required], true) + on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end) + end - test "rejects an invalid authorization code" do - app = insert(:oauth_app) + password = "testpassword" + user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) + info_change = Pleroma.User.Info.confirmation_changeset(user.info, :unconfirmed) - 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 - }) + {: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} + ) - assert resp = json_response(conn, 400) - assert %{"error" => _} = json_response(conn, 400) - refute Map.has_key?(resp, "access_token") + 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 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest 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 @@ defmodule Pleroma.Web.OStatus.DeleteHandlingTest 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 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest 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 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest 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 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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"] == "@<a href=\"https://gs.archae.me/user/4687\" class=\"h-card u-url p-nickname mention\" title=\"shpbot\">shpbot</a> 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.OStatusTest 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 @@ defmodule Pleroma.Web.Push.ImplTest do data: %{alerts: %{"follow" => true, "mention" => false}} ) + {:ok, activity} = CommonAPI.post(user, %{"status" => "<Lorem ipsum dolor sit amet."}) + notif = insert(:notification, user: user, - activity: %Pleroma.Activity{ - data: %{ - "type" => "Create", - "actor" => user.ap_id, - "object" => %{"content" => "<Lorem ipsum dolor sit amet."} - } - } + activity: activity ) - assert Impl.perform_send(notif) == [:ok, :ok] + assert Impl.perform(notif) == [:ok, :ok] end + @tag capture_log: true test "returns error if notif does not match " do - assert Impl.perform_send(%{}) == :error + assert Impl.perform(%{}) == :error end test "successful message sending" do assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok end + @tag capture_log: true test "fail message sending" do assert Impl.push_message( @message, @@ -98,48 +98,65 @@ defmodule Pleroma.Web.Push.ImplTest do end test "renders body for create activity" do + user = insert(:user, nickname: "Bob") + + {:ok, activity} = + CommonAPI.post(user, %{ + "status" => + "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis." + }) + + object = Object.normalize(activity) + assert Impl.format_body( %{ - activity: %{ - data: %{ - "type" => "Create", - "object" => %{ - "content" => - "<span>Lorem ipsum dolor sit amet</span>, 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" => - "<span>Lorem ipsum dolor sit amet</span>, consectetur :bear: adipiscing elit. Fusce sagittis finibus turpis." - } + {:ok, activity} = + CommonAPI.post(user, %{ + "status" => + "<span>Lorem ipsum dolor sit amet</span>, 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" => + "<span>Lorem ipsum dolor sit amet</span>, 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 @@ defmodule Pleroma.Web.Salmon.SalmonTest 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 @@ defmodule Pleroma.Web.StreamerTest 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 <https://pleroma.social/> -# 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 = - "<script>alert('YAY')</script>Some :2hu: content mentioning <a href='#{mentioned_user.ap_id}'>@shp</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(<img height="32px" width="32px" alt="2hu" title="2hu" src="corndog.png" />) - - expected_html = - ~s(<p>2hu ) <> - corndog_emojo <> - ~s(</p>alert\('YAY'\)Some ) <> - corndog_emojo <> - ~s( content mentioning <a href=") <> mentioned_user.ap_id <> ~s(">@shp</a>) - - 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 <img height=\"32px\" width=\"32px\" alt=\"2hu\" title=\"2hu\" src=\"corndog.png\" />" - } - - 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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}) conn = conn @@ -678,7 +698,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 end @@ -698,7 +720,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do }) 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do input = %{ "status" => - "Hello again, @shp.<script></script>\nThis is on another :moominmamma: line. #2hu #epic #phantasmagoric", + "Hello again, @shp.<script></script>\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, <span class='h-card'><a data-user='#{mentioned_user.id}' class='u-url mention' href='shp'>@<span>shp</span></a></span>.<script></script><br>This is on another :moominmamma: line. <a class='hashtag' data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a> <a class='hashtag' data-tag='epic' href='http://localhost:4001/tag/epic' rel='tag'>#epic</a> <a class='hashtag' data-tag='phantasmagoric' href='http://localhost:4001/tag/phantasmagoric' rel='tag'>#phantasmagoric</a><br><a href=\"http://example.org/image.jpg\" class='attachment'>image.jpg</a>" + "Hello again, <span class='h-card'><a data-user='#{mentioned_user.id}' class='u-url mention' href='shp'>@<span>shp</span></a></span>.<script></script><br>This is on another :firefox: line. <a class='hashtag' data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a> <a class='hashtag' data-tag='epic' href='http://localhost:4001/tag/epic' rel='tag'>#epic</a> <a class='hashtag' data-tag='phantasmagoric' href='http://localhost:4001/tag/phantasmagoric' rel='tag'>#phantasmagoric</a><br><a href=\"http://example.org/image.jpg\" class='attachment'>image.jpg</a>" - 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest 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]) + + if setting do + Pleroma.Config.put([:instance, :registrations_open], false) + on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end) + end + + 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)}) - {:error, msg} = TwitterAPI.register_user(data) + check_fn.(invite) - assert msg == "Invalid token" - refute Repo.get_by(User, nickname: "GrimReaper") + 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 + + 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 msg == "Expired token" - refute Repo.get_by(User, nickname: "GrimReaper") + 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest 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 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest 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 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest 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 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do 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 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do result = ActivityView.render("activity.json", activity: activity) assert result["statusnet_html"] == - "<a class=\"hashtag\" data-tag=\"bike\" href=\"http://localhost:4001/tag/bike\">#Bike</a> log - Commute Tuesday<br /><a href=\"https://pla.bike/posts/20181211/\">https://pla.bike/posts/20181211/</a><br /><a class=\"hashtag\" data-tag=\"cycling\" href=\"http://localhost:4001/tag/cycling\">#cycling</a> <a class=\"hashtag\" data-tag=\"chscycling\" href=\"http://localhost:4001/tag/chscycling\">#CHScycling</a> <a class=\"hashtag\" data-tag=\"commute\" href=\"http://localhost:4001/tag/commute\">#commute</a><br />MVIMG_20181211_054020.jpg" + "<a class=\"hashtag\" data-tag=\"bike\" href=\"http://localhost:4001/tag/bike\" rel=\"tag\">#Bike</a> log - Commute Tuesday<br /><a href=\"https://pla.bike/posts/20181211/\">https://pla.bike/posts/20181211/</a><br /><a class=\"hashtag\" data-tag=\"cycling\" href=\"http://localhost:4001/tag/cycling\" rel=\"tag\">#cycling</a> <a class=\"hashtag\" data-tag=\"chscycling\" href=\"http://localhost:4001/tag/chscycling\" rel=\"tag\">#CHScycling</a> <a class=\"hashtag\" data-tag=\"commute\" href=\"http://localhost:4001/tag/commute\" rel=\"tag\">#commute</a><br />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 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest 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 = - "<img height=\"32px\" width=\"32px\" alt=\"woollysocks\" title=\"woollysocks\" src=\"http://localhost:4001/finmoji/128px/woollysocks-128.png\" /> meow" + "<img height=\"32px\" width=\"32px\" alt=\"firefox\" title=\"firefox\" src=\"http://localhost:4001/emoji/Firefox.gif\" /> meow" assert result["summary"] == expected assert result["summary_html"] == expected_html @@ -126,10 +126,11 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest do }\">@<span>shp</span></a></span>!", "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 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest 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 @@ defmodule Pleroma.Web.TwitterAPI.ActivityViewTest 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 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest 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 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest 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 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest 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 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest 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 |