summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/config/transfer_task_test.exs22
-rw-r--r--test/conversation_test.exs10
-rw-r--r--test/fixtures/rich_media/non_ogp_embed.html1479
-rw-r--r--test/fixtures/rich_media/ogp-missing-title.html12
-rw-r--r--test/fixtures/tesla_mock/7369654.atom (renamed from test/fixtures/httpoison_mock/7369654.atom)0
-rw-r--r--test/fixtures/tesla_mock/7369654.html (renamed from test/fixtures/httpoison_mock/7369654.html)0
-rw-r--r--test/fixtures/tesla_mock/7even.json (renamed from test/fixtures/httpoison_mock/7even.json)0
-rw-r--r--test/fixtures/tesla_mock/admin@mastdon.example.org.json (renamed from test/fixtures/httpoison_mock/admin@mastdon.example.org.json)0
-rw-r--r--test/fixtures/tesla_mock/atarifrosch_feed.xml (renamed from test/fixtures/httpoison_mock/atarifrosch_feed.xml)0
-rw-r--r--test/fixtures/tesla_mock/atarifrosch_webfinger.xml (renamed from test/fixtures/httpoison_mock/atarifrosch_webfinger.xml)0
-rw-r--r--test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json (renamed from test/fixtures/httpoison_mock/baptiste.gelex.xyz-article.json)0
-rw-r--r--test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json (renamed from test/fixtures/httpoison_mock/baptiste.gelex.xyz-user.json)0
-rw-r--r--test/fixtures/tesla_mock/eal_sakamoto.xml (renamed from test/fixtures/httpoison_mock/eal_sakamoto.xml)0
-rw-r--r--test/fixtures/tesla_mock/emelie.atom (renamed from test/fixtures/httpoison_mock/emelie.atom)0
-rw-r--r--test/fixtures/tesla_mock/emelie.json (renamed from test/fixtures/httpoison_mock/emelie.json)0
-rw-r--r--test/fixtures/tesla_mock/framasoft@framatube.org.json (renamed from test/fixtures/httpoison_mock/framasoft@framatube.org.json)0
-rw-r--r--test/fixtures/tesla_mock/framatube.org_host_meta (renamed from test/fixtures/httpoison_mock/framatube.org_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/gerzilla.de_host_meta (renamed from test/fixtures/httpoison_mock/gerzilla.de_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/gnusocial.de_host_meta (renamed from test/fixtures/httpoison_mock/gnusocial.de_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/gs.example.org_host_meta (renamed from test/fixtures/httpoison_mock/gs.example.org_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/hellpie.json (renamed from test/fixtures/httpoison_mock/hellpie.json)0
-rw-r--r--test/fixtures/tesla_mock/http___gs.example.org_4040_index.php_user_1.xml (renamed from test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml)0
-rw-r--r--test/fixtures/tesla_mock/http___mastodon.example.org_users_admin_status_1234.json (renamed from test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json)0
-rw-r--r--test/fixtures/tesla_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml (renamed from test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___info.pleroma.site_actor.json (renamed from test/fixtures/httpoison_mock/https___info.pleroma.site_actor.json)0
-rw-r--r--test/fixtures/tesla_mock/https___mamot.fr_users_Skruyb.atom (renamed from test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom)0
-rw-r--r--test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.atom (renamed from test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.atom)0
-rw-r--r--test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.xml (renamed from test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json (renamed from test/fixtures/httpoison_mock/https___osada.macgirvin.com_channel_mike.json)0
-rw-r--r--test/fixtures/tesla_mock/https___pawoo.net_users_aqidaqidaqid.xml (renamed from test/fixtures/httpoison_mock/https___pawoo.net_users_aqidaqidaqid.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.atom (renamed from test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom)0
-rw-r--r--test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.xml (renamed from test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain.xml (renamed from test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml (renamed from test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___prismo.news__mxb.json (renamed from test/fixtures/httpoison_mock/https___prismo.news__mxb.json)0
-rw-r--r--test/fixtures/tesla_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml (renamed from test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml (renamed from test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.html (renamed from test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html)0
-rw-r--r--test/fixtures/tesla_mock/https___shitposter.club_user_1.xml (renamed from test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml (renamed from test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml (renamed from test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___social.heldscal.la_user_23211.xml (renamed from test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml)0
-rw-r--r--test/fixtures/tesla_mock/https___social.heldscal.la_user_29191.xml (renamed from test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml)0
-rw-r--r--test/fixtures/tesla_mock/https__info.pleroma.site_activity.json (renamed from test/fixtures/httpoison_mock/https__info.pleroma.site_activity.json)0
-rw-r--r--test/fixtures/tesla_mock/https__info.pleroma.site_activity2.json (renamed from test/fixtures/httpoison_mock/https__info.pleroma.site_activity2.json)0
-rw-r--r--test/fixtures/tesla_mock/https__info.pleroma.site_activity3.json (renamed from test/fixtures/httpoison_mock/https__info.pleroma.site_activity3.json)0
-rw-r--r--test/fixtures/tesla_mock/https__info.pleroma.site_activity4.json (renamed from test/fixtures/httpoison_mock/https__info.pleroma.site_activity4.json)0
-rw-r--r--test/fixtures/tesla_mock/kaniini@gerzilla.de.json (renamed from test/fixtures/httpoison_mock/kaniini@gerzilla.de.json)0
-rw-r--r--test/fixtures/tesla_mock/kaniini@hubzilla.example.org.json (renamed from test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json)0
-rw-r--r--test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml (renamed from test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml)0
-rw-r--r--test/fixtures/tesla_mock/lucifermysticus.json (renamed from test/fixtures/httpoison_mock/lucifermysticus.json)0
-rw-r--r--test/fixtures/tesla_mock/macgirvin.com_host_meta (renamed from test/fixtures/httpoison_mock/macgirvin.com_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/mamot.fr_host_meta (renamed from test/fixtures/httpoison_mock/mamot.fr_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/mastodon.social_host_meta (renamed from test/fixtures/httpoison_mock/mastodon.social_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/mastodon.xyz_host_meta (renamed from test/fixtures/httpoison_mock/mastodon.xyz_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/mayumayu.json (renamed from test/fixtures/httpoison_mock/mayumayu.json)0
-rw-r--r--test/fixtures/tesla_mock/mayumayupost.json (renamed from test/fixtures/httpoison_mock/mayumayupost.json)0
-rw-r--r--test/fixtures/tesla_mock/mike@osada.macgirvin.com.json (renamed from test/fixtures/httpoison_mock/mike@osada.macgirvin.com.json)0
-rw-r--r--test/fixtures/tesla_mock/nonexistant@social.heldscal.la.xml (renamed from test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml)0
-rw-r--r--test/fixtures/tesla_mock/pawoo.net_host_meta (renamed from test/fixtures/httpoison_mock/pawoo.net_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/peertube.moe-vid.json (renamed from test/fixtures/httpoison_mock/peertube.moe-vid.json)0
-rw-r--r--test/fixtures/tesla_mock/pleroma.soykaf.com_host_meta (renamed from test/fixtures/httpoison_mock/pleroma.soykaf.com_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/puckipedia.com.json (renamed from test/fixtures/httpoison_mock/puckipedia.com.json)0
-rw-r--r--test/fixtures/tesla_mock/rinpatch.json (renamed from test/fixtures/httpoison_mock/rinpatch.json)0
-rw-r--r--test/fixtures/tesla_mock/rye.json (renamed from test/fixtures/httpoison_mock/rye.json)0
-rw-r--r--test/fixtures/tesla_mock/sakamoto.atom (renamed from test/fixtures/httpoison_mock/sakamoto.atom)0
-rw-r--r--test/fixtures/tesla_mock/sakamoto_eal_feed.atom (renamed from test/fixtures/httpoison_mock/sakamoto_eal_feed.atom)0
-rw-r--r--test/fixtures/tesla_mock/shitposter.club_host_meta (renamed from test/fixtures/httpoison_mock/shitposter.club_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/shp@pleroma.soykaf.com.feed (renamed from test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.feed)0
-rw-r--r--test/fixtures/tesla_mock/shp@pleroma.soykaf.com.webfigner (renamed from test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.webfigner)0
-rw-r--r--test/fixtures/tesla_mock/shp@social.heldscal.la.xml (renamed from test/fixtures/httpoison_mock/shp@social.heldscal.la.xml)0
-rw-r--r--test/fixtures/tesla_mock/skruyb@mamot.fr.atom (renamed from test/fixtures/httpoison_mock/skruyb@mamot.fr.atom)0
-rw-r--r--test/fixtures/tesla_mock/social.heldscal.la_host_meta (renamed from test/fixtures/httpoison_mock/social.heldscal.la_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/social.sakamoto.gq_host_meta (renamed from test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/social.stopwatchingus-heidelberg.de_host_meta (renamed from test/fixtures/httpoison_mock/social.stopwatchingus-heidelberg.de_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/social.wxcafe.net_host_meta (renamed from test/fixtures/httpoison_mock/social.wxcafe.net_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/spc_5381.atom (renamed from test/fixtures/httpoison_mock/spc_5381.atom)0
-rw-r--r--test/fixtures/tesla_mock/spc_5381_xrd.xml (renamed from test/fixtures/httpoison_mock/spc_5381_xrd.xml)0
-rw-r--r--test/fixtures/tesla_mock/squeet.me_host_meta (renamed from test/fixtures/httpoison_mock/squeet.me_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/status.alpicola.com_host_meta (renamed from test/fixtures/httpoison_mock/status.alpicola.com_host_meta)0
-rw-r--r--test/fixtures/tesla_mock/status.emelie.json (renamed from test/fixtures/httpoison_mock/status.emelie.json)0
-rw-r--r--test/fixtures/tesla_mock/webfinger_emelie.json (renamed from test/fixtures/httpoison_mock/webfinger_emelie.json)0
-rw-r--r--test/fixtures/tesla_mock/winterdienst_webfinger.json (renamed from test/fixtures/httpoison_mock/winterdienst_webfinger.json)0
-rw-r--r--test/fixtures/users_mock/masto_closed_followers.json7
-rw-r--r--test/fixtures/users_mock/masto_closed_following.json7
-rw-r--r--test/fixtures/users_mock/pleroma_followers.json20
-rw-r--r--test/fixtures/users_mock/pleroma_following.json20
-rw-r--r--test/http/request_builder_test.exs91
-rw-r--r--test/integration/mastodon_websocket_test.exs7
-rw-r--r--test/media_proxy_test.exs35
-rw-r--r--test/plugs/idempotency_plug_test.exs110
-rw-r--r--test/reverse_proxy_test.exs297
-rw-r--r--test/support/factory.ex1
-rw-r--r--test/support/helpers.ex6
-rw-r--r--test/support/http_request_mock.ex204
-rw-r--r--test/tasks/config_test.exs23
-rw-r--r--test/tasks/ecto/ecto_test.exs11
-rw-r--r--test/tasks/pleroma_test.exs46
-rw-r--r--test/tasks/robots_txt_test.exs43
-rw-r--r--test/tasks/user_test.exs3
-rw-r--r--test/test_helper.exs2
-rw-r--r--test/upload/filter/anonymize_filename_test.exs40
-rw-r--r--test/user/synchronization_test.exs104
-rw-r--r--test/user/synchronization_worker_test.exs49
-rw-r--r--test/user_search_test.exs252
-rw-r--r--test/user_test.exs336
-rw-r--r--test/web/activity_pub/activity_pub_controller_test.exs7
-rw-r--r--test/web/activity_pub/mrf/anti_link_spam_policy_test.exs9
-rw-r--r--test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs45
-rw-r--r--test/web/activity_pub/transmogrifier_test.exs37
-rw-r--r--test/web/admin_api/admin_api_controller_test.exs261
-rw-r--r--test/web/admin_api/config_test.exs95
-rw-r--r--test/web/admin_api/views/report_view_test.exs40
-rw-r--r--test/web/common_api/common_api_test.exs7
-rw-r--r--test/web/federator_test.exs7
-rw-r--r--test/web/mastodon_api/mastodon_api_controller_test.exs355
-rw-r--r--test/web/mastodon_api/status_view_test.exs35
-rw-r--r--test/web/ostatus/ostatus_controller_test.exs7
-rw-r--r--test/web/ostatus/ostatus_test.exs28
-rw-r--r--test/web/plugs/federating_plug_test.exs13
-rw-r--r--test/web/rich_media/helpers_test.exs47
-rw-r--r--test/web/rich_media/parser_test.exs33
-rw-r--r--test/web/streamer_test.exs106
-rw-r--r--test/web/twitter_api/password_controller_test.exs56
-rw-r--r--test/web/web_finger/web_finger_controller_test.exs6
-rw-r--r--test/web/websub/websub_controller_test.exs10
126 files changed, 3908 insertions, 533 deletions
diff --git a/test/config/transfer_task_test.exs b/test/config/transfer_task_test.exs
index 9b8a8dd45..c0e433263 100644
--- a/test/config/transfer_task_test.exs
+++ b/test/config/transfer_task_test.exs
@@ -13,19 +13,37 @@ defmodule Pleroma.Config.TransferTaskTest do
test "transfer config values from db to env" do
refute Application.get_env(:pleroma, :test_key)
- Pleroma.Web.AdminAPI.Config.create(%{key: "test_key", value: [live: 2, com: 3]})
+ refute Application.get_env(:idna, :test_key)
+
+ Pleroma.Web.AdminAPI.Config.create(%{
+ group: "pleroma",
+ key: "test_key",
+ value: [live: 2, com: 3]
+ })
+
+ Pleroma.Web.AdminAPI.Config.create(%{
+ group: "idna",
+ key: "test_key",
+ value: [live: 15, com: 35]
+ })
Pleroma.Config.TransferTask.start_link()
assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]
+ assert Application.get_env(:idna, :test_key) == [live: 15, com: 35]
on_exit(fn ->
Application.delete_env(:pleroma, :test_key)
+ Application.delete_env(:idna, :test_key)
end)
end
test "non existing atom" do
- Pleroma.Web.AdminAPI.Config.create(%{key: "undefined_atom_key", value: [live: 2, com: 3]})
+ Pleroma.Web.AdminAPI.Config.create(%{
+ group: "pleroma",
+ key: "undefined_atom_key",
+ value: [live: 2, com: 3]
+ })
assert ExUnit.CaptureLog.capture_log(fn ->
Pleroma.Config.TransferTask.start_link()
diff --git a/test/conversation_test.exs b/test/conversation_test.exs
index 5903d10ff..aa193e0d4 100644
--- a/test/conversation_test.exs
+++ b/test/conversation_test.exs
@@ -11,6 +11,16 @@ defmodule Pleroma.ConversationTest do
import Pleroma.Factory
+ setup_all do
+ config_path = [:instance, :federating]
+ initial_setting = Pleroma.Config.get(config_path)
+
+ Pleroma.Config.put(config_path, true)
+ on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
+
+ :ok
+ end
+
test "it goes through old direct conversations" do
user = insert(:user)
other_user = insert(:user)
diff --git a/test/fixtures/rich_media/non_ogp_embed.html b/test/fixtures/rich_media/non_ogp_embed.html
new file mode 100644
index 000000000..62a1d677a
--- /dev/null
+++ b/test/fixtures/rich_media/non_ogp_embed.html
@@ -0,0 +1,1479 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="CACHE-CONTROL" content="NO-CACHE">
+ <meta charset="UTF-8">
+ <link rel="apple-touch-icon-precomposed" sizes="57x57" href="https://img.mfcimg.com/images/favicons/apple-touch-icon-57x57.png?nc=1" />
+<link rel="apple-touch-icon-precomposed" sizes="114x114" href="https://img.mfcimg.com/images/favicons/apple-touch-icon-114x114.png?nc=1" />
+<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://img.mfcimg.com/images/favicons/apple-touch-icon-72x72.png?nc=1" />
+<link rel="apple-touch-icon-precomposed" sizes="144x144" href="https://img.mfcimg.com/images/favicons/apple-touch-icon-144x144.png?nc=1" />
+<link rel="apple-touch-icon-precomposed" sizes="60x60" href="https://img.mfcimg.com/images/favicons/apple-touch-icon-60x60.png?nc=1" />
+<link rel="apple-touch-icon-precomposed" sizes="120x120" href="https://img.mfcimg.com/images/favicons/apple-touch-icon-120x120.png?nc=1" />
+<link rel="apple-touch-icon-precomposed" sizes="76x76" href="https://img.mfcimg.com/images/favicons/apple-touch-icon-76x76.png?nc=1" />
+<link rel="apple-touch-icon-precomposed" sizes="152x152" href="https://img.mfcimg.com/images/favicons/apple-touch-icon-152x152.png?nc=1" />
+<link rel="icon" type="image/png" href="https://img.mfcimg.com/images/favicons/favicon-196x196.png?nc=1" sizes="196x196" />
+<link rel="icon" type="image/png" href="https://img.mfcimg.com/images/favicons/favicon-96x96.png?nc=1" sizes="96x96" />
+<link rel="icon" type="image/png" href="https://img.mfcimg.com/images/favicons/favicon-32x32.png?nc=1" sizes="32x32" />
+<link rel="icon" type="image/png" href="https://img.mfcimg.com/images/favicons/favicon-16x16.png?nc=1" sizes="16x16" />
+<link rel="icon" type="image/png" href="https://img.mfcimg.com/images/favicons/favicon-128.png?nc=1" sizes="128x128" />
+<meta name="application-name" content="MyFreeCams.com Profiles" />
+<meta name="msapplication-TileColor" content="#008000" />
+<meta name="msapplication-TileImage" content="https://img.mfcimg.com/images/favicons/mstile-144x144.png?nc=1" />
+<meta name="msapplication-square70x70logo" content="https://img.mfcimg.com/images/favicons/mstile-70x70.png?nc=1" />
+<meta name="msapplication-square150x150logo" content="https://img.mfcimg.com/images/favicons/mstile-150x150.png?nc=1" />
+<meta name="msapplication-wide310x150logo" content="https://img.mfcimg.com/images/favicons/mstile-310x150.png?nc=1" />
+<meta name="msapplication-square310x310logo" content="https://img.mfcimg.com/images/favicons/mstile-310x310.png?nc=1" />
+
+ <script src="https://img.mfcimg.com/profiles/jquery/jquery-1.9.1.min.js"></script>
+<script src="https://img.mfcimg.com/profiles/jquery/jquery-ui-1.9.2.min.js"></script>
+<script src="https://img.mfcimg.com/profiles/jquery/jquery.ui.touch-punch.min.js"></script> <script>
+ var g_hPlatform = { "id": 1, "domain": "myfreecams.com", "name": "MyFreeCams", "code": "mfc", "image_url": "https://img.mfcimg.com/" };
+
+ try { document.domain = 'myfreecams.com'; } catch (e) {}
+
+ var MfcAssets = {
+ images: "/bundles/mfcprofile/vendor/img/",
+ urls: {
+ www: "https://www.myfreecams.com/",
+ new_comments: "/BlueAngelLove/comments/since/0"
+ }
+ };
+
+ var MfcPageVars = {
+ userId: 0,
+ accessLevel: 0,
+ token: "xIqyjzUBSrt6Rbl_su7UOrDxNZJlZNc4nsWh6eXxDkg",
+ profileState: {"number":127,"string":"Offline"},
+ serverTime: {"unixTime":1561209909,"time":"6:25am PDT","dst":1},
+ profileUsername: "BlueAngelLove",
+ admirers: 4719,
+ username: "",
+ userPhotoUrl: "",
+ vToken: "4c4ea23b221f89b73c964b7f99a50f78",
+ avatarRev: 0,
+ avgRating: {"rating_count":7060,"rating_average":"4.8681"},
+ rating: 0
+};
+
+
+ function MfcProfilePage( jQuery )
+ {
+ var _self = this;
+
+ _self.$ = jQuery;
+
+ _self.token = ( typeof(MfcPageVars) !== 'undefined' && MfcPageVars.token ) ? MfcPageVars.token : _self.$('meta[name=token]').attr('content');
+
+ _self.beforeDomReady();
+
+
+ _self.$(function(){ _self.afterDomReady(); });
+ };
+
+ MfcProfilePage.prototype.beforeDomReady = function()
+ {
+ var _self = this;
+ var $ = _self.$;
+
+ if ( _self.token )
+ {
+ $.ajaxSetup({
+ beforeSend: function(xhr, settings) {
+ if ( settings.type === 'GET' || settings.crossDomain )
+ return;
+
+ if ( $.type(settings.data) === 'object' && $.type(settings.data.append) === 'function' )
+ {
+ settings.data.append('_token', _self.token);
+ }
+ else if ( $.type(settings.data) === 'string' && settings.data.indexOf('_token=') === -1 )
+ {
+ if ( settings.data.length === 0 )
+ {
+ xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
+ } else {
+ settings.data += '&';
+ }
+
+ settings.data += encodeURIComponent('_token') + '=' + encodeURIComponent(_self.token);
+ }
+ }
+ });
+
+ $(document).on('submit', 'form', function(e)
+ {
+ if ( ! $(this).find('#_token').length && ! $(this).data('mfc-no-token') )
+ $(this).append($('<input type="hidden" name="_token" id="_token" value="' + _self.token + '">'));
+ });
+ }
+ };
+
+ MfcProfilePage.prototype.afterDomReady = function()
+ {
+ var _self = this;
+ var $ = this.$;
+
+ var page = $('body').data('mfc-page');
+
+ if ( $.isFunction(_self[page]) )
+ _self[page]();
+ };
+
+ new MfcProfilePage(jQuery);
+</script>
+
+ <link href="https://img.mfcimg.com/profiles/prod/22793316144741120/css/profiles.css?nc=22793316144741120" type="text/css" rel="stylesheet">
+
+ <title>BlueAngelLove's Homepage on MyFreeCams.com</title>
+ <meta name="description" content="BlueAngelLove's webcam homepage on MyFreeCams.com - your #1 adult webcam community">
+ <meta name="keywords" content="webcams,models,adult,community,nude,chat,video">
+
+ <style type="text/css">
+ body.mfc_display_inline_mode #header_bar, body.mfc_display_inline_mode #footer_bar {
+ visibility: hidden;
+ }
+ body.mfc_profile_standard.mfc_display_inline_mode {
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 5px;
+ padding-right: 5px;
+ }
+ body.mfc_profile_standard.mfc_display_inline_mode #profile_about_me {
+ display: flex;
+ flex-flow: wrap;
+ }
+ body.mfc_profile_standard.mfc_display_inline_mode #profile_about_me .heading {
+ flex: 0 0 100%;
+ }
+ body.mfc_profile_standard.mfc_display_inline_mode #profile_about_me .container {
+ flex: 0 1 50%;
+ margin: 0;
+ padding: 0;
+ }
+ body.mfc_profile_standard.mfc_display_inline_mode #profile_about_me #about_me_container, body.mfc_profile_standard.mfc_display_inline_mode #profile_about_me #tags_container {
+ flex: 0 0 100%;
+ margin-top: 0;
+ margin-bottom: 0;
+ }
+ @media (max-width: 850px) {
+ body.mfc_profile_standard.mfc_display_inline_mode #profile_about_me .container {
+ flex: 0 0 100%;
+ }
+ }
+ @media (min-width: 1500px) {
+ body.mfc_profile_standard.mfc_display_inline_mode #profile_about_me .container {
+ flex: 0 0 33%;
+ }
+ }
+ </style>
+
+ <link href="/BlueAngelLove/css?nc=204272526" rel="stylesheet" type="text/css">
+
+
+ <script type="text/javascript">
+ g_bInIframe = (function(w) {
+ try {
+ return w.self !== w.top;
+ } catch (e) {
+ return true;
+ }
+ return false;
+ })(window);
+
+ (function(w,d) {
+ 'use strict';
+
+ var hrefClickFn = function (e) {
+ e = e || w.event;
+
+ var target = findHrefElFn(e.target || e.srcElement);
+
+ if ( target != undefined && ((target.hostname + target.pathname.replace(/(^\/?)/,'/')).toLowerCase() !== (location.hostname + location.pathname).toLowerCase()) ) {
+ target.setAttribute('target', '_blank');
+ target.setAttribute('rel', 'noopener noreferrer');
+ }
+
+ return true;
+ };
+
+ var isHrefElFn = function(el) {
+ var elName = (el.nodeName || el.tagName).toLowerCase();
+ if ( (elName === 'a' || elName === 'area') && el.href != undefined ) { return true; }
+ return false;
+ };
+
+ var findHrefElFn = function(el) {
+ if ( isHrefElFn(el) ) { return el; }
+ while (el = el.parentNode) {
+ if ( isHrefElFn(el) ) { return el; }
+ }
+ return undefined;
+ };
+
+ if ( g_bInIframe ) {
+ if ( d.addEventListener ) {
+ d.addEventListener('click', hrefClickFn);
+ } else {
+ d.attachEvent('onclick', hrefClickFn);
+ }
+ }
+ })(window, document);
+</script>
+
+ </head>
+ <body class="mfc_profile_customized" data-mfc-page="userShow">
+ <script type="text/javascript">
+ (function(w,d,v) {
+ 'use strict';
+
+ var classes = [];
+ var search = w.location.search || '';
+ var vs = (typeof v === 'object' && v.profileState) ? v.profileState.number : 127;
+
+ if ( search.match(/[?&]inline_mode=1/) ) {
+ classes.push('mfc_display_inline_mode');
+ }
+ if ( search.match(/[?&]online=1/) || vs != 127 ) {
+ classes.push('mfc_online');
+ }
+ if ( 'Model' === 'Model' && ( search.match(/[?&]broadcasting=1/) || vs < 90 ) ) {
+ classes.push('mfc_broadcasting');
+ }
+
+ if ( classes.length ) {
+ d.getElementsByTagName('body')[0].className += ' ' + classes.join(' ');
+ }
+
+ })(window, document, MfcPageVars);
+</script>
+ <div id="fixed_background"></div>
+
+ <div id="header_bar">
+ <div class="header_links">
+ <a href="/">Profiles.MyFreeCams.com</a> |
+ <a href='https://www.myfreecams.com/'>MyFreeCams.com</a> |
+ <a href="/_/my_profile">My Profile</a> |
+ <a href="/_/login">Profile Settings</a>
+ </div>
+
+ <div class="clearfix header_time">
+
+ <div id="server_time">
+ <table>
+ <tbody>
+ <tr>
+ <td>Your Time:</td>
+ <td id="your_time"></td>
+ </tr>
+ <tr>
+ <td>MyFreeCams Time:</td>
+ <td id="mfc_time"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ </div>
+ </div>
+
+ <div id="profile">
+ <div class="profile_row">
+ <div class="profile_section" id="profile_header">
+ <div class="profile_section_content">
+ <div class="profile_section_background"></div>
+
+ <div id="avatar_holder">
+ <img id="profile_avatar" class="img_radius_shadow" src="https://img.mfcimg.com/photos2/320/3204009/avatar.90x90.jpg?nc=1557647675" onError="this.onerror=null; this.src='https://img.mfcimg.com/images/nophoto-f.gif';">
+ </div>
+
+ <div id="profile_header_container">
+ <div class="heading">
+ BlueAngelLove
+ </div>
+
+ <div class="container" id="status_container">
+ <div class="label" id="status_label">
+ Status:
+ </div>
+ <div class="value" id="status_value">
+ <span id="member_status_value" class="hidden"></span>
+ <span id="member_type_value">&nbsp;- Model -</span>
+ <span id="member_message_value" class="hidden" data-mfc-member-type="Model"><a href="#" id="show_message_dialog">Send a MyFreeCams Mail</a></span>
+ </div>
+ </div>
+
+
+
+ <div class="container" id="blurb_container">
+ <span class="label" id="blurb_label">
+ Profile Headline:
+ </span>
+
+ <span class="value" id="blurb_value">
+ Enjoy and Love
+ </span>
+ </div>
+
+
+
+
+
+
+
+
+ <div class="container" id="unix_last_broadcast_container">
+ <span class="label" id="unix_last_broadcast_label">
+ Last Broadcast:
+ </span>
+
+ <span class="value convert-time" id="unix_last_broadcast_value" data-mfc-unix-time="1561100400" data-mfc-time-format="ddd, MMM D YYYY"></span>
+ </div>
+
+
+
+
+
+ <div class="container" id="unix_last_updated_container">
+ <span class="label" id="unix_last_updated_label">
+ Last Updated:
+ </span>
+
+ <span class="value convert-time" id="unix_last_updated_value" data-mfc-unix-time="1561193088" data-mfc-time-format="llll"></span>
+ </div>
+
+
+
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="profile_row" id="profile_main_about_holder">
+
+ <div id="profile_main_photo">
+ <div class="profile_section">
+ <div class="profile_section_content">
+ <div class="profile_section_background"></div>
+
+
+ <div class="heading">
+ My Most Recent Pictures
+ </div>
+ <div class="recent_photos">
+ <img src="https://img.mfcimg.com/photos2/320/3204009/986-665-202-679-12065535.80x80.jpg" class="img_radius_shadow show_preview" onError="this.onerror=null; this.src='https://img.mfcimg.com/images/nophoto-f.gif';" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/986-665-202-679-12065535.250.jpg">
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="profile_section" id="profile_about_me_friends">
+ <div class="profile_section_content">
+ <div class="profile_section_background"></div>
+
+ <div class="profile_subsection" id="profile_about_me">
+
+ <div class="heading">
+ About Me
+ </div>
+
+
+
+ <div class="container" id="username_container">
+ <span class="label" id="username_label">
+ Username:
+ </span>
+
+ <span class="value" id="username_value">
+ BlueAngelLove </span>
+ </div>
+
+
+
+
+
+
+
+
+ <div class="container" id="gender_container">
+ <span class="label" id="gender_label">
+ Gender:
+ </span>
+
+ <span class="value" id="gender_value">
+ Female </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="body_type_container">
+ <span class="label" id="body_type_label">
+ Body Type:
+ </span>
+
+ <span class="value" id="body_type_value">
+ Athletic </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="ethnicity_container">
+ <span class="label" id="ethnicity_label">
+ Ethnicity:
+ </span>
+
+ <span class="value" id="ethnicity_value">
+ Other </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="hair_container">
+ <span class="label" id="hair_label">
+ Hair:
+ </span>
+
+ <span class="value" id="hair_value">
+ Brown </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="eyes_container">
+ <span class="label" id="eyes_label">
+ Eyes:
+ </span>
+
+ <span class="value" id="eyes_value">
+ Blue </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="weight_container">
+ <span class="label" id="weight_label">
+ Weight:
+ </span>
+
+ <span class="value" id="weight_value">
+ 45 kilos </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="height_container">
+ <span class="label" id="height_label">
+ Height:
+ </span>
+
+ <span class="value" id="height_value">
+ 165 centimeters </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="age_container">
+ <span class="label" id="age_label">
+ Age:
+ </span>
+
+ <span class="value" id="age_value">
+ 34 </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="city_container">
+ <span class="label" id="city_label">
+ City:
+ </span>
+
+ <span class="value" id="city_value">
+ Mountains </span>
+ </div>
+
+
+
+
+
+
+
+
+
+
+
+ <div class="container" id="sexual_preference_container">
+ <span class="label" id="sexual_preference_label">
+ Sexual Preference:
+ </span>
+
+ <span class="value" id="sexual_preference_value">
+ Bisexual </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="smoke_container">
+ <span class="label" id="smoke_label">
+ Smoke:
+ </span>
+
+ <span class="value" id="smoke_value">
+ Non Smoker </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="drink_container">
+ <span class="label" id="drink_label">
+ Drink:
+ </span>
+
+ <span class="value" id="drink_value">
+ Non Drinker </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="drugs_container">
+ <span class="label" id="drugs_label">
+ Drugs:
+ </span>
+
+ <span class="value" id="drugs_value">
+ Never </span>
+ </div>
+
+
+
+
+
+
+
+
+ <div class="container" id="occupation_container">
+ <span class="label" id="occupation_label">
+ Occupation/Major:
+ </span>
+
+ <span class="value" id="occupation_value">
+ Guide </span>
+ </div>
+
+
+
+
+
+
+
+
+ <div class="container" id="favorite_food_container">
+ <span class="label" id="favorite_food_label">
+ Favorite Food:
+ </span>
+
+ <span class="value" id="favorite_food_value">
+ Chocolate </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="pets_container">
+ <span class="label" id="pets_label">
+ Pets:
+ </span>
+
+ <span class="value" id="pets_value">
+ I dont like pets </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="automobile_container">
+ <span class="label" id="automobile_label">
+ Automobile:
+ </span>
+
+ <span class="value" id="automobile_value">
+ Ford </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="about_me_container">
+ <span class="label" id="about_me_label">
+ About Me:
+ </span>
+
+ <span class="value" id="about_me_value">
+ <a href="//www.dmca.com/Protection/Status.aspx?ID=96b05ddf-1265-4f81-9d84-7dcfeb87cbb6" title="DMCA.com Protection Status" class="dmca-badge"> <img src="https://images.dmca.com/Badges/dmca_protected_16_120.png?ID=96b05ddf-1265-4f81-9d84-7dcfeb87cbb6" alt="DMCA.com Protection Status"></a><a href="http://www.cutercounter.com/" target="_blank" rel="noopener noreferrer"><img src="http://www.cutercounter.com/hits.php?id=grmpackf&amp;nd=7&amp;style=102" border="0" alt="website counter"></a>
+<div id="myCv" class="gen">
+<div class="defaultbg"></div>
+<div class="maintitle">BlueAngelLove</div>
+ <div id="buttons">
+ <a href="https://wa.me/40747018024" class="btn blue"> CONTACT ME </a>
+ <div class="corp">
+
+ <div id="ModelCard">
+ <img src="https://img.mfcimg.com/photos2/320/3204009/314-736-287-236-10552594.jpg" alt="Model's image"><hr><div class="bum"></div>
+ <div class="bum"><a href="http://www.myfreecams.com/mfc2/php/tip.php?request=tip&amp;username=blueangellove" title="Tip Me Offline">Tip Me Offline</a></div>
+ <div class="bum"><a href="http://www.amazon.co.uk/wishlist/3D0MOTP0S0SE5" target="_blank" title="My Amazon Wishlist" rel="noopener noreferrer">My Amazon Wishlist</a></div>
+ <div class="bum"><a href="https://twitter.com/BlueAngelLove33" target="_blank" title="Follow me on Twitter" rel="noopener noreferrer">Follow Me on Twitter</a></div>
+ <div class="bum"><a href="https://wa.me/40747018024" target="_blank" title="Follow me on WhatsApp" rel="noopener noreferrer">Follow Me on Whatsapp</a></div>
+ <div class="bum"><a href="https://www.instagram.com/blueangellove3?r=nametag" title="Follow me on Instagram">Follow Me on Instagram</a></div>
+ <div class="bum"><a href="https://www.snapchat.com/add/cjullyana" title="Follow me on Snapchat">Follow Me on SnapChat</a></div>
+ <div class="bum"><a href="http://hatscripts.com/addskype?BlueAngelLove33" title="Follow me on Skype">Follow Me on Skype</a></div>
+ <div class="bum"><a href="https://www.youtube.com/playlist?list=PLGqo-7BiklVM37HIBud981EpiXxV3yM4m" title="Follow me on Youtube">Follow Me on Youtube </a></div>
+ <div class="bum"><a href="#roomrules" target="_blank" title="Join my Chat Room" rel="noopener noreferrer">Join My Room</a></div>
+ <div class="bum"><a href="https://MFCsha.re/BlueAngelLove" title="Follow me on MFC Share">Follow Me on MFC Share</a></div>
+ <div class="bum"><a href="https://social.myfreecams.com/BlueAngelLove" title="Follow me on Social MFC">Follow Me on Social MFC </a></div>
+ <div class="bum"><a href="https://www.rabb.it/s/d556sr" title="Follow me on Rabbit TV">Follow me on Rabbit TV</a></div>
+ <div class="bum"><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=Y8KLFSLZJAKP2&amp;source=url" title="Spoil Me and Offer Your Gift">Spoil Me and Offer Your Gift</a></div>
+ <div class="bum"><a href="https://player.vimeo.com/video/326274838" title="Follow Me">Follow Me</a></div>
+ <div class="bum"><a href="https://www.timeanddate.com/worldclock/personal.html?cities=179,136,248,250,263,152,2462,716,195,69&amp;wch=2" title="TIME CONVERTOR">TIME CONVERTOR</a></div>
+ <hr></div>
+ </div>
+ <div id="AboutMe">
+ <div class="skilltitle">Angel</div>
+ <div class="corp corpus xcr">
+ <div class="unscor">
+ I Want To Be Seduced
+ <div class="skills_model1"><div class="metru_experience skill bxhdw"></div></div>
+ </div>
+ <div class="unscor">
+ I Love Flirt
+ <div class="skills_model1"><div class="metru_coding skill bxhdw"></div></div>
+ </div>
+ <div class="unscor">
+ I Want Be Part Of Your life
+ <div class="skills_model1"><div class="metru_concept skill bxhdw"></div></div>
+ </div>
+ <div class="unscor">
+ I Love Dancing
+ <div class="skills_model1"><div class="metru_concept skill bxhdw"></div></div>
+ </div>
+ <div class="unscor">
+ I Love Erotic Chats
+ <div class="skills_model1"><div class="metru_graphic skill bxhdw"></div></div>
+ </div>
+ <div class="unscor">
+ I am Funny
+ <div class="skills_model1"><div class="metru_coding skill bxhdw"></div></div>
+ </div>
+ <div class="unscor">
+ I Enjoy C2C
+ <div class="skills_model1"><div class="metru_experience skill bxhdw"></div></div>
+ </div>
+ <div class="unscor">
+ I Love Sex and Feel u
+ <div class="skills_model1"><div class="metru_concept skill bxhdw"></div></div>
+ </div>
+ </div>
+ <hr></div>
+ </div>
+ <hr><div class="maintitle">June Month Contestst-Top 3 tippers Get A gift mailed,videos,pictures and will my right hand full month and room helpers as well (be my men for a month or who knows...maybe forever) ***
+Love Ya Angels*** We are currently ranked #2200 overall </div>
+ <p class="dasinfo">Get Listed on My Wall of Fame</p>
+ <div class="corp zreq">
+ <div class="ttippers xcr">
+
+ <p> ElmosEgo 6570 Tks </p>
+ <p> Rw2lite 4800 Tks </p>
+ <p> Toastboi 2093 Tks </p>
+ <p> Acoolahole 1850 Tks </p>
+ <p> Gonodog 1299 Tks </p>
+ <p> Pumpy_G 800 Tks </p>
+ <p> Fowser 690 Tks </p>
+ <p> Aquanautic 600 Tks </p>
+ <p> Daveonthelake 535 Tks </p>
+ <p> Wildpervert2 500 Tks </p>
+ <p> Cloud10101 350 Tks </p>
+ <p> Branson102 337 Tks </p>
+ <p> TheCopperhead 329 Tks </p>
+ <p> Mouche99 250 Tks </p>
+ <p> The88drummer 233 Tks </p>
+ <p> Stringtrees86 199 Tks </p>
+ <p> Blazegordon 183 Tks </p>
+ <p> Waiting_4 183 Tks </p>
+ <p> Sam_mie 170 Tks </p>
+ <p> UtterTripe 150 Tks </p>
+ <p> Darth_penguin 150 Tks </p>
+ <p> Playfullpurv 120 Tks </p>
+ <p> Jordnsprings 103 Tks </p>
+ <p> Travelinlover 100 Tks </p>
+ <p> Da884 100 Tks </p>
+
+ </div>
+ <a href="https://imgbb.com/"><img src="https://i.ibb.co/mybZhYn/cory1.jpg" alt="cory1" border="0"></a>
+</div>
+ <hr><div class="maintitle">May Contest winners - Each month Top 3 tippers Get A gift mailed, videos and pictures - Love Ya Angels </div>
+ <p class="dasinfo">Get Listed on My Wall of Fame</p>
+ <div class="corp zreq">
+ <div class="ttippers xcr">
+ <p> Rw2lite </p>
+ <p> ElmosEgo </p>
+ <p> TJuonesWoah </p>
+
+</div>
+ <a href="https://imgbb.com/"><img src="https://i.ibb.co/mybZhYn/cory1.jpg" alt="cory1" border="0"></a>
+</div>
+ <hr><div class="maintitle"> Menu Per Day </div>
+ <p class="dasinfo">LOVE YA ANGELS</p>
+ <div class="corp zreq">
+ <div class="ttippers xcr">
+ <p> Monday - Outfits Strip </p>
+ <p> Tusday - Raffle </p>
+ <p> Wensday - Gamblers Night </p>
+ <p> Thusday - Orgasmic Vibra or Dildos </p>
+ <p> Friday - Wheel/Treat or Trick </p>
+ <p> Saturday - Phrase </p>
+ <p> Sunday - Keno and Boyfriend choice </p>
+
+
+
+ </div>
+ <div class="dasinfo">You have to tip to get listed above so do your best to get on my exclusive Top Tippers List</div>
+ <div class="bum"></div>
+ </div>
+ <a href="https://ibb.co/vcFmK7x"><img src="https://i.ibb.co/j8NG1cv/Whats-App-Image-2019-01-09-at-10-35-17.jpg" alt="Whats-App-Image-2019-01-09-at-10-35-17" border="0"></a>
+ <a href="https://ibb.co/RcMF0T5"><img src="https://i.ibb.co/kXr782d/45280406-1564895203655742-4887638015087738880-n.jpg" alt="45280406-1564895203655742-4887638015087738880-n" border="0"></a>
+ <a href="https://ibb.co/1s0Tp2r"><img src="https://i.ibb.co/gvrJXgS/best.jpg" alt="best" border="0"></a>
+ <a href="https://ibb.co/cJmJHHv"><img src="https://i.ibb.co/F6P6MMW/Whats-App-Image-2019-01-09-at-10-35-16.jpg" alt="Whats-App-Image-2019-01-09-at-10-35-16" border="0"></a>
+
+ <div class="bum"><a href="http://www.myfreecams.com/mfc2/php/tip.php?request=tip&amp;username=blueangellove" title="Tip Me Offline">Tip Me Offline</a></div>
+ <div class="bum"><a href="http://www.amazon.co.uk/wishlist/3D0MOTP0S0SE5" target="_blank" title="My Amazon Wishlist" rel="noopener noreferrer">My Amazon Wishlist</a></div>
+ <div class="bum"><a href="https://twitter.com/BlueAngelLove33" target="_blank" title="Follow me on Twitter" rel="noopener noreferrer">Follow Me on Twitter</a></div>
+ <div class="bum"><a href="https://wa.me/40747018024" target="_blank" title="Follow me on WhatsApp" rel="noopener noreferrer">Follow Me on Whatsapp</a></div>
+ <div class="bum"><a href="https://www.instagram.com/blueangellove3?r=nametag" title="Follow me on Instagram">Follow Me on Instagram</a></div>
+ <div class="bum"><a href="https://www.snapchat.com/add/cjullyana" title="Follow me on Snapchat">Follow Me on SnapChat</a></div>
+ <div class="bum"><a href="http://hatscripts.com/addskype?BlueAngelLove33" title="Follow me on Skype">Follow Me on Skype</a></div>
+ <div class="bum"><a href="https://www.youtube.com/playlist?list=PLGqo-7BiklVM37HIBud981EpiXxV3yM4m" title="Follow me on Youtube">Follow Me on Youtube </a></div>
+ <div class="bum"><a href="#roomrules" target="_blank" title="Join my Chat Room" rel="noopener noreferrer">Join My Room</a></div>
+ <div class="bum"><a href="https://MFCsha.re/BlueAngelLove" title="Follow me on MFC Share">Follow Me on MFC Share</a></div>
+ <div class="bum"><a href="https://social.myfreecams.com/BlueAngelLove" title="Follow me on Social MFC">Follow Me on Social MFC </a></div>
+ <div class="bum"><a href="https://www.rabb.it/s/d556sr" title="Follow me on Rabbit TV">Follow me on Rabbit TV</a></div>
+ <div class="bum"><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=Y8KLFSLZJAKP2&amp;source=url" title="Spoil Me and Offer Your Gift">Spoil Me and Offer Your Gift</a></div>
+ <div class="bum"><a href="https://player.vimeo.com/video/326274838" title="Follow Me">Follow Me</a></div>
+ <div class="bum"><a href="https://www.timeanddate.com/worldclock/personal.html?cities=179,136,248,250,263,152,2462,716,195,69&amp;wch=2" title="TIME CONVERTOR">TIME CONVERTOR</a></div>
+ <hr><div id="GNav">
+ <div id="GNwrapper">
+ <a class="abMe" title="About Me" href="#aboutmesection"></a></div>
+</div>
+<div class="bum"><a href="https://MFCsha.re/BlueAngelLove" title="Follow me on MFC Share">Follow Me on MFC Share</a></div>
+<img src="https://i.ibb.co/Pmt2PsP/Whats-App-Image-2019-05-12-at-05-55-35-1.jpg" alt="Whats-App-Image-2019-05-12-at-05-55-35-1" border="0"><img src="https://image.ibb.co/bt1tzq/lovense-level.png" alt="lovense-level" border="0"><div>
+<span class="neontexte"></span></div>
+<div id="OneSection">
+<div></div>
+<div>
+<img src="http://1.bp.blogspot.com/-qTHLNVFggQU/VFdFIOFPDqI/AAAAAAAAGdU/6cWnDLVp0d8/s1600/findme.png" class="findme" alt="camgirl xxx amateur sex sexy"></div>
+</div>
+<div>
+<div id="TwoSection">
+<div id="aboutmesection">
+<a href="https://ibb.co/ZS2D4vh"><img src="https://i.ibb.co/421rvCj/39741863-284302529029606-7659956026455621632-n.jpg" alt="39741863-284302529029606-7659956026455621632-n" border="0"></a>
+<div id="abtmesec" class="frame">
+<span class="neontext">Let's Fun Laugh Live</span><br><i>I am Jullia from Transylvania and is a pleasure to have u around Enjoy me and my room</i><br><br><span class="neontext">BlueAngelLove</span><br><i>I am good, but not an angel. I do sin, but I am not the devil. I am just a girl in a big world trying to find someone to love and be loved</i><br><br></div>
+<a href="https://ibb.co/gvVKkkf"><img src="https://i.ibb.co/6vBCjjT/20171114-113848.jpg" alt="20171114-113848" border="0"></a><a>
+</a></div>~~~Live~Laugh~Love~~~<i><a href="https://info.flagcounter.com/1Ea8"><img src="https://s04.flagcounter.com/countxl/1Ea8/bg_85C2FF/txt_242424/border_CCCCCC/columns_3/maxflags_33/viewers_0/labels_1/pageviews_0/flags_0/percent_0/" alt="Flag Counter" border="0"></a>
+</i></div><div id="vimlft" class="frame"></div></div></div> </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="tags_container">
+ <span class="label" id="tags_label">
+ Tags:
+ </span>
+
+ <span class="value" id="tags_value">
+ natural, blue eyes, toys, funny, oil, shower, fetish, costume, sex, natural, masturbation, finger, dp, anal, girl next door, romantic, naughty, pervert, open mind, play roles, horny, playful, smiley, lover, sweet, sexy, beautiful, hot, shaved, friendly, pussy, skype </span>
+ </div>
+
+
+
+ </div>
+
+ <div class="profile_subsection" id="profile_friends">
+
+ <div class="heading">
+ Friends
+ </div>
+
+ <div class="container" id="average_rating_container">
+ <span class="label" id="average_rating_label">
+ Average Rating:
+ </span>
+ <span class="value" id="average_rating_value">
+ <span id="average_rating"></span>
+ <span id="average_rating_count"></span>
+ </span>
+ </div>
+
+ <div class="container" id="rate_container">
+ <span class="label" id="rate_label">
+ Rate BlueAngelLove:
+ </span>
+ <span class="value" id="rate_value">
+ <form id="new_rating" class="hidden" action="/BlueAngelLove/ratings" method="post">
+ <span id="rating_value_bar"></span>
+ <span id="rating_confirm" class="hidden emphasis notice"></span>
+</form>
+<div id="new_rating_login_message" class="hidden">
+ You must <a href="/_/login">login</a> to rate.
+</div> </span>
+ </div>
+
+ <div class="container" id="admirers_container">
+ <span class="label" id="admirers_label">
+ Admirers:
+ <br>
+ <form id="new_admirer" action="/BlueAngelLove/admirers" method="post">
+
+ (<a href='#' id="admire">admire</a><span id="admire_confirm" class="hidden notice">admired!</span>)
+</form> </span>
+ <span class="value" id="admirers_value"></span>
+ </div>
+
+ <div class="container" id="friends_container">
+ <span class="label" id="friends_label">
+ Profile Friends:
+ <br>
+ <form id="new_homepage_friend" action="/BlueAngelLove/homepage_friends" method="post">
+
+ (<a href='#' id='make_friend'>make friend</a><span id="make_friend_confirm" class="hidden notice">added!</span>)
+</form> </span>
+ <span class="value" id="friends_value">
+ <a href="/Schnitzngrubn">Schnitzngrubn</a>
+ <a href="/UtterTripe">UtterTripe</a>
+ <a href="/MisterPopular">MisterPopular</a>
+ <a href="/neoviewer">neoviewer</a>
+ <a href="/lasse1991">lasse1991</a>
+ <a href="/toastboi">toastboi</a>
+ <a href="/obiwan1965">obiwan1965</a>
+ <a href="/Eastie_Beasty">Eastie_Beasty</a>
+ <a href="/Robby1890">Robby1890</a>
+ <a href="/rw2lite">rw2lite</a>
+ <a href="/zoomie2178">zoomie2178</a>
+ <a href="/AS_rayman41">AS_rayman41</a>
+ <a href="/CJamz87">CJamz87</a>
+ <a href="/Dunky4Jullia">Dunky4Jullia</a>
+ <a href="/Zdasher">Zdasher</a>
+ <a href="/Fowser">Fowser</a>
+ <a href="/buffaloman69">buffaloman69</a>
+ <a href="/Numb33rs">Numb33rs</a>
+ <a href="/ElmosEgo">ElmosEgo</a>
+ <a href="/DaleCooper_">DaleCooper_</a>
+ <a href="/Aquanautic">Aquanautic</a>
+ <a href="/Waiting_4">Waiting_4</a>
+ <a href="/Oliver_xXx">Oliver_xXx</a>
+ <a href="/motion454">motion454</a>
+ <a href="/The_Greg1">The_Greg1</a>
+ <a href="/Razumichin">Razumichin</a>
+ <a href="/Sam_mie">Sam_mie</a>
+ </span>
+ </div>
+
+ <div class="container" id="favorite_models_container">
+ <span class="label" id="favorite_models_label">
+ Favorite Models:
+ </span>
+ <span class="value" id="favorite_models_value">
+ <a href="/BlueAngelLove">BlueAngelLove</a>
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ <div class="profile_row">
+ <div class="profile_section" id="profile_password_photo_galleries">
+ <div class="profile_section_content">
+ <div class="profile_section_background"></div>
+
+ <div class="heading">
+ Password Protected Galleries
+ </div>
+
+ <div class="holder" id="password_photo_gallery_control"></div>
+ <ul class="photo_gallery_previews" id="password_photo_gallery_previews">
+ <li class="photo_gallery_preview" data-mfc-name="Paris , Disneyland , Belgium , Frankfurt , Berlin" data-mfc-url="/BlueAngelLove/view_gallery/498241/password">
+ <div class="photo_gallery_name">
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="498241" data-mfc-protected="1">Paris , Disneyland , Belgium , Frankfurt , Berlin</a>
+
+ </div>
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="498241" data-mfc-protected="1"><img class='photo_gallery_lock img_radius_shadow' src='https://img.mfcimg.com/images/lock-icon.gif'></a>
+
+ <div class="photo_gallery_count">
+ 17 Photos
+ </div>
+ </li>
+
+
+ <li class="photo_gallery_preview" data-mfc-name="Paris , Disneyland , Belgium , Frankfurt , Berlin ..." data-mfc-url="/BlueAngelLove/view_gallery/498240/password">
+ <div class="photo_gallery_name">
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="498240" data-mfc-protected="1">Paris , Disneyland , Belgium , Frankfurt , Berlin ...</a>
+
+ </div>
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="498240" data-mfc-protected="1"><img class='photo_gallery_lock img_radius_shadow' src='https://img.mfcimg.com/images/lock-icon.gif'></a>
+
+ <div class="photo_gallery_count">
+ 37 Photos
+ </div>
+ </li>
+
+
+ <li class="photo_gallery_preview" data-mfc-name="CJ Art - Free Gallery - Just pm me and i give u the password" data-mfc-url="/BlueAngelLove/view_gallery/343862/password">
+ <div class="photo_gallery_name">
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="343862" data-mfc-protected="1">CJ Art - Free Gallery - Just pm me and i give u the password</a>
+
+ </div>
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="343862" data-mfc-protected="1"><img class='photo_gallery_lock img_radius_shadow' src='https://img.mfcimg.com/images/lock-icon.gif'></a>
+
+ <div class="photo_gallery_count">
+ 15 Photos
+ </div>
+ </li>
+
+
+ <li class="photo_gallery_preview" data-mfc-name="4MyAngels - Free Gallery - Just pm me and i give u the password" data-mfc-url="/BlueAngelLove/view_gallery/340500/password">
+ <div class="photo_gallery_name">
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="340500" data-mfc-protected="1">4MyAngels - Free Gallery - Just pm me and i give u the password</a>
+
+ </div>
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="340500" data-mfc-protected="1"><img class='photo_gallery_lock img_radius_shadow' src='https://img.mfcimg.com/images/lock-icon.gif'></a>
+
+ <div class="photo_gallery_count">
+ 97 Photos
+ </div>
+ </li>
+
+
+ </ul>
+ </div>
+ </div>
+
+ </div>
+ <div class="hidden profile_row" id="password_photo_galleries">
+ <div class="profile_section">
+ <div class="profile_section_content">
+ <div class="profile_section_background"></div>
+ </div>
+ </div>
+ </div>
+ <div class="profile_row">
+ <div class="profile_section" id="profile_photo_galleries">
+ <div class="profile_section_content">
+ <div class="profile_section_background"></div>
+
+ <div class="heading">
+ Photo Galleries
+ </div>
+
+ <div class="holder" id="photo_gallery_control"></div>
+ <ul class="photo_gallery_previews" id="photo_gallery_previews">
+ <li class="photo_gallery_preview" data-mfc-name="Recent Photo" >
+ <div class="photo_gallery_name">
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="3" >Recent Photo</a>
+
+ </div>
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="3" ><img class='photo_gallery_image img_radius_shadow' src='https://img.mfcimg.com/photos2/320/3204009/986-665-202-679-12065535.80x80.jpg' onError="this.onerror=null; this.src='https://img.mfcimg.com/images/nophoto-f.gif';"></a>
+
+ <div class="photo_gallery_count">
+ 1 Photo
+ </div>
+ </li>
+
+
+ <li class="photo_gallery_preview" data-mfc-name="jullia" >
+ <div class="photo_gallery_name">
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="56075" >jullia</a>
+
+ </div>
+ <a href="#" class="photo_gallery_link" data-mfc-gallery="56075" ><img class='photo_gallery_image img_radius_shadow' src='https://img.mfcimg.com/photos2/320/3204009/681-423-335-230-1243247.80x80.jpg' onError="this.onerror=null; this.src='https://img.mfcimg.com/images/nophoto-f.gif';"></a>
+
+ <div class="photo_gallery_count">
+ 68 Photos
+ </div>
+ </li>
+
+
+ </ul>
+ </div>
+ </div>
+
+ </div>
+ <div class="hidden profile_row" id="photo_galleries">
+ <div class="profile_section">
+ <div class="profile_section_content">
+ <div class="profile_section_background"></div>
+ <div class="hidden photo_gallery" id="profile_photo_gallery_3">
+ <div class="heading">
+ Recent Photo
+ </div>
+
+ <div class="images">
+ <a href="https://img.mfcimg.com/photos2/320/3204009/986-665-202-679-12065535.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/986-665-202-679-12065535.80x80.jpg" data-mfc-caption="" data-mfc-width="1600" data-mfc-height="1200" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/986-665-202-679-12065535.250.jpg"></a>
+ </div>
+</div> <div class="hidden photo_gallery" id="profile_photo_gallery_56075">
+ <div class="heading">
+ jullia
+ </div>
+
+ <div class="images">
+ <a href="https://img.mfcimg.com/photos2/320/3204009/681-423-335-230-1243247.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/681-423-335-230-1243247.80x80.jpg" data-mfc-caption="" data-mfc-width="975" data-mfc-height="635" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/681-423-335-230-1243247.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/317-507-429-599-1243547.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/317-507-429-599-1243547.80x80.jpg" data-mfc-caption="" data-mfc-width="841" data-mfc-height="905" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/317-507-429-599-1243547.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/140-305-410-615-1243548.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/140-305-410-615-1243548.80x80.jpg" data-mfc-caption="" data-mfc-width="553" data-mfc-height="703" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/140-305-410-615-1243548.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/307-788-771-545-1243550.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/307-788-771-545-1243550.80x80.jpg" data-mfc-caption="" data-mfc-width="649" data-mfc-height="875" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/307-788-771-545-1243550.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/684-466-940-744-1243551.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/684-466-940-744-1243551.80x80.jpg" data-mfc-caption="" data-mfc-width="504" data-mfc-height="558" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/684-466-940-744-1243551.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/231-700-451-967-1317683.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/231-700-451-967-1317683.80x80.jpg" data-mfc-caption="" data-mfc-width="759" data-mfc-height="631" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/231-700-451-967-1317683.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/889-473-722-704-1317685.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/889-473-722-704-1317685.80x80.jpg" data-mfc-caption="" data-mfc-width="794" data-mfc-height="616" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/889-473-722-704-1317685.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/632-850-956-399-1317690.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/632-850-956-399-1317690.80x80.jpg" data-mfc-caption="" data-mfc-width="774" data-mfc-height="769" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/632-850-956-399-1317690.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/340-370-972-798-1317693.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/340-370-972-798-1317693.80x80.jpg" data-mfc-caption="" data-mfc-width="718" data-mfc-height="491" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/340-370-972-798-1317693.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/675-946-621-275-1320874.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/675-946-621-275-1320874.80x80.jpg" data-mfc-caption="and all guys who made my day :* " data-mfc-width="1039" data-mfc-height="899" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/675-946-621-275-1320874.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/989-178-581-568-1352794.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/989-178-581-568-1352794.80x80.jpg" data-mfc-caption="" data-mfc-width="555" data-mfc-height="623" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/989-178-581-568-1352794.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/780-959-310-914-1352798.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/780-959-310-914-1352798.80x80.jpg" data-mfc-caption="" data-mfc-width="625" data-mfc-height="552" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/780-959-310-914-1352798.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/636-984-916-475-1354386.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/636-984-916-475-1354386.80x80.jpg" data-mfc-caption="" data-mfc-width="635" data-mfc-height="746" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/636-984-916-475-1354386.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/744-644-726-778-1491823.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/744-644-726-778-1491823.80x80.jpg" data-mfc-caption="" data-mfc-width="983" data-mfc-height="943" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/744-644-726-778-1491823.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/657-707-347-607-1491824.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/657-707-347-607-1491824.80x80.jpg" data-mfc-caption="" data-mfc-width="953" data-mfc-height="943" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/657-707-347-607-1491824.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/735-531-553-176-1648078.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/735-531-553-176-1648078.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/735-531-553-176-1648078.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/736-829-137-558-1648081.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/736-829-137-558-1648081.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/736-829-137-558-1648081.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/451-346-815-316-1648083.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/451-346-815-316-1648083.80x80.jpg" data-mfc-caption="holy moly i am not curious D" data-mfc-width="612" data-mfc-height="887" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/451-346-815-316-1648083.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/902-338-266-573-1648084.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/902-338-266-573-1648084.80x80.jpg" data-mfc-caption="still not curious " data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/902-338-266-573-1648084.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/738-765-195-927-1648085.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/738-765-195-927-1648085.80x80.jpg" data-mfc-caption="u curious one " data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/738-765-195-927-1648085.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/928-809-867-351-1652571.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/928-809-867-351-1652571.80x80.jpg" data-mfc-caption="" data-mfc-width="979" data-mfc-height="490" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/928-809-867-351-1652571.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/462-736-528-238-1686155.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/462-736-528-238-1686155.80x80.jpg" data-mfc-caption="" data-mfc-width="1280" data-mfc-height="512" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/462-736-528-238-1686155.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/334-394-125-456-1686157.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/334-394-125-456-1686157.80x80.jpg" data-mfc-caption="" data-mfc-width="972" data-mfc-height="1021" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/334-394-125-456-1686157.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/315-230-389-269-1686158.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/315-230-389-269-1686158.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/315-230-389-269-1686158.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/654-561-626-601-1686163.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/654-561-626-601-1686163.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/654-561-626-601-1686163.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/951-538-671-632-1686164.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/951-538-671-632-1686164.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/951-538-671-632-1686164.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/344-284-425-291-1686166.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/344-284-425-291-1686166.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/344-284-425-291-1686166.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/841-495-993-546-1686168.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/841-495-993-546-1686168.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/841-495-993-546-1686168.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/464-292-321-375-1686169.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/464-292-321-375-1686169.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/464-292-321-375-1686169.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/116-821-970-661-1686171.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/116-821-970-661-1686171.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/116-821-970-661-1686171.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/225-535-697-812-1686172.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/225-535-697-812-1686172.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/225-535-697-812-1686172.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/555-716-876-756-1686173.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/555-716-876-756-1686173.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/555-716-876-756-1686173.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/716-290-623-869-1686175.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/716-290-623-869-1686175.80x80.jpg" data-mfc-caption="" data-mfc-width="1038" data-mfc-height="1006" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/716-290-623-869-1686175.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/230-899-707-297-1686178.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/230-899-707-297-1686178.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/230-899-707-297-1686178.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/162-148-524-271-1686182.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/162-148-524-271-1686182.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/162-148-524-271-1686182.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/415-762-949-132-1686186.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/415-762-949-132-1686186.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/415-762-949-132-1686186.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/755-564-921-527-1686189.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/755-564-921-527-1686189.80x80.jpg" data-mfc-caption="" data-mfc-width="623" data-mfc-height="654" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/755-564-921-527-1686189.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/336-756-382-542-1767063.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/336-756-382-542-1767063.80x80.jpg" data-mfc-caption="" data-mfc-width="1280" data-mfc-height="1023" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/336-756-382-542-1767063.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/855-646-780-704-1767067.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/855-646-780-704-1767067.80x80.jpg" data-mfc-caption="" data-mfc-width="1280" data-mfc-height="1023" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/855-646-780-704-1767067.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/371-515-389-663-1767070.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/371-515-389-663-1767070.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/371-515-389-663-1767070.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/971-471-877-691-1767071.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/971-471-877-691-1767071.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/971-471-877-691-1767071.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/408-470-703-495-1767072.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/408-470-703-495-1767072.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/408-470-703-495-1767072.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/349-843-504-986-1767076.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/349-843-504-986-1767076.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/349-843-504-986-1767076.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/929-861-253-392-1767078.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/929-861-253-392-1767078.80x80.jpg" data-mfc-caption="" data-mfc-width="2560" data-mfc-height="1024" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/929-861-253-392-1767078.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/806-418-694-591-1767139.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/806-418-694-591-1767139.80x80.jpg" data-mfc-caption="" data-mfc-width="1280" data-mfc-height="800" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/806-418-694-591-1767139.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/713-749-399-951-1767140.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/713-749-399-951-1767140.80x80.jpg" data-mfc-caption="" data-mfc-width="1280" data-mfc-height="800" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/713-749-399-951-1767140.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/940-530-100-397-1847969.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/940-530-100-397-1847969.80x80.jpg" data-mfc-caption="" data-mfc-width="1280" data-mfc-height="800" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/940-530-100-397-1847969.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/331-281-416-758-1847972.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/331-281-416-758-1847972.80x80.jpg" data-mfc-caption="" data-mfc-width="1280" data-mfc-height="800" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/331-281-416-758-1847972.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/989-327-876-935-2041425.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/989-327-876-935-2041425.80x80.jpg" data-mfc-caption="" data-mfc-width="1280" data-mfc-height="800" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/989-327-876-935-2041425.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/789-661-181-290-2227549.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/789-661-181-290-2227549.80x80.jpg" data-mfc-caption="" data-mfc-width="1920" data-mfc-height="1288" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/789-661-181-290-2227549.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/253-580-172-496-2617499.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/253-580-172-496-2617499.80x80.jpg" data-mfc-caption="" data-mfc-width="977" data-mfc-height="767" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/253-580-172-496-2617499.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/987-119-713-682-2624979.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/987-119-713-682-2624979.80x80.jpg" data-mfc-caption="" data-mfc-width="1142" data-mfc-height="566" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/987-119-713-682-2624979.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/559-379-311-707-2633932.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/559-379-311-707-2633932.80x80.jpg" data-mfc-caption="" data-mfc-width="1920" data-mfc-height="1288" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/559-379-311-707-2633932.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/177-536-481-276-7714372.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/177-536-481-276-7714372.80x80.jpg" data-mfc-caption="" data-mfc-width="1366" data-mfc-height="768" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/177-536-481-276-7714372.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/511-128-866-710-7714373.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/511-128-866-710-7714373.80x80.jpg" data-mfc-caption="" data-mfc-width="1366" data-mfc-height="768" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/511-128-866-710-7714373.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/126-900-930-456-7714374.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/126-900-930-456-7714374.80x80.jpg" data-mfc-caption="" data-mfc-width="671" data-mfc-height="649" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/126-900-930-456-7714374.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/639-324-503-206-7714375.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/639-324-503-206-7714375.80x80.jpg" data-mfc-caption="" data-mfc-width="673" data-mfc-height="671" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/639-324-503-206-7714375.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/746-103-976-888-8099406.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/746-103-976-888-8099406.80x80.jpg" data-mfc-caption="" data-mfc-width="1075" data-mfc-height="822" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/746-103-976-888-8099406.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/361-992-343-713-8099407.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/361-992-343-713-8099407.80x80.jpg" data-mfc-caption="" data-mfc-width="1920" data-mfc-height="1080" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/361-992-343-713-8099407.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/184-179-678-355-8099408.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/184-179-678-355-8099408.80x80.jpg" data-mfc-caption="" data-mfc-width="1920" data-mfc-height="1080" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/184-179-678-355-8099408.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/834-731-356-329-8099409.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/834-731-356-329-8099409.80x80.jpg" data-mfc-caption="" data-mfc-width="1920" data-mfc-height="1080" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/834-731-356-329-8099409.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/717-329-382-179-8828007.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/717-329-382-179-8828007.80x80.jpg" data-mfc-caption="" data-mfc-width="1013" data-mfc-height="853" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/717-329-382-179-8828007.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/621-220-484-504-8828008.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/621-220-484-504-8828008.80x80.jpg" data-mfc-caption="" data-mfc-width="1920" data-mfc-height="1080" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/621-220-484-504-8828008.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/530-414-264-944-8828009.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/530-414-264-944-8828009.80x80.jpg" data-mfc-caption="" data-mfc-width="1291" data-mfc-height="835" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/530-414-264-944-8828009.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/615-981-631-653-11173625.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/615-981-631-653-11173625.80x80.jpg" data-mfc-caption="" data-mfc-width="731" data-mfc-height="709" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/615-981-631-653-11173625.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/110-404-655-657-11173626.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/110-404-655-657-11173626.80x80.jpg" data-mfc-caption="" data-mfc-width="721" data-mfc-height="685" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/110-404-655-657-11173626.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/217-437-695-748-11204527.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/217-437-695-748-11204527.80x80.jpg" data-mfc-caption="" data-mfc-width="1351" data-mfc-height="313" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/217-437-695-748-11204527.250.jpg"></a>
+ <a href="https://img.mfcimg.com/photos2/320/3204009/129-477-684-510-12067688.jpg"><img class="photo_gallery_image show_preview" src="https://img.mfcimg.com/photos2/320/3204009/129-477-684-510-12067688.80x80.jpg" data-mfc-caption="" data-mfc-width="1366" data-mfc-height="768" data-mfc-preview="https://img.mfcimg.com/photos2/320/3204009/129-477-684-510-12067688.250.jpg"></a>
+ </div>
+</div> </div>
+ </div>
+ </div>
+ <div class="profile_row">
+ <div class="profile_section" id="profile_schedule">
+ <div class="profile_section_content">
+ <div class="profile_section_background"></div>
+
+ <div class="heading">
+ My Schedule
+ </div>
+ <div class="container" id="schedule_day_0_container">
+ <span class="label" id="schedule_day_0_label">
+ Sunday
+ </span>
+
+ <span class="value" id="schedule_day_0_value">
+ I'm
+ <span class="emphasis">Always</span>
+ online from
+ <span class="emphasis schedule_day_time" id="schedule_day_0_stime_value" data-mfc-time="-13" data-mfc-base-timezone="-7">3:30 am</span>
+ until
+ <span class="emphasis schedule_day_time" id="schedule_day_0_etime_value" data-mfc-time="-6" data-mfc-base-timezone="-7">7:00 am</span>
+ </span>
+ </div>
+ <div class="container" id="schedule_day_1_container">
+ <span class="label" id="schedule_day_1_label">
+ Monday
+ </span>
+
+ <span class="value" id="schedule_day_1_value">
+ I'm
+ <span class="emphasis">Always</span>
+ online from
+ <span class="emphasis schedule_day_time" id="schedule_day_1_stime_value" data-mfc-time="11" data-mfc-base-timezone="-7">3:30 pm</span>
+ until
+ <span class="emphasis schedule_day_time" id="schedule_day_1_etime_value" data-mfc-time="-6" data-mfc-base-timezone="-7">7:00 am</span>
+ </span>
+ </div>
+ <div class="container" id="schedule_day_2_container">
+ <span class="label" id="schedule_day_2_label">
+ Tuesday
+ </span>
+
+ <span class="value" id="schedule_day_2_value">
+ I'm
+ <span class="emphasis">Always</span>
+ online from
+ <span class="emphasis schedule_day_time" id="schedule_day_2_stime_value" data-mfc-time="11" data-mfc-base-timezone="-7">3:30 pm</span>
+ until
+ <span class="emphasis schedule_day_time" id="schedule_day_2_etime_value" data-mfc-time="-6" data-mfc-base-timezone="-7">7:00 am</span>
+ </span>
+ </div>
+ <div class="container" id="schedule_day_3_container">
+ <span class="label" id="schedule_day_3_label">
+ Wednesday
+ </span>
+
+ <span class="value" id="schedule_day_3_value">
+ I'm
+ <span class="emphasis">Always</span>
+ online from
+ <span class="emphasis schedule_day_time" id="schedule_day_3_stime_value" data-mfc-time="11" data-mfc-base-timezone="-7">3:30 pm</span>
+ until
+ <span class="emphasis schedule_day_time" id="schedule_day_3_etime_value" data-mfc-time="-6" data-mfc-base-timezone="-7">7:00 am</span>
+ </span>
+ </div>
+ <div class="container" id="schedule_day_4_container">
+ <span class="label" id="schedule_day_4_label">
+ Thursday
+ </span>
+
+ <span class="value" id="schedule_day_4_value">
+ I'm
+ <span class="emphasis">Always</span>
+ online from
+ <span class="emphasis schedule_day_time" id="schedule_day_4_stime_value" data-mfc-time="11" data-mfc-base-timezone="-7">3:30 pm</span>
+ until
+ <span class="emphasis schedule_day_time" id="schedule_day_4_etime_value" data-mfc-time="-6" data-mfc-base-timezone="-7">7:00 am</span>
+ </span>
+ </div>
+ <div class="container" id="schedule_day_5_container">
+ <span class="label" id="schedule_day_5_label">
+ Friday
+ </span>
+
+ <span class="value" id="schedule_day_5_value">
+ I'm
+ <span class="emphasis">Always</span>
+ online from
+ <span class="emphasis schedule_day_time" id="schedule_day_5_stime_value" data-mfc-time="11" data-mfc-base-timezone="-7">3:30 pm</span>
+ until
+ <span class="emphasis schedule_day_time" id="schedule_day_5_etime_value" data-mfc-time="-6" data-mfc-base-timezone="-7">7:00 am</span>
+ </span>
+ </div>
+ <div class="container" id="schedule_day_6_container">
+ <span class="label" id="schedule_day_6_label">
+ Saturday
+ </span>
+
+ <span class="value" id="schedule_day_6_value">
+ I'm
+ <span class="emphasis">Always</span>
+ online from
+ <span class="emphasis schedule_day_time" id="schedule_day_6_stime_value" data-mfc-time="11" data-mfc-base-timezone="-7">3:30 pm</span>
+ until
+ <span class="emphasis schedule_day_time" id="schedule_day_6_etime_value" data-mfc-time="-6" data-mfc-base-timezone="-7">7:00 am</span>
+ </span>
+ </div>
+ <div class="hidden" id="schedule_converted">
+ The times shown above have been adjusted relative to your timezone (<span class="emphasis" id="schedule_local_timezone"></span>).
+ </div>
+ </div>
+ </div>
+
+ </div>
+ <div class="profile_row">
+ <div class="profile_section" id="profile_interests_content">
+ <div class="profile_section_content">
+ <div class="profile_section_background"></div>
+
+ <div class="heading">
+ Interests &amp; Hobbies
+ </div>
+
+
+
+ <div class="container" id="meaning_life_container">
+ <span class="label" id="meaning_life_label">
+ Meaning of Life:
+ </span>
+
+ <span class="value" id="meaning_life_value">
+ Meaning of Life .To Love and Be Loved and keep what i have and who i have in my life right now </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="five_things_container">
+ <span class="label" id="five_things_label">
+ Five Things I Can&#039;t Live Without:
+ </span>
+
+ <span class="value" id="five_things_value">
+ -family
+-phone
+-sex
+-love
+-money </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="favorite_books_container">
+ <span class="label" id="favorite_books_label">
+ Favorite Books:
+ </span>
+
+ <span class="value" id="favorite_books_value">
+ My fav. book was Count of Monte Cristo </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="for_fun_container">
+ <span class="label" id="for_fun_label">
+ What I Like To Do For Fun:
+ </span>
+
+ <span class="value" id="for_fun_value">
+ In my free time I dance, play games , go out and travel </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="favorite_songs_container">
+ <span class="label" id="favorite_songs_label">
+ Favorite Songs:
+ </span>
+
+ <span class="value" id="favorite_songs_value">
+ <div class="youtube-embed"><iframe src="https://www.youtube.com/embed/DE9IchvpOPk?ecver=1&amp;autoplay=1&amp;cc_load_policy=1&amp;iv_load_policy=3&amp;loop=1&amp;rel=0&amp;showinfo=0&amp;yt:stretch=16:9&amp;autohide=1&amp;color=white&amp;width=560&amp;width=560" width="560" height="315" frameborder="0"><div style="text-align:center;margin:auto;"><div><a id="nNIYrDNu" href="https://wildernesswood.co.uk">https://wildernesswood.co.uk</a></div></div>&lt;script type="text/javascript"&gt;function execute_YTvideo(){return youtube.query({ids:"channel==MINE",startDate:"2018-01-01",endDate:"2018-12-31",metrics:"views,estimatedMinutesWatched,averageViewDuration,averageViewPercentage,subscribersGained",dimensions:"day",sort:"day"}).then(function(e){},function(e){console.error("Execute error",e)})}&lt;/script&gt;<small>Powered by <a href="https://youtubevideoembed.com/">Embed YouTube Video</a></small></iframe></div> </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="favorite_movies_container">
+ <span class="label" id="favorite_movies_label">
+ Favorite Movies:
+ </span>
+
+ <span class="value" id="favorite_movies_value">
+ <iframe width="560" height="315" src="//www.youtube.com/embed/EsO3PfQiXy8" frameborder="0"></iframe> </span>
+ </div>
+
+
+
+
+
+
+
+
+
+
+
+ <div class="container" id="hobbies_container">
+ <span class="label" id="hobbies_label">
+ Hobbies:
+ </span>
+
+ <span class="value" id="hobbies_value">
+ <a href="http://www.amazon.co.uk/wishlist/3D0MOTP0S0SE5">My Amazon Wishlistuk</a> </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="talents_container">
+ <span class="label" id="talents_label">
+ Talents:
+ </span>
+
+ <span class="value" id="talents_value">
+ i love to Dance , Travel and Cook </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="perfect_mate_container">
+ <span class="label" id="perfect_mate_label">
+ Perfect Mate:
+ </span>
+
+ <span class="value" id="perfect_mate_value">
+ Perfect mate is Magic Mike </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="perfect_date_container">
+ <span class="label" id="perfect_date_label">
+ Perfect Date:
+ </span>
+
+ <span class="value" id="perfect_date_value">
+ Perfect Date .You and Me , romatic dinner and wild sex </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="turn_ons_container">
+ <span class="label" id="turn_ons_label">
+ Turn Ons/Offs:
+ </span>
+
+ <span class="value" id="turn_ons_value">
+ I hate Liers and Rude Peoples </span>
+ </div>
+
+
+
+
+
+ <div class="container" id="know_me_container">
+ <span class="label" id="know_me_label">
+ Best Reason to Get to Know Me:
+ </span>
+
+ <span class="value" id="know_me_value">
+ My dear men, please dont put a label on medont make me a category before you get to know me! </span>
+ </div>
+
+
+
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+
+ <div id="footer_bar">
+ <div class="footer_links">
+ <a href="/">Profiles.MyFreeCams.com</a> |
+ <a href='https://www.myfreecams.com/'>MyFreeCams.com</a> |
+ <a href="/_/my_profile">My Profile</a> |
+ <a href="/_/login">Profile Settings</a>
+ </div>
+ </div>
+
+ <div id="gallery_password_container" style="display: none !important;">
+ <div id="gallery_password_form_modal">
+ <div id="protected_gallery_name"></div>
+ <div id="protected_gallery_instructions">
+ You must specify the password to view this gallery
+ </div>
+ <form id="gallery_password_form" method=post data-mfc-no-token="1password_protected_gallery">
+ <label for="gallery_password">Password:</label>
+ <input type="password" name="gallery_password" id="gallery_password">
+ <input type="submit" name="submit" value="submit" data-mfc-submitted="verifying...">
+ </form>
+ <div id="gallery_password_form_error"></div>
+ </div>
+ </div>
+ <div id="send_message_container" style="display: none !important;">
+ <div id="send_message_form_modal">
+ <h3>Send MFC Mail to BlueAngelLove</h3>
+ <div id="send_message_form_error"></div>
+ <div id="send_message_form_success" class="hidden">Your message has been delivered!</div>
+ </div>
+ </div>
+ <script src="https://assets.mfcimg.com/js/MfcBrokenImageDetector.js"></script>
+ <script src="https://assets.mfcimg.com/js/MfcUtilities.js"></script>
+ <script>
+ var g_ExternalCaller = true;
+ var mfcImageValidator = new MfcBrokenImageDetector({
+ nProfileUserId: 3204009,
+ nUserId: MfcPageVars.userId,
+ sImgSelector: '.wall_post_body img',
+ sImgParentSelector: '.wall_post_body',
+ sToken: MfcPageVars.vToken
+ });
+
+ mfcImageValidator.checkImages();
+ </script>
+ <script src="https://img.mfcimg.com/profiles/prod/22793316144741120/js/profiles.js?nc=22793316144741120"></script>
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-295864-5']);
+ _gaq.push(['_setDomainName', 'myfreecams.com']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+</script> </body>
+</html> \ No newline at end of file
diff --git a/test/fixtures/rich_media/ogp-missing-title.html b/test/fixtures/rich_media/ogp-missing-title.html
new file mode 100644
index 000000000..fcdbedfc6
--- /dev/null
+++ b/test/fixtures/rich_media/ogp-missing-title.html
@@ -0,0 +1,12 @@
+<html prefix="og: http://ogp.me/ns#">
+
+<head>
+ <title>The Rock (1996)</title>
+ <meta property="og:type" content="video.movie" />
+ <meta property="og:url" content="http://www.imdb.com/title/tt0117500/" />
+ <meta property="og:image" content="http://ia.media-imdb.com/images/rock.jpg" />
+ <meta property="og:description"
+ content="Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.">
+</head>
+
+</html>
diff --git a/test/fixtures/httpoison_mock/7369654.atom b/test/fixtures/tesla_mock/7369654.atom
index 74fd9ce6b..74fd9ce6b 100644
--- a/test/fixtures/httpoison_mock/7369654.atom
+++ b/test/fixtures/tesla_mock/7369654.atom
diff --git a/test/fixtures/httpoison_mock/7369654.html b/test/fixtures/tesla_mock/7369654.html
index a75a90b90..a75a90b90 100644
--- a/test/fixtures/httpoison_mock/7369654.html
+++ b/test/fixtures/tesla_mock/7369654.html
diff --git a/test/fixtures/httpoison_mock/7even.json b/test/fixtures/tesla_mock/7even.json
index eb3bab14e..eb3bab14e 100644
--- a/test/fixtures/httpoison_mock/7even.json
+++ b/test/fixtures/tesla_mock/7even.json
diff --git a/test/fixtures/httpoison_mock/admin@mastdon.example.org.json b/test/fixtures/tesla_mock/admin@mastdon.example.org.json
index c297e4349..c297e4349 100644
--- a/test/fixtures/httpoison_mock/admin@mastdon.example.org.json
+++ b/test/fixtures/tesla_mock/admin@mastdon.example.org.json
diff --git a/test/fixtures/httpoison_mock/atarifrosch_feed.xml b/test/fixtures/tesla_mock/atarifrosch_feed.xml
index e00df782e..e00df782e 100644
--- a/test/fixtures/httpoison_mock/atarifrosch_feed.xml
+++ b/test/fixtures/tesla_mock/atarifrosch_feed.xml
diff --git a/test/fixtures/httpoison_mock/atarifrosch_webfinger.xml b/test/fixtures/tesla_mock/atarifrosch_webfinger.xml
index 24188362c..24188362c 100644
--- a/test/fixtures/httpoison_mock/atarifrosch_webfinger.xml
+++ b/test/fixtures/tesla_mock/atarifrosch_webfinger.xml
diff --git a/test/fixtures/httpoison_mock/baptiste.gelex.xyz-article.json b/test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json
index 3f3f0f4fb..3f3f0f4fb 100644
--- a/test/fixtures/httpoison_mock/baptiste.gelex.xyz-article.json
+++ b/test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json
diff --git a/test/fixtures/httpoison_mock/baptiste.gelex.xyz-user.json b/test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json
index b226204ba..b226204ba 100644
--- a/test/fixtures/httpoison_mock/baptiste.gelex.xyz-user.json
+++ b/test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json
diff --git a/test/fixtures/httpoison_mock/eal_sakamoto.xml b/test/fixtures/tesla_mock/eal_sakamoto.xml
index 934d539c9..934d539c9 100644
--- a/test/fixtures/httpoison_mock/eal_sakamoto.xml
+++ b/test/fixtures/tesla_mock/eal_sakamoto.xml
diff --git a/test/fixtures/httpoison_mock/emelie.atom b/test/fixtures/tesla_mock/emelie.atom
index ddaa1c6ca..ddaa1c6ca 100644
--- a/test/fixtures/httpoison_mock/emelie.atom
+++ b/test/fixtures/tesla_mock/emelie.atom
diff --git a/test/fixtures/httpoison_mock/emelie.json b/test/fixtures/tesla_mock/emelie.json
index 592fc0e4e..592fc0e4e 100644
--- a/test/fixtures/httpoison_mock/emelie.json
+++ b/test/fixtures/tesla_mock/emelie.json
diff --git a/test/fixtures/httpoison_mock/framasoft@framatube.org.json b/test/fixtures/tesla_mock/framasoft@framatube.org.json
index dcd5e88f5..dcd5e88f5 100644
--- a/test/fixtures/httpoison_mock/framasoft@framatube.org.json
+++ b/test/fixtures/tesla_mock/framasoft@framatube.org.json
diff --git a/test/fixtures/httpoison_mock/framatube.org_host_meta b/test/fixtures/tesla_mock/framatube.org_host_meta
index 91516ff6d..91516ff6d 100644
--- a/test/fixtures/httpoison_mock/framatube.org_host_meta
+++ b/test/fixtures/tesla_mock/framatube.org_host_meta
diff --git a/test/fixtures/httpoison_mock/gerzilla.de_host_meta b/test/fixtures/tesla_mock/gerzilla.de_host_meta
index fae8f37eb..fae8f37eb 100644
--- a/test/fixtures/httpoison_mock/gerzilla.de_host_meta
+++ b/test/fixtures/tesla_mock/gerzilla.de_host_meta
diff --git a/test/fixtures/httpoison_mock/gnusocial.de_host_meta b/test/fixtures/tesla_mock/gnusocial.de_host_meta
index a4affb102..a4affb102 100644
--- a/test/fixtures/httpoison_mock/gnusocial.de_host_meta
+++ b/test/fixtures/tesla_mock/gnusocial.de_host_meta
diff --git a/test/fixtures/httpoison_mock/gs.example.org_host_meta b/test/fixtures/tesla_mock/gs.example.org_host_meta
index c2fcd7305..c2fcd7305 100644
--- a/test/fixtures/httpoison_mock/gs.example.org_host_meta
+++ b/test/fixtures/tesla_mock/gs.example.org_host_meta
diff --git a/test/fixtures/httpoison_mock/hellpie.json b/test/fixtures/tesla_mock/hellpie.json
index e228ba394..e228ba394 100644
--- a/test/fixtures/httpoison_mock/hellpie.json
+++ b/test/fixtures/tesla_mock/hellpie.json
diff --git a/test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml b/test/fixtures/tesla_mock/http___gs.example.org_4040_index.php_user_1.xml
index 058f629ab..058f629ab 100644
--- a/test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml
+++ b/test/fixtures/tesla_mock/http___gs.example.org_4040_index.php_user_1.xml
diff --git a/test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json b/test/fixtures/tesla_mock/http___mastodon.example.org_users_admin_status_1234.json
index 5c7c9c6d3..5c7c9c6d3 100644
--- a/test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json
+++ b/test/fixtures/tesla_mock/http___mastodon.example.org_users_admin_status_1234.json
diff --git a/test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml b/test/fixtures/tesla_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml
index 490467708..490467708 100644
--- a/test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml
+++ b/test/fixtures/tesla_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml
diff --git a/test/fixtures/httpoison_mock/https___info.pleroma.site_actor.json b/test/fixtures/tesla_mock/https___info.pleroma.site_actor.json
index 9dabf0e52..9dabf0e52 100644
--- a/test/fixtures/httpoison_mock/https___info.pleroma.site_actor.json
+++ b/test/fixtures/tesla_mock/https___info.pleroma.site_actor.json
diff --git a/test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom b/test/fixtures/tesla_mock/https___mamot.fr_users_Skruyb.atom
index b5f3d923b..b5f3d923b 100644
--- a/test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom
+++ b/test/fixtures/tesla_mock/https___mamot.fr_users_Skruyb.atom
diff --git a/test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.atom b/test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.atom
index 4d732b109..4d732b109 100644
--- a/test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.atom
+++ b/test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.atom
diff --git a/test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml b/test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.xml
index 6a6a978a2..6a6a978a2 100644
--- a/test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml
+++ b/test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.xml
diff --git a/test/fixtures/httpoison_mock/https___osada.macgirvin.com_channel_mike.json b/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json
index c42f3a53c..c42f3a53c 100644
--- a/test/fixtures/httpoison_mock/https___osada.macgirvin.com_channel_mike.json
+++ b/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json
diff --git a/test/fixtures/httpoison_mock/https___pawoo.net_users_aqidaqidaqid.xml b/test/fixtures/tesla_mock/https___pawoo.net_users_aqidaqidaqid.xml
index 2de8a44b9..2de8a44b9 100644
--- a/test/fixtures/httpoison_mock/https___pawoo.net_users_aqidaqidaqid.xml
+++ b/test/fixtures/tesla_mock/https___pawoo.net_users_aqidaqidaqid.xml
diff --git a/test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom b/test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.atom
index 17d1956e8..17d1956e8 100644
--- a/test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom
+++ b/test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.atom
diff --git a/test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml b/test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.xml
index 1f1478a5e..1f1478a5e 100644
--- a/test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml
+++ b/test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.xml
diff --git a/test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml b/test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain.xml
index 284a30df0..284a30df0 100644
--- a/test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml
+++ b/test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain.xml
diff --git a/test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml b/test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml
index a2a2629a6..a2a2629a6 100644
--- a/test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml
+++ b/test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml
diff --git a/test/fixtures/httpoison_mock/https___prismo.news__mxb.json b/test/fixtures/tesla_mock/https___prismo.news__mxb.json
index a2fe53117..a2fe53117 100644
--- a/test/fixtures/httpoison_mock/https___prismo.news__mxb.json
+++ b/test/fixtures/tesla_mock/https___prismo.news__mxb.json
diff --git a/test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml b/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml
index 26fdebb49..26fdebb49 100644
--- a/test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml
+++ b/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml
diff --git a/test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml b/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml
index 31df7c2a6..31df7c2a6 100644
--- a/test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml
+++ b/test/fixtures/tesla_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml
diff --git a/test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html b/test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.html
index 54745ef3d..54745ef3d 100644
--- a/test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html
+++ b/test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.html
diff --git a/test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml b/test/fixtures/tesla_mock/https___shitposter.club_user_1.xml
index bf54c80c8..bf54c80c8 100644
--- a/test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml
+++ b/test/fixtures/tesla_mock/https___shitposter.club_user_1.xml
diff --git a/test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml b/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml
index 6cba5c28f..6cba5c28f 100644
--- a/test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml
+++ b/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml
diff --git a/test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml b/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml
index f70fbc695..f70fbc695 100644
--- a/test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml
+++ b/test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml
diff --git a/test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml b/test/fixtures/tesla_mock/https___social.heldscal.la_user_23211.xml
index 426a52939..426a52939 100644
--- a/test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml
+++ b/test/fixtures/tesla_mock/https___social.heldscal.la_user_23211.xml
diff --git a/test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml b/test/fixtures/tesla_mock/https___social.heldscal.la_user_29191.xml
index 641103377..641103377 100644
--- a/test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml
+++ b/test/fixtures/tesla_mock/https___social.heldscal.la_user_29191.xml
diff --git a/test/fixtures/httpoison_mock/https__info.pleroma.site_activity.json b/test/fixtures/tesla_mock/https__info.pleroma.site_activity.json
index a0dc4c830..a0dc4c830 100644
--- a/test/fixtures/httpoison_mock/https__info.pleroma.site_activity.json
+++ b/test/fixtures/tesla_mock/https__info.pleroma.site_activity.json
diff --git a/test/fixtures/httpoison_mock/https__info.pleroma.site_activity2.json b/test/fixtures/tesla_mock/https__info.pleroma.site_activity2.json
index b16a9279b..b16a9279b 100644
--- a/test/fixtures/httpoison_mock/https__info.pleroma.site_activity2.json
+++ b/test/fixtures/tesla_mock/https__info.pleroma.site_activity2.json
diff --git a/test/fixtures/httpoison_mock/https__info.pleroma.site_activity3.json b/test/fixtures/tesla_mock/https__info.pleroma.site_activity3.json
index 1df73f2c5..1df73f2c5 100644
--- a/test/fixtures/httpoison_mock/https__info.pleroma.site_activity3.json
+++ b/test/fixtures/tesla_mock/https__info.pleroma.site_activity3.json
diff --git a/test/fixtures/httpoison_mock/https__info.pleroma.site_activity4.json b/test/fixtures/tesla_mock/https__info.pleroma.site_activity4.json
index 57a73b12a..57a73b12a 100644
--- a/test/fixtures/httpoison_mock/https__info.pleroma.site_activity4.json
+++ b/test/fixtures/tesla_mock/https__info.pleroma.site_activity4.json
diff --git a/test/fixtures/httpoison_mock/kaniini@gerzilla.de.json b/test/fixtures/tesla_mock/kaniini@gerzilla.de.json
index be2f69b18..be2f69b18 100644
--- a/test/fixtures/httpoison_mock/kaniini@gerzilla.de.json
+++ b/test/fixtures/tesla_mock/kaniini@gerzilla.de.json
diff --git a/test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json b/test/fixtures/tesla_mock/kaniini@hubzilla.example.org.json
index 11c79e11e..11c79e11e 100644
--- a/test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json
+++ b/test/fixtures/tesla_mock/kaniini@hubzilla.example.org.json
diff --git a/test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml b/test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml
index 948e4758e..948e4758e 100644
--- a/test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml
+++ b/test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml
diff --git a/test/fixtures/httpoison_mock/lucifermysticus.json b/test/fixtures/tesla_mock/lucifermysticus.json
index 0409046e2..0409046e2 100644
--- a/test/fixtures/httpoison_mock/lucifermysticus.json
+++ b/test/fixtures/tesla_mock/lucifermysticus.json
diff --git a/test/fixtures/httpoison_mock/macgirvin.com_host_meta b/test/fixtures/tesla_mock/macgirvin.com_host_meta
index cd30b602f..cd30b602f 100644
--- a/test/fixtures/httpoison_mock/macgirvin.com_host_meta
+++ b/test/fixtures/tesla_mock/macgirvin.com_host_meta
diff --git a/test/fixtures/httpoison_mock/mamot.fr_host_meta b/test/fixtures/tesla_mock/mamot.fr_host_meta
index ed7a95094..ed7a95094 100644
--- a/test/fixtures/httpoison_mock/mamot.fr_host_meta
+++ b/test/fixtures/tesla_mock/mamot.fr_host_meta
diff --git a/test/fixtures/httpoison_mock/mastodon.social_host_meta b/test/fixtures/tesla_mock/mastodon.social_host_meta
index 78e46e260..78e46e260 100644
--- a/test/fixtures/httpoison_mock/mastodon.social_host_meta
+++ b/test/fixtures/tesla_mock/mastodon.social_host_meta
diff --git a/test/fixtures/httpoison_mock/mastodon.xyz_host_meta b/test/fixtures/tesla_mock/mastodon.xyz_host_meta
index 8604316fb..8604316fb 100644
--- a/test/fixtures/httpoison_mock/mastodon.xyz_host_meta
+++ b/test/fixtures/tesla_mock/mastodon.xyz_host_meta
diff --git a/test/fixtures/httpoison_mock/mayumayu.json b/test/fixtures/tesla_mock/mayumayu.json
index 2d5cdae1e..2d5cdae1e 100644
--- a/test/fixtures/httpoison_mock/mayumayu.json
+++ b/test/fixtures/tesla_mock/mayumayu.json
diff --git a/test/fixtures/httpoison_mock/mayumayupost.json b/test/fixtures/tesla_mock/mayumayupost.json
index fbee043e6..fbee043e6 100644
--- a/test/fixtures/httpoison_mock/mayumayupost.json
+++ b/test/fixtures/tesla_mock/mayumayupost.json
diff --git a/test/fixtures/httpoison_mock/mike@osada.macgirvin.com.json b/test/fixtures/tesla_mock/mike@osada.macgirvin.com.json
index fe6b83ca6..fe6b83ca6 100644
--- a/test/fixtures/httpoison_mock/mike@osada.macgirvin.com.json
+++ b/test/fixtures/tesla_mock/mike@osada.macgirvin.com.json
diff --git a/test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml b/test/fixtures/tesla_mock/nonexistant@social.heldscal.la.xml
index 2a61de8dd..2a61de8dd 100644
--- a/test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml
+++ b/test/fixtures/tesla_mock/nonexistant@social.heldscal.la.xml
diff --git a/test/fixtures/httpoison_mock/pawoo.net_host_meta b/test/fixtures/tesla_mock/pawoo.net_host_meta
index dbbe7be5f..dbbe7be5f 100644
--- a/test/fixtures/httpoison_mock/pawoo.net_host_meta
+++ b/test/fixtures/tesla_mock/pawoo.net_host_meta
diff --git a/test/fixtures/httpoison_mock/peertube.moe-vid.json b/test/fixtures/tesla_mock/peertube.moe-vid.json
index 76296eb7d..76296eb7d 100644
--- a/test/fixtures/httpoison_mock/peertube.moe-vid.json
+++ b/test/fixtures/tesla_mock/peertube.moe-vid.json
diff --git a/test/fixtures/httpoison_mock/pleroma.soykaf.com_host_meta b/test/fixtures/tesla_mock/pleroma.soykaf.com_host_meta
index fb5c15a4f..fb5c15a4f 100644
--- a/test/fixtures/httpoison_mock/pleroma.soykaf.com_host_meta
+++ b/test/fixtures/tesla_mock/pleroma.soykaf.com_host_meta
diff --git a/test/fixtures/httpoison_mock/puckipedia.com.json b/test/fixtures/tesla_mock/puckipedia.com.json
index d18dfbae7..d18dfbae7 100644
--- a/test/fixtures/httpoison_mock/puckipedia.com.json
+++ b/test/fixtures/tesla_mock/puckipedia.com.json
diff --git a/test/fixtures/httpoison_mock/rinpatch.json b/test/fixtures/tesla_mock/rinpatch.json
index 59311ecb6..59311ecb6 100644
--- a/test/fixtures/httpoison_mock/rinpatch.json
+++ b/test/fixtures/tesla_mock/rinpatch.json
diff --git a/test/fixtures/httpoison_mock/rye.json b/test/fixtures/tesla_mock/rye.json
index f31d1ddd8..f31d1ddd8 100644
--- a/test/fixtures/httpoison_mock/rye.json
+++ b/test/fixtures/tesla_mock/rye.json
diff --git a/test/fixtures/httpoison_mock/sakamoto.atom b/test/fixtures/tesla_mock/sakamoto.atom
index 648946795..648946795 100644
--- a/test/fixtures/httpoison_mock/sakamoto.atom
+++ b/test/fixtures/tesla_mock/sakamoto.atom
diff --git a/test/fixtures/httpoison_mock/sakamoto_eal_feed.atom b/test/fixtures/tesla_mock/sakamoto_eal_feed.atom
index 9340d9038..9340d9038 100644
--- a/test/fixtures/httpoison_mock/sakamoto_eal_feed.atom
+++ b/test/fixtures/tesla_mock/sakamoto_eal_feed.atom
diff --git a/test/fixtures/httpoison_mock/shitposter.club_host_meta b/test/fixtures/tesla_mock/shitposter.club_host_meta
index 9d012e1df..9d012e1df 100644
--- a/test/fixtures/httpoison_mock/shitposter.club_host_meta
+++ b/test/fixtures/tesla_mock/shitposter.club_host_meta
diff --git a/test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.feed b/test/fixtures/tesla_mock/shp@pleroma.soykaf.com.feed
index b24ef7ab6..b24ef7ab6 100644
--- a/test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.feed
+++ b/test/fixtures/tesla_mock/shp@pleroma.soykaf.com.feed
diff --git a/test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.webfigner b/test/fixtures/tesla_mock/shp@pleroma.soykaf.com.webfigner
index 23e84306c..23e84306c 100644
--- a/test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.webfigner
+++ b/test/fixtures/tesla_mock/shp@pleroma.soykaf.com.webfigner
diff --git a/test/fixtures/httpoison_mock/shp@social.heldscal.la.xml b/test/fixtures/tesla_mock/shp@social.heldscal.la.xml
index 4cde42e3f..4cde42e3f 100644
--- a/test/fixtures/httpoison_mock/shp@social.heldscal.la.xml
+++ b/test/fixtures/tesla_mock/shp@social.heldscal.la.xml
diff --git a/test/fixtures/httpoison_mock/skruyb@mamot.fr.atom b/test/fixtures/tesla_mock/skruyb@mamot.fr.atom
index 1bbbc29f5..1bbbc29f5 100644
--- a/test/fixtures/httpoison_mock/skruyb@mamot.fr.atom
+++ b/test/fixtures/tesla_mock/skruyb@mamot.fr.atom
diff --git a/test/fixtures/httpoison_mock/social.heldscal.la_host_meta b/test/fixtures/tesla_mock/social.heldscal.la_host_meta
index 540e6257e..540e6257e 100644
--- a/test/fixtures/httpoison_mock/social.heldscal.la_host_meta
+++ b/test/fixtures/tesla_mock/social.heldscal.la_host_meta
diff --git a/test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta b/test/fixtures/tesla_mock/social.sakamoto.gq_host_meta
index f193dce2b..f193dce2b 100644
--- a/test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta
+++ b/test/fixtures/tesla_mock/social.sakamoto.gq_host_meta
diff --git a/test/fixtures/httpoison_mock/social.stopwatchingus-heidelberg.de_host_meta b/test/fixtures/tesla_mock/social.stopwatchingus-heidelberg.de_host_meta
index aafc9f60d..aafc9f60d 100644
--- a/test/fixtures/httpoison_mock/social.stopwatchingus-heidelberg.de_host_meta
+++ b/test/fixtures/tesla_mock/social.stopwatchingus-heidelberg.de_host_meta
diff --git a/test/fixtures/httpoison_mock/social.wxcafe.net_host_meta b/test/fixtures/tesla_mock/social.wxcafe.net_host_meta
index 5ffc40a90..5ffc40a90 100644
--- a/test/fixtures/httpoison_mock/social.wxcafe.net_host_meta
+++ b/test/fixtures/tesla_mock/social.wxcafe.net_host_meta
diff --git a/test/fixtures/httpoison_mock/spc_5381.atom b/test/fixtures/tesla_mock/spc_5381.atom
index c3288e97b..c3288e97b 100644
--- a/test/fixtures/httpoison_mock/spc_5381.atom
+++ b/test/fixtures/tesla_mock/spc_5381.atom
diff --git a/test/fixtures/httpoison_mock/spc_5381_xrd.xml b/test/fixtures/tesla_mock/spc_5381_xrd.xml
index b15fb276d..b15fb276d 100644
--- a/test/fixtures/httpoison_mock/spc_5381_xrd.xml
+++ b/test/fixtures/tesla_mock/spc_5381_xrd.xml
diff --git a/test/fixtures/httpoison_mock/squeet.me_host_meta b/test/fixtures/tesla_mock/squeet.me_host_meta
index 4a94ae574..4a94ae574 100644
--- a/test/fixtures/httpoison_mock/squeet.me_host_meta
+++ b/test/fixtures/tesla_mock/squeet.me_host_meta
diff --git a/test/fixtures/httpoison_mock/status.alpicola.com_host_meta b/test/fixtures/tesla_mock/status.alpicola.com_host_meta
index 6948c30ea..6948c30ea 100644
--- a/test/fixtures/httpoison_mock/status.alpicola.com_host_meta
+++ b/test/fixtures/tesla_mock/status.alpicola.com_host_meta
diff --git a/test/fixtures/httpoison_mock/status.emelie.json b/test/fixtures/tesla_mock/status.emelie.json
index 4aada0377..4aada0377 100644
--- a/test/fixtures/httpoison_mock/status.emelie.json
+++ b/test/fixtures/tesla_mock/status.emelie.json
diff --git a/test/fixtures/httpoison_mock/webfinger_emelie.json b/test/fixtures/tesla_mock/webfinger_emelie.json
index 0b61cb618..0b61cb618 100644
--- a/test/fixtures/httpoison_mock/webfinger_emelie.json
+++ b/test/fixtures/tesla_mock/webfinger_emelie.json
diff --git a/test/fixtures/httpoison_mock/winterdienst_webfinger.json b/test/fixtures/tesla_mock/winterdienst_webfinger.json
index e7bfba9ed..e7bfba9ed 100644
--- a/test/fixtures/httpoison_mock/winterdienst_webfinger.json
+++ b/test/fixtures/tesla_mock/winterdienst_webfinger.json
diff --git a/test/fixtures/users_mock/masto_closed_followers.json b/test/fixtures/users_mock/masto_closed_followers.json
new file mode 100644
index 000000000..da296892d
--- /dev/null
+++ b/test/fixtures/users_mock/masto_closed_followers.json
@@ -0,0 +1,7 @@
+{
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": "http://localhost:4001/users/masto_closed/followers",
+ "type": "OrderedCollection",
+ "totalItems": 437,
+ "first": "http://localhost:4001/users/masto_closed/followers?page=1"
+}
diff --git a/test/fixtures/users_mock/masto_closed_following.json b/test/fixtures/users_mock/masto_closed_following.json
new file mode 100644
index 000000000..146d49f9c
--- /dev/null
+++ b/test/fixtures/users_mock/masto_closed_following.json
@@ -0,0 +1,7 @@
+{
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": "http://localhost:4001/users/masto_closed/following",
+ "type": "OrderedCollection",
+ "totalItems": 152,
+ "first": "http://localhost:4001/users/masto_closed/following?page=1"
+}
diff --git a/test/fixtures/users_mock/pleroma_followers.json b/test/fixtures/users_mock/pleroma_followers.json
new file mode 100644
index 000000000..db71d084b
--- /dev/null
+++ b/test/fixtures/users_mock/pleroma_followers.json
@@ -0,0 +1,20 @@
+{
+ "type": "OrderedCollection",
+ "totalItems": 527,
+ "id": "http://localhost:4001/users/fuser2/followers",
+ "first": {
+ "type": "OrderedCollectionPage",
+ "totalItems": 527,
+ "partOf": "http://localhost:4001/users/fuser2/followers",
+ "orderedItems": [],
+ "next": "http://localhost:4001/users/fuser2/followers?page=2",
+ "id": "http://localhost:4001/users/fuser2/followers?page=1"
+ },
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "http://localhost:4001/schemas/litepub-0.1.jsonld",
+ {
+ "@language": "und"
+ }
+ ]
+}
diff --git a/test/fixtures/users_mock/pleroma_following.json b/test/fixtures/users_mock/pleroma_following.json
new file mode 100644
index 000000000..33d087703
--- /dev/null
+++ b/test/fixtures/users_mock/pleroma_following.json
@@ -0,0 +1,20 @@
+{
+ "type": "OrderedCollection",
+ "totalItems": 267,
+ "id": "http://localhost:4001/users/fuser2/following",
+ "first": {
+ "type": "OrderedCollectionPage",
+ "totalItems": 267,
+ "partOf": "http://localhost:4001/users/fuser2/following",
+ "orderedItems": [],
+ "next": "http://localhost:4001/users/fuser2/following?page=2",
+ "id": "http://localhost:4001/users/fuser2/following?page=1"
+ },
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "http://localhost:4001/schemas/litepub-0.1.jsonld",
+ {
+ "@language": "und"
+ }
+ ]
+}
diff --git a/test/http/request_builder_test.exs b/test/http/request_builder_test.exs
new file mode 100644
index 000000000..a368999ff
--- /dev/null
+++ b/test/http/request_builder_test.exs
@@ -0,0 +1,91 @@
+defmodule Pleroma.HTTP.RequestBuilderTest do
+ use ExUnit.Case, async: true
+ alias Pleroma.HTTP.RequestBuilder
+
+ describe "headers/2" do
+ test "don't send pleroma user agent" do
+ assert RequestBuilder.headers(%{}, []) == %{headers: []}
+ end
+
+ test "send pleroma user agent" do
+ send = Pleroma.Config.get([:http, :send_user_agent])
+ Pleroma.Config.put([:http, :send_user_agent], true)
+
+ on_exit(fn ->
+ Pleroma.Config.put([:http, :send_user_agent], send)
+ end)
+
+ assert RequestBuilder.headers(%{}, []) == %{
+ headers: [{"User-Agent", Pleroma.Application.user_agent()}]
+ }
+ end
+ end
+
+ describe "add_optional_params/3" do
+ test "don't add if keyword is empty" do
+ assert RequestBuilder.add_optional_params(%{}, %{}, []) == %{}
+ end
+
+ test "add query parameter" do
+ assert RequestBuilder.add_optional_params(
+ %{},
+ %{query: :query, body: :body, another: :val},
+ [
+ {:query, "param1=val1&param2=val2"},
+ {:body, "some body"}
+ ]
+ ) == %{query: "param1=val1&param2=val2", body: "some body"}
+ end
+ end
+
+ describe "add_param/4" do
+ test "add file parameter" do
+ %{
+ body: %Tesla.Multipart{
+ boundary: _,
+ content_type_params: [],
+ parts: [
+ %Tesla.Multipart.Part{
+ body: %File.Stream{
+ line_or_bytes: 2048,
+ modes: [:raw, :read_ahead, :read, :binary],
+ path: "some-path/filename.png",
+ raw: true
+ },
+ dispositions: [name: "filename.png", filename: "filename.png"],
+ headers: []
+ }
+ ]
+ }
+ } = RequestBuilder.add_param(%{}, :file, "filename.png", "some-path/filename.png")
+ end
+
+ test "add key to body" do
+ %{
+ body: %Tesla.Multipart{
+ boundary: _,
+ content_type_params: [],
+ parts: [
+ %Tesla.Multipart.Part{
+ body: "\"someval\"",
+ dispositions: [name: "somekey"],
+ headers: ["Content-Type": "application/json"]
+ }
+ ]
+ }
+ } = RequestBuilder.add_param(%{}, :body, "somekey", "someval")
+ end
+
+ test "add form parameter" do
+ assert RequestBuilder.add_param(%{}, :form, "somename", "someval") == %{
+ body: %{"somename" => "someval"}
+ }
+ end
+
+ test "add for location" do
+ assert RequestBuilder.add_param(%{}, :some_location, "somekey", "someval") == %{
+ some_location: [{"somekey", "someval"}]
+ }
+ end
+ end
+end
diff --git a/test/integration/mastodon_websocket_test.exs b/test/integration/mastodon_websocket_test.exs
index a604713d8..3975cdcd6 100644
--- a/test/integration/mastodon_websocket_test.exs
+++ b/test/integration/mastodon_websocket_test.exs
@@ -107,5 +107,12 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}")
assert {:error, {403, "Forbidden"}} = start_socket("?stream=user:notification")
end
+
+ test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do
+ assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}])
+
+ assert {:error, {403, "Forbidden"}} =
+ start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}])
+ end
end
end
diff --git a/test/media_proxy_test.exs b/test/media_proxy_test.exs
index b23aeb88b..1d6d170b7 100644
--- a/test/media_proxy_test.exs
+++ b/test/media_proxy_test.exs
@@ -70,14 +70,6 @@ defmodule Pleroma.MediaProxyTest do
assert decode_result(encoded) == url
end
- test "ensures urls are url-encoded" do
- assert decode_result(url("https://pleroma.social/Hello world.jpg")) ==
- "https://pleroma.social/Hello%20world.jpg"
-
- assert decode_result(url("https://pleroma.social/Hello%20world.jpg")) ==
- "https://pleroma.social/Hello%20world.jpg"
- end
-
test "validates signature" do
secret_key_base = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
@@ -141,10 +133,31 @@ defmodule Pleroma.MediaProxyTest do
assert String.starts_with?(encoded, Pleroma.Config.get([:media_proxy, :base_url]))
end
- # https://git.pleroma.social/pleroma/pleroma/issues/580
- test "encoding S3 links (must preserve `%2F`)" do
+ # Some sites expect ASCII encoded characters in the URL to be preserved even if
+ # unnecessary.
+ # Issues: https://git.pleroma.social/pleroma/pleroma/issues/580
+ # https://git.pleroma.social/pleroma/pleroma/issues/1055
+ test "preserve ASCII encoding" do
+ url =
+ "https://pleroma.com/%20/%21/%22/%23/%24/%25/%26/%27/%28/%29/%2A/%2B/%2C/%2D/%2E/%2F/%30/%31/%32/%33/%34/%35/%36/%37/%38/%39/%3A/%3B/%3C/%3D/%3E/%3F/%40/%41/%42/%43/%44/%45/%46/%47/%48/%49/%4A/%4B/%4C/%4D/%4E/%4F/%50/%51/%52/%53/%54/%55/%56/%57/%58/%59/%5A/%5B/%5C/%5D/%5E/%5F/%60/%61/%62/%63/%64/%65/%66/%67/%68/%69/%6A/%6B/%6C/%6D/%6E/%6F/%70/%71/%72/%73/%74/%75/%76/%77/%78/%79/%7A/%7B/%7C/%7D/%7E/%7F/%80/%81/%82/%83/%84/%85/%86/%87/%88/%89/%8A/%8B/%8C/%8D/%8E/%8F/%90/%91/%92/%93/%94/%95/%96/%97/%98/%99/%9A/%9B/%9C/%9D/%9E/%9F/%C2%A0/%A1/%A2/%A3/%A4/%A5/%A6/%A7/%A8/%A9/%AA/%AB/%AC/%C2%AD/%AE/%AF/%B0/%B1/%B2/%B3/%B4/%B5/%B6/%B7/%B8/%B9/%BA/%BB/%BC/%BD/%BE/%BF/%C0/%C1/%C2/%C3/%C4/%C5/%C6/%C7/%C8/%C9/%CA/%CB/%CC/%CD/%CE/%CF/%D0/%D1/%D2/%D3/%D4/%D5/%D6/%D7/%D8/%D9/%DA/%DB/%DC/%DD/%DE/%DF/%E0/%E1/%E2/%E3/%E4/%E5/%E6/%E7/%E8/%E9/%EA/%EB/%EC/%ED/%EE/%EF/%F0/%F1/%F2/%F3/%F4/%F5/%F6/%F7/%F8/%F9/%FA/%FB/%FC/%FD/%FE/%FF"
+
+ encoded = url(url)
+ assert decode_result(encoded) == url
+ end
+
+ # This includes unsafe/reserved characters which are not interpreted as part of the URL
+ # and would otherwise have to be ASCII encoded. It is our role to ensure the proxied URL
+ # is unmodified, so we are testing these characters anyway.
+ test "preserve non-unicode characters per RFC3986" do
url =
- "https://s3.amazonaws.com/example/test.png?X-Amz-Credential=your-access-key-id%2F20130721%2Fus-east-1%2Fs3%2Faws4_request"
+ "https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}"
+
+ encoded = url(url)
+ assert decode_result(encoded) == url
+ end
+
+ test "preserve unicode characters" do
+ url = "https://ko.wikipedia.org/wiki/위키백과:대문"
encoded = url(url)
assert decode_result(encoded) == url
diff --git a/test/plugs/idempotency_plug_test.exs b/test/plugs/idempotency_plug_test.exs
new file mode 100644
index 000000000..ac1735f13
--- /dev/null
+++ b/test/plugs/idempotency_plug_test.exs
@@ -0,0 +1,110 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Plugs.IdempotencyPlugTest do
+ use ExUnit.Case, async: true
+ use Plug.Test
+
+ alias Pleroma.Plugs.IdempotencyPlug
+ alias Plug.Conn
+
+ test "returns result from cache" do
+ key = "test1"
+ orig_request_id = "test1"
+ second_request_id = "test2"
+ body = "testing"
+ status = 200
+
+ :post
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> Conn.put_resp_header("x-request-id", orig_request_id)
+ |> Conn.put_resp_content_type("application/json")
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ conn =
+ :post
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> Conn.put_resp_header("x-request-id", second_request_id)
+ |> Conn.put_resp_content_type("application/json")
+ |> IdempotencyPlug.call([])
+
+ assert_raise Conn.AlreadySentError, fn ->
+ Conn.send_resp(conn, :im_a_teapot, "no cofe")
+ end
+
+ assert conn.resp_body == body
+ assert conn.status == status
+
+ assert [^second_request_id] = Conn.get_resp_header(conn, "x-request-id")
+ assert [^orig_request_id] = Conn.get_resp_header(conn, "x-original-request-id")
+ assert [^key] = Conn.get_resp_header(conn, "idempotency-key")
+ assert ["true"] = Conn.get_resp_header(conn, "idempotent-replayed")
+ assert ["application/json; charset=utf-8"] = Conn.get_resp_header(conn, "content-type")
+ end
+
+ test "pass conn downstream if the cache not found" do
+ key = "test2"
+ orig_request_id = "test3"
+ body = "testing"
+ status = 200
+
+ conn =
+ :post
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> Conn.put_resp_header("x-request-id", orig_request_id)
+ |> Conn.put_resp_content_type("application/json")
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ assert conn.resp_body == body
+ assert conn.status == status
+
+ assert [] = Conn.get_resp_header(conn, "idempotent-replayed")
+ assert [^key] = Conn.get_resp_header(conn, "idempotency-key")
+ end
+
+ test "passes conn downstream if idempotency is not present in headers" do
+ orig_request_id = "test4"
+ body = "testing"
+ status = 200
+
+ conn =
+ :post
+ |> conn("/cofe")
+ |> Conn.put_resp_header("x-request-id", orig_request_id)
+ |> Conn.put_resp_content_type("application/json")
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ assert [] = Conn.get_resp_header(conn, "idempotency-key")
+ end
+
+ test "doesn't work with GET/DELETE" do
+ key = "test3"
+ body = "testing"
+ status = 200
+
+ conn =
+ :get
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ assert [] = Conn.get_resp_header(conn, "idempotency-key")
+
+ conn =
+ :delete
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ assert [] = Conn.get_resp_header(conn, "idempotency-key")
+ end
+end
diff --git a/test/reverse_proxy_test.exs b/test/reverse_proxy_test.exs
new file mode 100644
index 000000000..75a61445a
--- /dev/null
+++ b/test/reverse_proxy_test.exs
@@ -0,0 +1,297 @@
+defmodule Pleroma.ReverseProxyTest do
+ use Pleroma.Web.ConnCase, async: true
+ import ExUnit.CaptureLog
+ import ExUnit.CaptureLog
+ import Mox
+ alias Pleroma.ReverseProxy
+ alias Pleroma.ReverseProxy.ClientMock
+
+ setup_all do
+ {:ok, _} = Registry.start_link(keys: :unique, name: Pleroma.ReverseProxy.ClientMock)
+ :ok
+ end
+
+ setup :verify_on_exit!
+
+ defp user_agent_mock(user_agent, invokes) do
+ json = Jason.encode!(%{"user-agent": user_agent})
+
+ ClientMock
+ |> expect(:request, fn :get, url, _, _, _ ->
+ Registry.register(Pleroma.ReverseProxy.ClientMock, url, 0)
+
+ {:ok, 200,
+ [
+ {"content-type", "application/json"},
+ {"content-length", byte_size(json) |> to_string()}
+ ], %{url: url}}
+ end)
+ |> expect(:stream_body, invokes, fn %{url: url} ->
+ case Registry.lookup(Pleroma.ReverseProxy.ClientMock, url) do
+ [{_, 0}] ->
+ Registry.update_value(Pleroma.ReverseProxy.ClientMock, url, &(&1 + 1))
+ {:ok, json}
+
+ [{_, 1}] ->
+ Registry.unregister(Pleroma.ReverseProxy.ClientMock, url)
+ :done
+ end
+ end)
+ end
+
+ describe "user-agent" do
+ test "don't keep", %{conn: conn} do
+ user_agent_mock("hackney/1.15.1", 2)
+ conn = ReverseProxy.call(conn, "/user-agent")
+ assert json_response(conn, 200) == %{"user-agent" => "hackney/1.15.1"}
+ end
+
+ test "keep", %{conn: conn} do
+ user_agent_mock(Pleroma.Application.user_agent(), 2)
+ conn = ReverseProxy.call(conn, "/user-agent-keep", keep_user_agent: true)
+ assert json_response(conn, 200) == %{"user-agent" => Pleroma.Application.user_agent()}
+ end
+ end
+
+ test "closed connection", %{conn: conn} do
+ ClientMock
+ |> expect(:request, fn :get, "/closed", _, _, _ -> {:ok, 200, [], %{}} end)
+ |> expect(:stream_body, fn _ -> {:error, :closed} end)
+ |> expect(:close, fn _ -> :ok end)
+
+ conn = ReverseProxy.call(conn, "/closed")
+ assert conn.halted
+ end
+
+ describe "max_body " do
+ test "length returns error if content-length more than option", %{conn: conn} do
+ user_agent_mock("hackney/1.15.1", 0)
+
+ assert capture_log(fn ->
+ ReverseProxy.call(conn, "/user-agent", max_body_length: 4)
+ end) =~
+ "[error] Elixir.Pleroma.ReverseProxy: request to \"/user-agent\" failed: :body_too_large"
+ end
+
+ defp stream_mock(invokes, with_close? \\ false) do
+ ClientMock
+ |> expect(:request, fn :get, "/stream-bytes/" <> length, _, _, _ ->
+ Registry.register(Pleroma.ReverseProxy.ClientMock, "/stream-bytes/" <> length, 0)
+
+ {:ok, 200, [{"content-type", "application/octet-stream"}],
+ %{url: "/stream-bytes/" <> length}}
+ end)
+ |> expect(:stream_body, invokes, fn %{url: "/stream-bytes/" <> length} ->
+ max = String.to_integer(length)
+
+ case Registry.lookup(Pleroma.ReverseProxy.ClientMock, "/stream-bytes/" <> length) do
+ [{_, current}] when current < max ->
+ Registry.update_value(
+ Pleroma.ReverseProxy.ClientMock,
+ "/stream-bytes/" <> length,
+ &(&1 + 10)
+ )
+
+ {:ok, "0123456789"}
+
+ [{_, ^max}] ->
+ Registry.unregister(Pleroma.ReverseProxy.ClientMock, "/stream-bytes/" <> length)
+ :done
+ end
+ end)
+
+ if with_close? do
+ expect(ClientMock, :close, fn _ -> :ok end)
+ end
+ end
+
+ test "max_body_size returns error if streaming body more than that option", %{conn: conn} do
+ stream_mock(3, true)
+
+ assert capture_log(fn ->
+ ReverseProxy.call(conn, "/stream-bytes/50", max_body_size: 30)
+ end) =~
+ "[warn] Elixir.Pleroma.ReverseProxy request to /stream-bytes/50 failed while reading/chunking: :body_too_large"
+ end
+ end
+
+ describe "HEAD requests" do
+ test "common", %{conn: conn} do
+ ClientMock
+ |> expect(:request, fn :head, "/head", _, _, _ ->
+ {:ok, 200, [{"content-type", "text/html; charset=utf-8"}]}
+ end)
+
+ conn = ReverseProxy.call(Map.put(conn, :method, "HEAD"), "/head")
+ assert html_response(conn, 200) == ""
+ end
+ end
+
+ defp error_mock(status) when is_integer(status) do
+ ClientMock
+ |> expect(:request, fn :get, "/status/" <> _, _, _, _ ->
+ {:error, status}
+ end)
+ end
+
+ describe "returns error on" do
+ test "500", %{conn: conn} do
+ error_mock(500)
+
+ capture_log(fn -> ReverseProxy.call(conn, "/status/500") end) =~
+ "[error] Elixir.Pleroma.ReverseProxy: request to /status/500 failed with HTTP status 500"
+ end
+
+ test "400", %{conn: conn} do
+ error_mock(400)
+
+ capture_log(fn -> ReverseProxy.call(conn, "/status/400") end) =~
+ "[error] Elixir.Pleroma.ReverseProxy: request to /status/400 failed with HTTP status 400"
+ end
+
+ test "204", %{conn: conn} do
+ ClientMock
+ |> expect(:request, fn :get, "/status/204", _, _, _ -> {:ok, 204, [], %{}} end)
+
+ capture_log(fn ->
+ conn = ReverseProxy.call(conn, "/status/204")
+ assert conn.resp_body == "Request failed: No Content"
+ assert conn.halted
+ end) =~
+ "[error] Elixir.Pleroma.ReverseProxy: request to \"/status/204\" failed with HTTP status 204"
+ end
+ end
+
+ test "streaming", %{conn: conn} do
+ stream_mock(21)
+ conn = ReverseProxy.call(conn, "/stream-bytes/200")
+ assert conn.state == :chunked
+ assert byte_size(conn.resp_body) == 200
+ assert Plug.Conn.get_resp_header(conn, "content-type") == ["application/octet-stream"]
+ end
+
+ defp headers_mock(_) do
+ ClientMock
+ |> expect(:request, fn :get, "/headers", headers, _, _ ->
+ Registry.register(Pleroma.ReverseProxy.ClientMock, "/headers", 0)
+ {:ok, 200, [{"content-type", "application/json"}], %{url: "/headers", headers: headers}}
+ end)
+ |> expect(:stream_body, 2, fn %{url: url, headers: headers} ->
+ case Registry.lookup(Pleroma.ReverseProxy.ClientMock, url) do
+ [{_, 0}] ->
+ Registry.update_value(Pleroma.ReverseProxy.ClientMock, url, &(&1 + 1))
+ headers = for {k, v} <- headers, into: %{}, do: {String.capitalize(k), v}
+ {:ok, Jason.encode!(%{headers: headers})}
+
+ [{_, 1}] ->
+ Registry.unregister(Pleroma.ReverseProxy.ClientMock, url)
+ :done
+ end
+ end)
+
+ :ok
+ end
+
+ describe "keep request headers" do
+ setup [:headers_mock]
+
+ test "header passes", %{conn: conn} do
+ conn =
+ Plug.Conn.put_req_header(
+ conn,
+ "accept",
+ "text/html"
+ )
+ |> ReverseProxy.call("/headers")
+
+ %{"headers" => headers} = json_response(conn, 200)
+ assert headers["Accept"] == "text/html"
+ end
+
+ test "header is filtered", %{conn: conn} do
+ conn =
+ Plug.Conn.put_req_header(
+ conn,
+ "accept-language",
+ "en-US"
+ )
+ |> ReverseProxy.call("/headers")
+
+ %{"headers" => headers} = json_response(conn, 200)
+ refute headers["Accept-Language"]
+ end
+ end
+
+ test "returns 400 on non GET, HEAD requests", %{conn: conn} do
+ conn = ReverseProxy.call(Map.put(conn, :method, "POST"), "/ip")
+ assert conn.status == 400
+ end
+
+ describe "cache resp headers" do
+ test "returns headers", %{conn: conn} do
+ ClientMock
+ |> expect(:request, fn :get, "/cache/" <> ttl, _, _, _ ->
+ {:ok, 200, [{"cache-control", "public, max-age=" <> ttl}], %{}}
+ end)
+ |> expect(:stream_body, fn _ -> :done end)
+
+ conn = ReverseProxy.call(conn, "/cache/10")
+ assert {"cache-control", "public, max-age=10"} in conn.resp_headers
+ end
+
+ test "add cache-control", %{conn: conn} do
+ ClientMock
+ |> expect(:request, fn :get, "/cache", _, _, _ ->
+ {:ok, 200, [{"ETag", "some ETag"}], %{}}
+ end)
+ |> expect(:stream_body, fn _ -> :done end)
+
+ conn = ReverseProxy.call(conn, "/cache")
+ assert {"cache-control", "public"} in conn.resp_headers
+ end
+ end
+
+ defp disposition_headers_mock(headers) do
+ ClientMock
+ |> expect(:request, fn :get, "/disposition", _, _, _ ->
+ Registry.register(Pleroma.ReverseProxy.ClientMock, "/disposition", 0)
+
+ {:ok, 200, headers, %{url: "/disposition"}}
+ end)
+ |> expect(:stream_body, 2, fn %{url: "/disposition"} ->
+ case Registry.lookup(Pleroma.ReverseProxy.ClientMock, "/disposition") do
+ [{_, 0}] ->
+ Registry.update_value(Pleroma.ReverseProxy.ClientMock, "/disposition", &(&1 + 1))
+ {:ok, ""}
+
+ [{_, 1}] ->
+ Registry.unregister(Pleroma.ReverseProxy.ClientMock, "/disposition")
+ :done
+ end
+ end)
+ end
+
+ describe "response content disposition header" do
+ test "not atachment", %{conn: conn} do
+ disposition_headers_mock([
+ {"content-type", "image/gif"},
+ {"content-length", 0}
+ ])
+
+ conn = ReverseProxy.call(conn, "/disposition")
+
+ assert {"content-type", "image/gif"} in conn.resp_headers
+ end
+
+ test "with content-disposition header", %{conn: conn} do
+ disposition_headers_mock([
+ {"content-disposition", "attachment; filename=\"filename.jpg\""},
+ {"content-length", 0}
+ ])
+
+ conn = ReverseProxy.call(conn, "/disposition")
+
+ assert {"content-disposition", "attachment; filename=\"filename.jpg\""} in conn.resp_headers
+ end
+ end
+end
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 5be34660e..c2812e8f7 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -314,6 +314,7 @@ defmodule Pleroma.Factory do
def config_factory do
%Pleroma.Web.AdminAPI.Config{
key: sequence(:key, &"some_key_#{&1}"),
+ group: "pleroma",
value:
sequence(
:value,
diff --git a/test/support/helpers.ex b/test/support/helpers.ex
index 6e389ce52..1a92be065 100644
--- a/test/support/helpers.ex
+++ b/test/support/helpers.ex
@@ -9,6 +9,12 @@ defmodule Pleroma.Tests.Helpers do
defmacro __using__(_opts) do
quote do
+ def collect_ids(collection) do
+ collection
+ |> Enum.map(& &1.id)
+ |> Enum.sort()
+ end
+
def refresh_record(%{id: id, __struct__: model} = _),
do: refresh_record(model, %{id: id})
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index f7f55a11a..c593a5e4a 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -31,8 +31,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body:
- File.read!("test/fixtures/httpoison_mock/https___osada.macgirvin.com_channel_mike.json")
+ body: File.read!("test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json")
}}
end
@@ -40,7 +39,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/status.emelie.json")
+ body: File.read!("test/fixtures/tesla_mock/status.emelie.json")
}}
end
@@ -48,7 +47,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/emelie.json")
+ body: File.read!("test/fixtures/tesla_mock/emelie.json")
}}
end
@@ -56,7 +55,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/rinpatch.json")
+ body: File.read!("test/fixtures/tesla_mock/rinpatch.json")
}}
end
@@ -69,7 +68,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/webfinger_emelie.json")
+ body: File.read!("test/fixtures/tesla_mock/webfinger_emelie.json")
}}
end
@@ -77,7 +76,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/emelie.atom")
+ body: File.read!("test/fixtures/tesla_mock/emelie.atom")
}}
end
@@ -90,7 +89,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/mike@osada.macgirvin.com.json")
+ body: File.read!("test/fixtures/tesla_mock/mike@osada.macgirvin.com.json")
}}
end
@@ -103,7 +102,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml")
+ body: File.read!("test/fixtures/tesla_mock/https___social.heldscal.la_user_29191.xml")
}}
end
@@ -111,7 +110,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom")
+ body: File.read!("test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.atom")
}}
end
@@ -124,7 +123,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml")
+ body: File.read!("test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.xml")
}}
end
@@ -137,7 +136,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/atarifrosch_feed.xml")
+ body: File.read!("test/fixtures/tesla_mock/atarifrosch_feed.xml")
}}
end
@@ -150,7 +149,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/atarifrosch_webfinger.xml")
+ body: File.read!("test/fixtures/tesla_mock/atarifrosch_webfinger.xml")
}}
end
@@ -158,7 +157,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom")
+ body: File.read!("test/fixtures/tesla_mock/https___mamot.fr_users_Skruyb.atom")
}}
end
@@ -171,7 +170,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom")
+ body: File.read!("test/fixtures/tesla_mock/skruyb@mamot.fr.atom")
}}
end
@@ -184,7 +183,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml")
+ body: File.read!("test/fixtures/tesla_mock/nonexistant@social.heldscal.la.xml")
}}
end
@@ -197,7 +196,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml")
+ body: File.read!("test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml")
}}
end
@@ -210,7 +209,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/lucifermysticus.json")
+ body: File.read!("test/fixtures/tesla_mock/lucifermysticus.json")
}}
end
@@ -218,7 +217,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/https___prismo.news__mxb.json")
+ body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json")
}}
end
@@ -231,7 +230,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json")
+ body: File.read!("test/fixtures/tesla_mock/kaniini@hubzilla.example.org.json")
}}
end
@@ -239,7 +238,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/rye.json")
+ body: File.read!("test/fixtures/tesla_mock/rye.json")
}}
end
@@ -247,7 +246,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/rye.json")
+ body: File.read!("test/fixtures/tesla_mock/rye.json")
}}
end
@@ -257,7 +256,7 @@ defmodule HttpRequestMock do
status: 200,
body:
File.read!(
- "test/fixtures/httpoison_mock/http___mastodon.example.org_users_admin_status_1234.json"
+ "test/fixtures/tesla_mock/http___mastodon.example.org_users_admin_status_1234.json"
)
}}
end
@@ -266,7 +265,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/puckipedia.com.json")
+ body: File.read!("test/fixtures/tesla_mock/puckipedia.com.json")
}}
end
@@ -274,7 +273,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/7even.json")
+ body: File.read!("test/fixtures/tesla_mock/7even.json")
}}
end
@@ -282,7 +281,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/peertube.moe-vid.json")
+ body: File.read!("test/fixtures/tesla_mock/peertube.moe-vid.json")
}}
end
@@ -290,7 +289,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/baptiste.gelex.xyz-user.json")
+ body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json")
}}
end
@@ -298,7 +297,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/baptiste.gelex.xyz-article.json")
+ body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json")
}}
end
@@ -306,7 +305,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/admin@mastdon.example.org.json")
+ body: File.read!("test/fixtures/tesla_mock/admin@mastdon.example.org.json")
}}
end
@@ -331,7 +330,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/7369654.html")
+ body: File.read!("test/fixtures/tesla_mock/7369654.html")
}}
end
@@ -339,7 +338,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/mayumayu.json")
+ body: File.read!("test/fixtures/tesla_mock/mayumayu.json")
}}
end
@@ -352,7 +351,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/mayumayupost.json")
+ body: File.read!("test/fixtures/tesla_mock/mayumayupost.json")
}}
end
@@ -362,7 +361,7 @@ defmodule HttpRequestMock do
status: 200,
body:
File.read!(
- "test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml"
+ "test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml"
)
}}
end
@@ -375,7 +374,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml")
+ body: File.read!("test/fixtures/tesla_mock/https___pleroma.soykaf.com_users_lain.xml")
}}
end
@@ -385,7 +384,7 @@ defmodule HttpRequestMock do
status: 200,
body:
File.read!(
- "test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml"
+ "test/fixtures/tesla_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml"
)
}}
end
@@ -399,7 +398,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml")
+ body: File.read!("test/fixtures/tesla_mock/https___shitposter.club_user_1.xml")
}}
end
@@ -407,8 +406,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body:
- File.read!("test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html")
+ body: File.read!("test/fixtures/tesla_mock/https___shitposter.club_notice_2827873.html")
}}
end
@@ -418,7 +416,7 @@ defmodule HttpRequestMock do
status: 200,
body:
File.read!(
- "test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml"
+ "test/fixtures/tesla_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml"
)
}}
end
@@ -431,7 +429,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/spc_5381.atom")
+ body: File.read!("test/fixtures/tesla_mock/spc_5381.atom")
}}
end
@@ -444,7 +442,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/spc_5381_xrd.xml")
+ body: File.read!("test/fixtures/tesla_mock/spc_5381_xrd.xml")
}}
end
@@ -452,7 +450,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/shitposter.club_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/shitposter.club_host_meta")
}}
end
@@ -460,7 +458,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/7369654.atom")
+ body: File.read!("test/fixtures/tesla_mock/7369654.atom")
}}
end
@@ -468,7 +466,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/7369654.html")
+ body: File.read!("test/fixtures/tesla_mock/7369654.html")
}}
end
@@ -476,7 +474,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/sakamoto_eal_feed.atom")
+ body: File.read!("test/fixtures/tesla_mock/sakamoto_eal_feed.atom")
}}
end
@@ -484,7 +482,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/social.sakamoto.gq_host_meta")
}}
end
@@ -497,7 +495,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml")
+ body: File.read!("test/fixtures/tesla_mock/eal_sakamoto.xml")
}}
end
@@ -507,14 +505,14 @@ defmodule HttpRequestMock do
_,
Accept: "application/atom+xml"
) do
- {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/httpoison_mock/sakamoto.atom")}}
+ {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/sakamoto.atom")}}
end
def get("http://mastodon.social/.well-known/host-meta", _, _, _) do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/mastodon.social_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/mastodon.social_host_meta")
}}
end
@@ -528,9 +526,7 @@ defmodule HttpRequestMock do
%Tesla.Env{
status: 200,
body:
- File.read!(
- "test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml"
- )
+ File.read!("test/fixtures/tesla_mock/https___mastodon.social_users_lambadalambda.xml")
}}
end
@@ -538,7 +534,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/gs.example.org_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/gs.example.org_host_meta")
}}
end
@@ -552,9 +548,7 @@ defmodule HttpRequestMock do
%Tesla.Env{
status: 200,
body:
- File.read!(
- "test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml"
- )
+ File.read!("test/fixtures/tesla_mock/http___gs.example.org_4040_index.php_user_1.xml")
}}
end
@@ -573,7 +567,7 @@ defmodule HttpRequestMock do
status: 200,
body:
File.read!(
- "test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml"
+ "test/fixtures/tesla_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml"
)
}}
end
@@ -584,14 +578,14 @@ defmodule HttpRequestMock do
status: 200,
body:
File.read!(
- "test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml"
+ "test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml"
)
}}
end
def get("http://squeet.me/.well-known/host-meta", _, _, _) do
{:ok,
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/httpoison_mock/squeet.me_host_meta")}}
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/squeet.me_host_meta")}}
end
def get(
@@ -603,7 +597,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml")
+ body: File.read!("test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml")
}}
end
@@ -616,7 +610,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml")
+ body: File.read!("test/fixtures/tesla_mock/shp@social.heldscal.la.xml")
}}
end
@@ -624,7 +618,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/framatube.org_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/framatube.org_host_meta")
}}
end
@@ -638,7 +632,7 @@ defmodule HttpRequestMock do
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/json"}],
- body: File.read!("test/fixtures/httpoison_mock/framasoft@framatube.org.json")
+ body: File.read!("test/fixtures/tesla_mock/framasoft@framatube.org.json")
}}
end
@@ -646,7 +640,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/gnusocial.de_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/gnusocial.de_host_meta")
}}
end
@@ -659,7 +653,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/winterdienst_webfinger.json")
+ body: File.read!("test/fixtures/tesla_mock/winterdienst_webfinger.json")
}}
end
@@ -667,7 +661,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/status.alpicola.com_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/status.alpicola.com_host_meta")
}}
end
@@ -675,7 +669,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/macgirvin.com_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/macgirvin.com_host_meta")
}}
end
@@ -683,7 +677,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/gerzilla.de_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/gerzilla.de_host_meta")
}}
end
@@ -697,7 +691,7 @@ defmodule HttpRequestMock do
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/json"}],
- body: File.read!("test/fixtures/httpoison_mock/kaniini@gerzilla.de.json")
+ body: File.read!("test/fixtures/tesla_mock/kaniini@gerzilla.de.json")
}}
end
@@ -707,7 +701,7 @@ defmodule HttpRequestMock do
status: 200,
body:
File.read!(
- "test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml"
+ "test/fixtures/tesla_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml"
)
}}
end
@@ -721,7 +715,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml")
+ body: File.read!("test/fixtures/tesla_mock/https___social.heldscal.la_user_23211.xml")
}}
end
@@ -729,7 +723,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/social.heldscal.la_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/social.heldscal.la_host_meta")
}}
end
@@ -737,7 +731,7 @@ defmodule HttpRequestMock do
{:ok,
%Tesla.Env{
status: 200,
- body: File.read!("test/fixtures/httpoison_mock/social.heldscal.la_host_meta")
+ body: File.read!("test/fixtures/tesla_mock/social.heldscal.la_host_meta")
}}
end
@@ -757,6 +751,62 @@ defmodule HttpRequestMock do
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}}
end
+ def get("https://example.com/ogp", _, _, _) do
+ {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}}
+ end
+
+ def get("https://pleroma.local/notice/9kCP7V", _, _, _) do
+ {:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}}
+ end
+
+ def get("http://localhost:4001/users/masto_closed/followers", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/users_mock/masto_closed_followers.json")
+ }}
+ end
+
+ def get("http://localhost:4001/users/masto_closed/following", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/users_mock/masto_closed_following.json")
+ }}
+ end
+
+ def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/users_mock/pleroma_followers.json")
+ }}
+ end
+
+ def get("http://localhost:4001/users/fuser2/following", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/users_mock/pleroma_following.json")
+ }}
+ end
+
+ def get("http://domain-with-errors:4001/users/fuser1/followers", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 504,
+ body: ""
+ }}
+ end
+
+ def get("http://domain-with-errors:4001/users/fuser1/following", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 504,
+ body: ""
+ }}
+ end
+
def get("http://example.com/ogp-missing-data", _, _, _) do
{:ok,
%Tesla.Env{
@@ -765,6 +815,14 @@ defmodule HttpRequestMock do
}}
end
+ def get("https://example.com/ogp-missing-data", _, _, _) do
+ {:ok,
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/rich_media/ogp-missing-data.html")
+ }}
+ end
+
def get("http://example.com/malformed", _, _, _) do
{:ok,
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")}}
diff --git a/test/tasks/config_test.exs b/test/tasks/config_test.exs
index d448b0444..83a363356 100644
--- a/test/tasks/config_test.exs
+++ b/test/tasks/config_test.exs
@@ -30,19 +30,28 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
- first_db = Config.get_by_key("first_setting")
- second_db = Config.get_by_key("second_setting")
- refute Config.get_by_key("Pleroma.Repo")
+ first_db = Config.get_by_params(%{group: "pleroma", key: "first_setting"})
+ second_db = Config.get_by_params(%{group: "pleroma", key: "second_setting"})
+ refute Config.get_by_params(%{group: "pleroma", key: "Pleroma.Repo"})
assert Config.from_binary(first_db.value) == [key: "value", key2: [Pleroma.Repo]]
assert Config.from_binary(second_db.value) == [key: "value2", key2: [Pleroma.Activity]]
end
test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do
- Config.create(%{key: "setting_first", value: [key: "value", key2: [Pleroma.Activity]]})
- Config.create(%{key: "setting_second", value: [key: "valu2", key2: [Pleroma.Repo]]})
-
- Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "temp"])
+ Config.create(%{
+ group: "pleroma",
+ key: "setting_first",
+ value: [key: "value", key2: [Pleroma.Activity]]
+ })
+
+ Config.create(%{
+ group: "pleroma",
+ key: "setting_second",
+ value: [key: "valu2", key2: [Pleroma.Repo]]
+ })
+
+ Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "temp", "true"])
assert Repo.all(Config) == []
assert File.exists?(temp_file)
diff --git a/test/tasks/ecto/ecto_test.exs b/test/tasks/ecto/ecto_test.exs
new file mode 100644
index 000000000..b48662c88
--- /dev/null
+++ b/test/tasks/ecto/ecto_test.exs
@@ -0,0 +1,11 @@
+defmodule Mix.Tasks.Pleroma.EctoTest do
+ use ExUnit.Case, async: true
+
+ test "raise on bad path" do
+ assert_raise RuntimeError, ~r/Could not find migrations directory/, fn ->
+ Mix.Tasks.Pleroma.Ecto.ensure_migrations_path(Pleroma.Repo,
+ migrations_path: "some-path"
+ )
+ end
+ end
+end
diff --git a/test/tasks/pleroma_test.exs b/test/tasks/pleroma_test.exs
new file mode 100644
index 000000000..e236ccbbb
--- /dev/null
+++ b/test/tasks/pleroma_test.exs
@@ -0,0 +1,46 @@
+defmodule Mix.PleromaTest do
+ use ExUnit.Case, async: true
+ import Mix.Pleroma
+
+ setup_all do
+ Mix.shell(Mix.Shell.Process)
+
+ on_exit(fn ->
+ Mix.shell(Mix.Shell.IO)
+ end)
+
+ :ok
+ end
+
+ describe "shell_prompt/1" do
+ test "input" do
+ send(self(), {:mix_shell_input, :prompt, "Yes"})
+
+ answer = shell_prompt("Do you want this?")
+ assert_received {:mix_shell, :prompt, [message]}
+ assert message =~ "Do you want this?"
+ assert answer == "Yes"
+ end
+
+ test "with defval" do
+ send(self(), {:mix_shell_input, :prompt, "\n"})
+
+ answer = shell_prompt("Do you want this?", "defval")
+
+ assert_received {:mix_shell, :prompt, [message]}
+ assert message =~ "Do you want this? [defval]"
+ assert answer == "defval"
+ end
+ end
+
+ describe "get_option/3" do
+ test "get from options" do
+ assert get_option([domain: "some-domain.com"], :domain, "Promt") == "some-domain.com"
+ end
+
+ test "get from prompt" do
+ send(self(), {:mix_shell_input, :prompt, "another-domain.com"})
+ assert get_option([], :domain, "Prompt") == "another-domain.com"
+ end
+ end
+end
diff --git a/test/tasks/robots_txt_test.exs b/test/tasks/robots_txt_test.exs
new file mode 100644
index 000000000..539193f73
--- /dev/null
+++ b/test/tasks/robots_txt_test.exs
@@ -0,0 +1,43 @@
+defmodule Mix.Tasks.Pleroma.RobotsTxtTest do
+ use ExUnit.Case, async: true
+ alias Mix.Tasks.Pleroma.RobotsTxt
+
+ test "creates new dir" do
+ path = "test/fixtures/new_dir/"
+ file_path = path <> "robots.txt"
+
+ static_dir = Pleroma.Config.get([:instance, :static_dir])
+ Pleroma.Config.put([:instance, :static_dir], path)
+
+ on_exit(fn ->
+ Pleroma.Config.put([:instance, :static_dir], static_dir)
+ {:ok, ["test/fixtures/new_dir/", "test/fixtures/new_dir/robots.txt"]} = File.rm_rf(path)
+ end)
+
+ RobotsTxt.run(["disallow_all"])
+
+ assert File.exists?(file_path)
+ {:ok, file} = File.read(file_path)
+
+ assert file == "User-Agent: *\nDisallow: /\n"
+ end
+
+ test "to existance folder" do
+ path = "test/fixtures/"
+ file_path = path <> "robots.txt"
+ static_dir = Pleroma.Config.get([:instance, :static_dir])
+ Pleroma.Config.put([:instance, :static_dir], path)
+
+ on_exit(fn ->
+ Pleroma.Config.put([:instance, :static_dir], static_dir)
+ :ok = File.rm(file_path)
+ end)
+
+ RobotsTxt.run(["disallow_all"])
+
+ assert File.exists?(file_path)
+ {:ok, file} = File.read(file_path)
+
+ assert file == "User-Agent: *\nDisallow: /\n"
+ end
+end
diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs
index 6fd7c7113..3d4b08fba 100644
--- a/test/tasks/user_test.exs
+++ b/test/tasks/user_test.exs
@@ -89,8 +89,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do
assert_received {:mix_shell, :info, [message]}
assert message =~ " deleted"
- user = User.get_cached_by_nickname(user.nickname)
- assert user.info.deactivated
+ refute User.get_by_nickname(user.nickname)
end
test "no user to delete" do
diff --git a/test/test_helper.exs b/test/test_helper.exs
index f604ba63d..3e33f0335 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -3,6 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only
ExUnit.start()
-
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
+Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client)
{:ok, _} = Application.ensure_all_started(:ex_machina)
diff --git a/test/upload/filter/anonymize_filename_test.exs b/test/upload/filter/anonymize_filename_test.exs
new file mode 100644
index 000000000..02241cfa4
--- /dev/null
+++ b/test/upload/filter/anonymize_filename_test.exs
@@ -0,0 +1,40 @@
+defmodule Pleroma.Upload.Filter.AnonymizeFilenameTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Config
+ alias Pleroma.Upload
+
+ setup do
+ custom_filename = Config.get([Upload.Filter.AnonymizeFilename, :text])
+
+ on_exit(fn ->
+ Config.put([Upload.Filter.AnonymizeFilename, :text], custom_filename)
+ end)
+
+ upload_file = %Upload{
+ name: "an… image.jpg",
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image_tmp.jpg")
+ }
+
+ %{upload_file: upload_file}
+ end
+
+ test "it replaces filename on pre-defined text", %{upload_file: upload_file} do
+ Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.png")
+ {:ok, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
+ assert name == "custom-file.png"
+ end
+
+ test "it replaces filename on pre-defined text expression", %{upload_file: upload_file} do
+ Config.put([Upload.Filter.AnonymizeFilename, :text], "custom-file.{extension}")
+ {:ok, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
+ assert name == "custom-file.jpg"
+ end
+
+ test "it replaces filename on random text", %{upload_file: upload_file} do
+ {:ok, %Upload{name: name}} = Upload.Filter.AnonymizeFilename.filter(upload_file)
+ assert <<_::bytes-size(14)>> <> ".jpg" = name
+ refute name == "an… image.jpg"
+ end
+end
diff --git a/test/user/synchronization_test.exs b/test/user/synchronization_test.exs
new file mode 100644
index 000000000..67b669431
--- /dev/null
+++ b/test/user/synchronization_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.User.SynchronizationTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+ alias Pleroma.User
+ alias Pleroma.User.Synchronization
+
+ setup do
+ Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ test "update following/followers counters" do
+ user1 =
+ insert(:user,
+ local: false,
+ ap_id: "http://localhost:4001/users/masto_closed"
+ )
+
+ user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
+
+ users = User.external_users()
+ assert length(users) == 2
+ {user, %{}} = Synchronization.call(users, %{})
+ assert user == List.last(users)
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
+ assert followers == 437
+ assert following == 152
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
+
+ assert followers == 527
+ assert following == 267
+ end
+
+ test "don't check host if errors exist" do
+ user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
+
+ user2 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser2")
+
+ users = User.external_users()
+ assert length(users) == 2
+
+ {user, %{"domain-with-errors" => 2}} =
+ Synchronization.call(users, %{"domain-with-errors" => 2}, max_retries: 2)
+
+ assert user == List.last(users)
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
+ assert followers == 0
+ assert following == 0
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
+
+ assert followers == 0
+ assert following == 0
+ end
+
+ test "don't check host if errors appeared" do
+ user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
+
+ user2 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser2")
+
+ users = User.external_users()
+ assert length(users) == 2
+
+ {user, %{"domain-with-errors" => 2}} = Synchronization.call(users, %{}, max_retries: 2)
+
+ assert user == List.last(users)
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
+ assert followers == 0
+ assert following == 0
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
+
+ assert followers == 0
+ assert following == 0
+ end
+
+ test "other users after error appeared" do
+ user1 = insert(:user, local: false, ap_id: "http://domain-with-errors:4001/users/fuser1")
+ user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
+
+ users = User.external_users()
+ assert length(users) == 2
+
+ {user, %{"domain-with-errors" => 2}} = Synchronization.call(users, %{}, max_retries: 2)
+ assert user == List.last(users)
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
+ assert followers == 0
+ assert following == 0
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
+
+ assert followers == 527
+ assert following == 267
+ end
+end
diff --git a/test/user/synchronization_worker_test.exs b/test/user/synchronization_worker_test.exs
new file mode 100644
index 000000000..835c5327f
--- /dev/null
+++ b/test/user/synchronization_worker_test.exs
@@ -0,0 +1,49 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.User.SynchronizationWorkerTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ setup do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+ config = Pleroma.Config.get([:instance, :external_user_synchronization])
+
+ for_update = [enabled: true, interval: 1000]
+
+ Pleroma.Config.put([:instance, :external_user_synchronization], for_update)
+
+ on_exit(fn ->
+ Pleroma.Config.put([:instance, :external_user_synchronization], config)
+ end)
+
+ :ok
+ end
+
+ test "sync follow counters" do
+ user1 =
+ insert(:user,
+ local: false,
+ ap_id: "http://localhost:4001/users/masto_closed"
+ )
+
+ user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
+
+ {:ok, _} = Pleroma.User.SynchronizationWorker.start_link()
+ :timer.sleep(1500)
+
+ %{follower_count: followers, following_count: following} =
+ Pleroma.User.get_cached_user_info(user1)
+
+ assert followers == 437
+ assert following == 152
+
+ %{follower_count: followers, following_count: following} =
+ Pleroma.User.get_cached_user_info(user2)
+
+ assert followers == 527
+ assert following == 267
+ end
+end
diff --git a/test/user_search_test.exs b/test/user_search_test.exs
new file mode 100644
index 000000000..1f0162486
--- /dev/null
+++ b/test/user_search_test.exs
@@ -0,0 +1,252 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.UserSearchTest do
+ alias Pleroma.Repo
+ alias Pleroma.User
+ use Pleroma.DataCase
+
+ import Pleroma.Factory
+
+ setup_all do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ describe "User.search" do
+ test "accepts limit parameter" do
+ Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
+ assert length(User.search("john", limit: 3)) == 3
+ assert length(User.search("john")) == 5
+ end
+
+ test "accepts offset parameter" do
+ Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
+ assert length(User.search("john", limit: 3)) == 3
+ assert length(User.search("john", limit: 3, offset: 3)) == 2
+ end
+
+ test "finds a user by full or partial nickname" do
+ user = insert(:user, %{nickname: "john"})
+
+ Enum.each(["john", "jo", "j"], fn query ->
+ assert user ==
+ User.search(query)
+ |> List.first()
+ |> Map.put(:search_rank, nil)
+ |> Map.put(:search_type, nil)
+ end)
+ end
+
+ test "finds a user by full or partial name" do
+ user = insert(:user, %{name: "John Doe"})
+
+ Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query ->
+ assert user ==
+ User.search(query)
+ |> List.first()
+ |> Map.put(:search_rank, nil)
+ |> Map.put(:search_type, nil)
+ end)
+ end
+
+ test "finds users, preferring nickname matches over name matches" do
+ u1 = insert(:user, %{name: "lain", nickname: "nick1"})
+ u2 = insert(:user, %{nickname: "lain", name: "nick1"})
+
+ assert [u2.id, u1.id] == Enum.map(User.search("lain"), & &1.id)
+ end
+
+ test "finds users, considering density of matched tokens" do
+ u1 = insert(:user, %{name: "Bar Bar plus Word Word"})
+ u2 = insert(:user, %{name: "Word Word Bar Bar Bar"})
+
+ assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
+ end
+
+ test "finds users, ranking by similarity" do
+ u1 = insert(:user, %{name: "lain"})
+ _u2 = insert(:user, %{name: "ean"})
+ u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
+ u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
+
+ assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple", for_user: u1), & &1.id)
+ end
+
+ test "finds users, handling misspelled requests" do
+ u1 = insert(:user, %{name: "lain"})
+
+ assert [u1.id] == Enum.map(User.search("laiin"), & &1.id)
+ end
+
+ test "finds users, boosting ranks of friends and followers" do
+ u1 = insert(:user)
+ u2 = insert(:user, %{name: "Doe"})
+ follower = insert(:user, %{name: "Doe"})
+ friend = insert(:user, %{name: "Doe"})
+
+ {:ok, follower} = User.follow(follower, u1)
+ {:ok, u1} = User.follow(u1, friend)
+
+ assert [friend.id, follower.id, u2.id] --
+ Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
+ end
+
+ test "finds followers of user by partial name" do
+ u1 = insert(:user)
+ u2 = insert(:user, %{name: "Jimi"})
+ follower_jimi = insert(:user, %{name: "Jimi Hendrix"})
+ follower_lizz = insert(:user, %{name: "Lizz Wright"})
+ friend = insert(:user, %{name: "Jimi"})
+
+ {:ok, follower_jimi} = User.follow(follower_jimi, u1)
+ {:ok, _follower_lizz} = User.follow(follower_lizz, u2)
+ {:ok, u1} = User.follow(u1, friend)
+
+ assert Enum.map(User.search("jimi", following: true, for_user: u1), & &1.id) == [
+ follower_jimi.id
+ ]
+
+ assert User.search("lizz", following: true, for_user: u1) == []
+ end
+
+ test "find local and remote users for authenticated users" do
+ u1 = insert(:user, %{name: "lain"})
+ u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
+ u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
+
+ results =
+ "lain"
+ |> User.search(for_user: u1)
+ |> Enum.map(& &1.id)
+ |> Enum.sort()
+
+ assert [u1.id, u2.id, u3.id] == results
+ end
+
+ test "find only local users for unauthenticated users" do
+ %{id: id} = insert(:user, %{name: "lain"})
+ insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
+ insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
+
+ assert [%{id: ^id}] = User.search("lain")
+ end
+
+ test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do
+ Pleroma.Config.put([:instance, :limit_to_local_content], :all)
+
+ %{id: id} = insert(:user, %{name: "lain"})
+ insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
+ insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
+
+ assert [%{id: ^id}] = User.search("lain")
+
+ Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
+ end
+
+ test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
+ Pleroma.Config.put([:instance, :limit_to_local_content], false)
+
+ u1 = insert(:user, %{name: "lain"})
+ u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
+ u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
+
+ results =
+ "lain"
+ |> User.search()
+ |> Enum.map(& &1.id)
+ |> Enum.sort()
+
+ assert [u1.id, u2.id, u3.id] == results
+
+ Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
+ end
+
+ test "finds a user whose name is nil" do
+ _user = insert(:user, %{name: "notamatch", nickname: "testuser@pleroma.amplifie.red"})
+ user_two = insert(:user, %{name: nil, nickname: "lain@pleroma.soykaf.com"})
+
+ assert user_two ==
+ 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
+ insert(:user, %{name: "John Doe"})
+
+ Enum.each(["mary", "a", ""], fn query ->
+ assert [] == User.search(query)
+ end)
+ end
+
+ test "works with URIs" do
+ user = insert(:user)
+
+ results =
+ User.search("http://mastodon.example.org/users/admin", resolve: true, for_user: user)
+
+ result = results |> List.first()
+
+ user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
+
+ assert length(results) == 1
+ assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
+ end
+
+ test "excludes a blocked users from search result" do
+ user = insert(:user, %{nickname: "Bill"})
+
+ [blocked_user | users] = Enum.map(0..3, &insert(:user, %{nickname: "john#{&1}"}))
+
+ blocked_user2 =
+ insert(
+ :user,
+ %{nickname: "john awful", ap_id: "https://awful-and-rude-instance.com/user/bully"}
+ )
+
+ User.block_domain(user, "awful-and-rude-instance.com")
+ User.block(user, blocked_user)
+
+ account_ids = User.search("john", for_user: refresh_record(user)) |> collect_ids
+
+ assert account_ids == collect_ids(users)
+ refute Enum.member?(account_ids, blocked_user.id)
+ refute Enum.member?(account_ids, blocked_user2.id)
+ assert length(account_ids) == 3
+ end
+
+ test "local user has the same search_rank as for users with the same nickname, but another domain" do
+ user = insert(:user)
+ insert(:user, nickname: "lain@mastodon.social")
+ insert(:user, nickname: "lain")
+ insert(:user, nickname: "lain@pleroma.social")
+
+ assert User.search("lain@localhost", resolve: true, for_user: user)
+ |> Enum.each(fn u -> u.search_rank == 0.5 end)
+ end
+
+ test "localhost is the part of the domain" do
+ user = insert(:user)
+ insert(:user, nickname: "another@somedomain")
+ insert(:user, nickname: "lain")
+ insert(:user, nickname: "lain@examplelocalhost")
+
+ result = User.search("lain@examplelocalhost", resolve: true, for_user: user)
+ assert Enum.each(result, fn u -> u.search_rank == 0.5 end)
+ assert length(result) == 2
+ end
+
+ test "local user search with users" do
+ user = insert(:user)
+ local_user = insert(:user, nickname: "lain")
+ insert(:user, nickname: "another@localhost.com")
+ insert(:user, nickname: "localhost@localhost.com")
+
+ [result] = User.search("lain@localhost", resolve: true, for_user: user)
+ assert Map.put(result, :search_rank, nil) |> Map.put(:search_type, nil) == local_user
+ end
+ end
+end
diff --git a/test/user_test.exs b/test/user_test.exs
index a8176025c..0f27d73f7 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -920,42 +920,44 @@ defmodule Pleroma.UserTest do
{:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
- Ecto.Adapters.SQL.Sandbox.unboxed_run(Repo, fn ->
- {:ok, _} = User.delete_user_activities(user)
- # TODO: Remove favorites, repeats, delete activities.
- refute Activity.get_by_id(activity.id)
- end)
+ {: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
+ test ".delete deactivates a user, all follow relationships and all activities" do
user = insert(:user)
- followed = insert(:user)
follower = insert(:user)
- {:ok, user} = User.follow(user, followed)
{:ok, follower} = User.follow(follower, user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
{:ok, activity_two} = CommonAPI.post(follower, %{"status" => "3hu"})
- {:ok, _, _} = CommonAPI.favorite(activity_two.id, user)
- {:ok, _, _} = CommonAPI.favorite(activity.id, follower)
- {:ok, _, _} = CommonAPI.repeat(activity.id, follower)
+ {:ok, like, _} = CommonAPI.favorite(activity_two.id, user)
+ {:ok, like_two, _} = CommonAPI.favorite(activity.id, follower)
+ {:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)
{:ok, _} = User.delete(user)
- 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
+ refute User.following?(follower, user)
+ refute User.get_by_id(user.id)
- refute User.following?(user, followed)
- refute User.following?(followed, follower)
+ user_activities =
+ user.ap_id
+ |> Activity.query_by_actor()
+ |> Repo.all()
+ |> Enum.map(fn act -> act.data["type"] end)
- # TODO: Remove favorites, repeats, delete activities.
+ assert Enum.all?(user_activities, fn act -> act in ~w(Delete Undo) end)
refute Activity.get_by_id(activity.id)
+ refute Activity.get_by_id(like.id)
+ refute Activity.get_by_id(like_two.id)
+ refute Activity.get_by_id(repeat.id)
end
test "get_public_key_for_ap_id fetches a user that's not in the db" do
@@ -1010,189 +1012,6 @@ defmodule Pleroma.UserTest do
end
end
- describe "User.search" do
- test "accepts limit parameter" do
- Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
- assert length(User.search("john", limit: 3)) == 3
- assert length(User.search("john")) == 5
- end
-
- test "accepts offset parameter" do
- Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
- assert length(User.search("john", limit: 3)) == 3
- assert length(User.search("john", limit: 3, offset: 3)) == 2
- end
-
- test "finds a user by full or partial nickname" do
- user = insert(:user, %{nickname: "john"})
-
- Enum.each(["john", "jo", "j"], fn query ->
- assert user ==
- User.search(query)
- |> List.first()
- |> Map.put(:search_rank, nil)
- |> Map.put(:search_type, nil)
- end)
- end
-
- test "finds a user by full or partial name" do
- user = insert(:user, %{name: "John Doe"})
-
- Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query ->
- assert user ==
- User.search(query)
- |> List.first()
- |> Map.put(:search_rank, nil)
- |> Map.put(:search_type, nil)
- end)
- end
-
- test "finds users, preferring nickname matches over name matches" do
- u1 = insert(:user, %{name: "lain", nickname: "nick1"})
- u2 = insert(:user, %{nickname: "lain", name: "nick1"})
-
- assert [u2.id, u1.id] == Enum.map(User.search("lain"), & &1.id)
- end
-
- test "finds users, considering density of matched tokens" do
- u1 = insert(:user, %{name: "Bar Bar plus Word Word"})
- u2 = insert(:user, %{name: "Word Word Bar Bar Bar"})
-
- assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id)
- end
-
- test "finds users, ranking by similarity" do
- u1 = insert(:user, %{name: "lain"})
- _u2 = insert(:user, %{name: "ean"})
- u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
- u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
-
- assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple", for_user: u1), & &1.id)
- end
-
- test "finds users, handling misspelled requests" do
- u1 = insert(:user, %{name: "lain"})
-
- assert [u1.id] == Enum.map(User.search("laiin"), & &1.id)
- end
-
- test "finds users, boosting ranks of friends and followers" do
- u1 = insert(:user)
- u2 = insert(:user, %{name: "Doe"})
- follower = insert(:user, %{name: "Doe"})
- friend = insert(:user, %{name: "Doe"})
-
- {:ok, follower} = User.follow(follower, u1)
- {:ok, u1} = User.follow(u1, friend)
-
- assert [friend.id, follower.id, u2.id] --
- Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
- end
-
- test "finds followers of user by partial name" do
- u1 = insert(:user)
- u2 = insert(:user, %{name: "Jimi"})
- follower_jimi = insert(:user, %{name: "Jimi Hendrix"})
- follower_lizz = insert(:user, %{name: "Lizz Wright"})
- friend = insert(:user, %{name: "Jimi"})
-
- {:ok, follower_jimi} = User.follow(follower_jimi, u1)
- {:ok, _follower_lizz} = User.follow(follower_lizz, u2)
- {:ok, u1} = User.follow(u1, friend)
-
- assert Enum.map(User.search("jimi", following: true, for_user: u1), & &1.id) == [
- follower_jimi.id
- ]
-
- assert User.search("lizz", following: true, for_user: u1) == []
- end
-
- test "find local and remote users for authenticated users" do
- u1 = insert(:user, %{name: "lain"})
- u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
- u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
-
- results =
- "lain"
- |> User.search(for_user: u1)
- |> Enum.map(& &1.id)
- |> Enum.sort()
-
- assert [u1.id, u2.id, u3.id] == results
- end
-
- test "find only local users for unauthenticated users" do
- %{id: id} = insert(:user, %{name: "lain"})
- insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
- insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
-
- assert [%{id: ^id}] = User.search("lain")
- end
-
- test "find only local users for authenticated users when `limit_to_local_content` is `:all`" do
- Pleroma.Config.put([:instance, :limit_to_local_content], :all)
-
- %{id: id} = insert(:user, %{name: "lain"})
- insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
- insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
-
- assert [%{id: ^id}] = User.search("lain")
-
- Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
- end
-
- test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
- Pleroma.Config.put([:instance, :limit_to_local_content], false)
-
- u1 = insert(:user, %{name: "lain"})
- u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
- u3 = insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
-
- results =
- "lain"
- |> User.search()
- |> Enum.map(& &1.id)
- |> Enum.sort()
-
- assert [u1.id, u2.id, u3.id] == results
-
- Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
- end
-
- test "finds a user whose name is nil" do
- _user = insert(:user, %{name: "notamatch", nickname: "testuser@pleroma.amplifie.red"})
- user_two = insert(:user, %{name: nil, nickname: "lain@pleroma.soykaf.com"})
-
- assert user_two ==
- 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
- insert(:user, %{name: "John Doe"})
-
- Enum.each(["mary", "a", ""], fn query ->
- assert [] == User.search(query)
- end)
- end
-
- test "works with URIs" do
- user = insert(:user)
-
- results =
- User.search("http://mastodon.example.org/users/admin", resolve: true, for_user: user)
-
- result = results |> List.first()
-
- user = User.get_cached_by_ap_id("http://mastodon.example.org/users/admin")
-
- assert length(results) == 1
- assert user == result |> Map.put(:search_rank, nil) |> Map.put(:search_type, nil)
- end
- end
-
test "auth_active?/1 works correctly" do
Pleroma.Config.put([:instance, :account_activation_required], true)
@@ -1364,4 +1183,121 @@ defmodule Pleroma.UserTest do
assert user_two.ap_id in ap_ids
end
end
+
+ describe "sync followers count" do
+ setup do
+ user1 = insert(:user, local: false, ap_id: "http://localhost:4001/users/masto_closed")
+ user2 = insert(:user, local: false, ap_id: "http://localhost:4001/users/fuser2")
+ insert(:user, local: true)
+ insert(:user, local: false, info: %{deactivated: true})
+ {:ok, user1: user1, user2: user2}
+ end
+
+ test "external_users/1 external active users with limit", %{user1: user1, user2: user2} do
+ [fdb_user1] = User.external_users(limit: 1)
+
+ assert fdb_user1.ap_id
+ assert fdb_user1.ap_id == user1.ap_id
+ assert fdb_user1.id == user1.id
+
+ [fdb_user2] = User.external_users(max_id: fdb_user1.id, limit: 1)
+
+ assert fdb_user2.ap_id
+ assert fdb_user2.ap_id == user2.ap_id
+ assert fdb_user2.id == user2.id
+
+ assert User.external_users(max_id: fdb_user2.id, limit: 1) == []
+ end
+
+ test "sync_follow_counters/1", %{user1: user1, user2: user2} do
+ {:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors)
+
+ :ok = User.sync_follow_counters()
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
+ assert followers == 437
+ assert following == 152
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
+
+ assert followers == 527
+ assert following == 267
+
+ Agent.stop(:domain_errors)
+ end
+
+ test "sync_follow_counters/1 in separate batches", %{user1: user1, user2: user2} do
+ {:ok, _pid} = Agent.start_link(fn -> %{} end, name: :domain_errors)
+
+ :ok = User.sync_follow_counters(limit: 1)
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
+ assert followers == 437
+ assert following == 152
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
+
+ assert followers == 527
+ assert following == 267
+
+ Agent.stop(:domain_errors)
+ end
+
+ test "perform/1 with :sync_follow_counters", %{user1: user1, user2: user2} do
+ :ok = User.perform(:sync_follow_counters)
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user1)
+ assert followers == 437
+ assert following == 152
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user2)
+
+ assert followers == 527
+ assert following == 267
+ end
+ end
+
+ describe "set_info_cache/2" do
+ setup do
+ user = insert(:user)
+ {:ok, user: user}
+ end
+
+ test "update from args", %{user: user} do
+ User.set_info_cache(user, %{following_count: 15, follower_count: 18})
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user)
+ assert followers == 18
+ assert following == 15
+ end
+
+ test "without args", %{user: user} do
+ User.set_info_cache(user, %{})
+
+ %{follower_count: followers, following_count: following} = User.get_cached_user_info(user)
+ assert followers == 0
+ assert following == 0
+ end
+ end
+
+ describe "user_info/2" do
+ setup do
+ user = insert(:user)
+ {:ok, user: user}
+ end
+
+ test "update from args", %{user: user} do
+ %{follower_count: followers, following_count: following} =
+ User.user_info(user, %{following_count: 15, follower_count: 18})
+
+ assert followers == 18
+ assert following == 15
+ end
+
+ test "without args", %{user: user} do
+ %{follower_count: followers, following_count: following} = User.user_info(user)
+
+ assert followers == 0
+ assert following == 0
+ end
+ 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 8b3233729..5a8a67155 100644
--- a/test/web/activity_pub/activity_pub_controller_test.exs
+++ b/test/web/activity_pub/activity_pub_controller_test.exs
@@ -15,6 +15,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+ config_path = [:instance, :federating]
+ initial_setting = Pleroma.Config.get(config_path)
+
+ Pleroma.Config.put(config_path, true)
+ on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
+
:ok
end
diff --git a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs
index 284c13336..03dc299ec 100644
--- a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs
+++ b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
use Pleroma.DataCase
import Pleroma.Factory
+ import ExUnit.CaptureLog
alias Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy
@@ -114,7 +115,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
@linkless_message
|> Map.put("actor", "http://invalid.actor")
- {:reject, _} = AntiLinkSpamPolicy.filter(message)
+ assert capture_log(fn ->
+ {:reject, _} = AntiLinkSpamPolicy.filter(message)
+ end) =~ "[error] Could not decode user at fetch http://invalid.actor"
end
test "it rejects posts with links" do
@@ -122,7 +125,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
@linkful_message
|> Map.put("actor", "http://invalid.actor")
- {:reject, _} = AntiLinkSpamPolicy.filter(message)
+ assert capture_log(fn ->
+ {:reject, _} = AntiLinkSpamPolicy.filter(message)
+ end) =~ "[error] Could not decode user at fetch http://invalid.actor"
end
end
diff --git a/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs b/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs
new file mode 100644
index 000000000..372e789be
--- /dev/null
+++ b/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs
@@ -0,0 +1,45 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.HTTP
+ alias Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy
+
+ import Mock
+
+ @message %{
+ "type" => "Create",
+ "object" => %{
+ "type" => "Note",
+ "content" => "content",
+ "attachment" => [
+ %{"url" => [%{"href" => "http://example.com/image.jpg"}]}
+ ]
+ }
+ }
+
+ test "it prefetches media proxy URIs" do
+ with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
+ MediaProxyWarmingPolicy.filter(@message)
+ assert called(HTTP.get(:_, :_, :_))
+ end
+ end
+
+ test "it does nothing when no attachments are present" do
+ object =
+ @message["object"]
+ |> Map.delete("attachment")
+
+ message =
+ @message
+ |> Map.put("object", object)
+
+ with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
+ MediaProxyWarmingPolicy.filter(message)
+ refute called(HTTP.get(:_, :_, :_))
+ end
+ end
+end
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 68ec03c33..a914d3c4c 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -11,12 +11,13 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier
+ alias Pleroma.Web.CommonAPI
alias Pleroma.Web.OStatus
alias Pleroma.Web.Websub.WebsubClientSubscription
+ import Mock
import Pleroma.Factory
import ExUnit.CaptureLog
- alias Pleroma.Web.CommonAPI
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -46,12 +47,10 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
data["object"]
|> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
- data =
- data
- |> Map.put("object", object)
-
+ data = Map.put(data, "object", object)
{:ok, returned_activity} = Transmogrifier.handle_incoming(data)
- returned_object = Object.normalize(returned_activity.data["object"])
+
+ returned_object = Object.normalize(returned_activity.data["object"], false)
assert activity =
Activity.get_create_by_object_ap_id(
@@ -61,6 +60,32 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
end
+ test "it does not fetch replied-to activities beyond max_replies_depth" do
+ data =
+ File.read!("test/fixtures/mastodon-post-activity.json")
+ |> Poison.decode!()
+
+ object =
+ data["object"]
+ |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
+
+ data = Map.put(data, "object", object)
+
+ with_mock Pleroma.Web.Federator,
+ allowed_incoming_reply_depth?: fn _ -> false end do
+ {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
+
+ returned_object = Object.normalize(returned_activity.data["object"], false)
+
+ refute Activity.get_create_by_object_ap_id(
+ "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
+ )
+
+ assert returned_object.data["inReplyToAtomUri"] ==
+ "https://shitposter.club/notice/2827873"
+ end
+ end
+
test "it does not crash if the object in inReplyTo can't be fetched" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 2a5912645..4ea33a6cc 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -6,9 +6,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
use Pleroma.Web.ConnCase
alias Pleroma.Activity
+ alias Pleroma.HTML
alias Pleroma.User
alias Pleroma.UserInviteToken
alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.MediaProxy
import Pleroma.Factory
describe "/api/pleroma/admin/users" do
@@ -58,7 +60,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"local" => true,
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
assert expected == json_response(conn, 200)
@@ -445,7 +449,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(admin.name || admin.nickname)
},
%{
"deactivated" => user.info.deactivated,
@@ -453,7 +459,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => false,
- "tags" => ["foo", "bar"]
+ "tags" => ["foo", "bar"],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
]
|> Enum.sort_by(& &1["nickname"])
@@ -492,7 +500,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
]
}
@@ -514,7 +524,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
]
}
@@ -536,7 +548,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
]
}
@@ -558,7 +572,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
]
}
@@ -580,7 +596,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
]
}
@@ -602,7 +620,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
]
}
@@ -619,7 +639,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user2.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user2.name || user2.nickname)
}
]
}
@@ -646,7 +668,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
]
}
@@ -671,7 +695,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
},
%{
"deactivated" => admin.info.deactivated,
@@ -679,7 +705,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(admin.name || admin.nickname)
},
%{
"deactivated" => false,
@@ -687,7 +715,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"local" => true,
"nickname" => old_admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname)
}
]
|> Enum.sort_by(& &1["nickname"])
@@ -714,7 +744,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => admin.local,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(admin.name || admin.nickname)
},
%{
"deactivated" => false,
@@ -722,7 +754,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => second_admin.nickname,
"roles" => %{"admin" => true, "moderator" => false},
"local" => second_admin.local,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname)
}
]
|> Enum.sort_by(& &1["nickname"])
@@ -751,7 +785,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => moderator.nickname,
"roles" => %{"admin" => false, "moderator" => true},
"local" => moderator.local,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(moderator.name || moderator.nickname)
}
]
}
@@ -773,7 +809,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user1.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => user1.local,
- "tags" => ["first"]
+ "tags" => ["first"],
+ "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user1.name || user1.nickname)
},
%{
"deactivated" => false,
@@ -781,7 +819,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user2.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => user2.local,
- "tags" => ["second"]
+ "tags" => ["second"],
+ "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user2.name || user2.nickname)
}
]
|> Enum.sort_by(& &1["nickname"])
@@ -815,7 +855,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => user.local,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
]
}
@@ -838,7 +880,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"nickname" => user.nickname,
"roles" => %{"admin" => false, "moderator" => false},
"local" => true,
- "tags" => []
+ "tags" => [],
+ "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+ "display_name" => HTML.strip_tags(user.name || user.nickname)
}
end
@@ -1343,6 +1387,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
Application.delete_env(:pleroma, :key4)
Application.delete_env(:pleroma, :keyaa1)
Application.delete_env(:pleroma, :keyaa2)
+ Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
+ Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
:ok = File.rm(temp_file)
end)
@@ -1361,8 +1407,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
conn =
post(conn, "/api/pleroma/admin/config", %{
configs: [
- %{key: "key1", value: "value1"},
+ %{group: "pleroma", key: "key1", value: "value1"},
%{
+ group: "pleroma",
key: "key2",
value: %{
"nested_1" => "nested_value1",
@@ -1373,6 +1420,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
}
},
%{
+ group: "pleroma",
key: "key3",
value: [
%{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
@@ -1380,8 +1428,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
]
},
%{
+ group: "pleroma",
key: "key4",
value: %{"nested_5" => ":upload", "endpoint" => "https://example.com"}
+ },
+ %{
+ group: "idna",
+ key: "key5",
+ value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
}
]
})
@@ -1389,10 +1443,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert json_response(conn, 200) == %{
"configs" => [
%{
+ "group" => "pleroma",
"key" => "key1",
"value" => "value1"
},
%{
+ "group" => "pleroma",
"key" => "key2",
"value" => [
%{"nested_1" => "nested_value1"},
@@ -1405,6 +1461,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
]
},
%{
+ "group" => "pleroma",
"key" => "key3",
"value" => [
[%{"nested_3" => "nested_3"}, %{"nested_33" => "nested_33"}],
@@ -1412,8 +1469,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
]
},
%{
+ "group" => "pleroma",
"key" => "key4",
"value" => [%{"endpoint" => "https://example.com"}, %{"nested_5" => "upload"}]
+ },
+ %{
+ "group" => "idna",
+ "key" => "key5",
+ "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
}
]
}
@@ -1437,6 +1500,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
endpoint: "https://example.com",
nested_5: :upload
]
+
+ assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
end
test "update config setting & delete", %{conn: conn} do
@@ -1446,14 +1511,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
conn =
post(conn, "/api/pleroma/admin/config", %{
configs: [
- %{key: config1.key, value: "another_value"},
- %{key: config2.key, delete: "true"}
+ %{group: config1.group, key: config1.key, value: "another_value"},
+ %{group: config2.group, key: config2.key, delete: "true"}
]
})
assert json_response(conn, 200) == %{
"configs" => [
%{
+ "group" => "pleroma",
"key" => config1.key,
"value" => "another_value"
}
@@ -1463,5 +1529,152 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
assert Application.get_env(:pleroma, :keyaa1) == "another_value"
refute Application.get_env(:pleroma, :keyaa2)
end
+
+ test "common config example", %{conn: conn} do
+ conn =
+ post(conn, "/api/pleroma/admin/config", %{
+ configs: [
+ %{
+ "group" => "pleroma",
+ "key" => "Pleroma.Captcha.NotReal",
+ "value" => %{
+ "enabled" => ":false",
+ "method" => "Pleroma.Captcha.Kocaptcha",
+ "seconds_valid" => "i:60"
+ }
+ }
+ ]
+ })
+
+ assert json_response(conn, 200) == %{
+ "configs" => [
+ %{
+ "group" => "pleroma",
+ "key" => "Pleroma.Captcha.NotReal",
+ "value" => [
+ %{"enabled" => false},
+ %{"method" => "Pleroma.Captcha.Kocaptcha"},
+ %{"seconds_valid" => 60}
+ ]
+ }
+ ]
+ }
+ end
+
+ test "tuples with more than two values", %{conn: conn} do
+ conn =
+ post(conn, "/api/pleroma/admin/config", %{
+ configs: [
+ %{
+ "group" => "pleroma",
+ "key" => "Pleroma.Web.Endpoint.NotReal",
+ "value" => [
+ %{
+ "http" => %{
+ "dispatch" => [
+ %{
+ "tuple" => [
+ ":_",
+ [
+ %{
+ "tuple" => [
+ "/api/v1/streaming",
+ "Pleroma.Web.MastodonAPI.WebsocketHandler",
+ []
+ ]
+ },
+ %{
+ "tuple" => [
+ "/websocket",
+ "Phoenix.Endpoint.CowboyWebSocket",
+ %{
+ "tuple" => [
+ "Phoenix.Transports.WebSocket",
+ %{
+ "tuple" => [
+ "Pleroma.Web.Endpoint",
+ "Pleroma.Web.UserSocket",
+ []
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ %{
+ "tuple" => [
+ ":_",
+ "Phoenix.Endpoint.Cowboy2Handler",
+ %{
+ "tuple" => ["Pleroma.Web.Endpoint", []]
+ }
+ ]
+ }
+ ]
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ })
+
+ assert json_response(conn, 200) == %{
+ "configs" => [
+ %{
+ "group" => "pleroma",
+ "key" => "Pleroma.Web.Endpoint.NotReal",
+ "value" => [
+ %{
+ "http" => %{
+ "dispatch" => %{
+ "_" => [
+ %{
+ "tuple" => [
+ "/api/v1/streaming",
+ "Pleroma.Web.MastodonAPI.WebsocketHandler",
+ []
+ ]
+ },
+ %{
+ "tuple" => [
+ "/websocket",
+ "Phoenix.Endpoint.CowboyWebSocket",
+ %{
+ "Elixir.Phoenix.Transports.WebSocket" => %{
+ "tuple" => [
+ "Pleroma.Web.Endpoint",
+ "Pleroma.Web.UserSocket",
+ []
+ ]
+ }
+ }
+ ]
+ },
+ %{
+ "tuple" => [
+ "_",
+ "Phoenix.Endpoint.Cowboy2Handler",
+ %{"Elixir.Pleroma.Web.Endpoint" => []}
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ end
end
end
+
+# Needed for testing
+defmodule Pleroma.Web.Endpoint.NotReal do
+end
+
+defmodule Pleroma.Captcha.NotReal do
+end
diff --git a/test/web/admin_api/config_test.exs b/test/web/admin_api/config_test.exs
index a2fedca40..10cb3b68a 100644
--- a/test/web/admin_api/config_test.exs
+++ b/test/web/admin_api/config_test.exs
@@ -7,18 +7,18 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
config = insert(:config)
insert(:config)
- assert config == Config.get_by_key(config.key)
+ assert config == Config.get_by_params(%{group: config.group, key: config.key})
end
test "create/1" do
- {:ok, config} = Config.create(%{key: "some_key", value: "some_value"})
- assert config == Config.get_by_key("some_key")
+ {:ok, config} = Config.create(%{group: "pleroma", key: "some_key", value: "some_value"})
+ assert config == Config.get_by_params(%{group: "pleroma", key: "some_key"})
end
test "update/1" do
config = insert(:config)
{:ok, updated} = Config.update(config, %{value: "some_value"})
- loaded = Config.get_by_key(config.key)
+ loaded = Config.get_by_params(%{group: config.group, key: config.key})
assert loaded == updated
end
@@ -27,8 +27,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
key2 = "another_key"
params = [
- %{key: key2, value: "another_value"},
- %{key: config.key, value: "new_value"}
+ %{group: "pleroma", key: key2, value: "another_value"},
+ %{group: config.group, key: config.key, value: "new_value"}
]
assert Repo.all(Config) |> length() == 1
@@ -37,8 +37,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
assert Repo.all(Config) |> length() == 2
- config1 = Config.get_by_key(config.key)
- config2 = Config.get_by_key(key2)
+ config1 = Config.get_by_params(%{group: config.group, key: config.key})
+ config2 = Config.get_by_params(%{group: "pleroma", key: key2})
assert config1.value == Config.transform("new_value")
assert config2.value == Config.transform("another_value")
@@ -46,8 +46,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
test "delete/1" do
config = insert(:config)
- {:ok, _} = Config.delete(config.key)
- refute Config.get_by_key(config.key)
+ {:ok, _} = Config.delete(%{key: config.key, group: config.group})
+ refute Config.get_by_params(%{key: config.key, group: config.group})
end
describe "transform/1" do
@@ -179,5 +179,80 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
assert Config.from_binary(binary) ==
[federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []]
end
+
+ test "complex map with tuples with more than 2 values" do
+ binary =
+ Config.transform(%{
+ "http" => %{
+ "dispatch" => [
+ %{
+ "tuple" => [
+ ":_",
+ [
+ %{
+ "tuple" => [
+ "/api/v1/streaming",
+ "Pleroma.Web.MastodonAPI.WebsocketHandler",
+ []
+ ]
+ },
+ %{
+ "tuple" => [
+ "/websocket",
+ "Phoenix.Endpoint.CowboyWebSocket",
+ %{
+ "tuple" => [
+ "Phoenix.Transports.WebSocket",
+ %{"tuple" => ["Pleroma.Web.Endpoint", "Pleroma.Web.UserSocket", []]}
+ ]
+ }
+ ]
+ },
+ %{
+ "tuple" => [
+ ":_",
+ "Phoenix.Endpoint.Cowboy2Handler",
+ %{
+ "tuple" => ["Pleroma.Web.Endpoint", []]
+ }
+ ]
+ }
+ ]
+ ]
+ }
+ ]
+ }
+ })
+
+ assert binary ==
+ :erlang.term_to_binary(
+ http: [
+ dispatch: [
+ _: [
+ {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+ {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
+ {Phoenix.Transports.WebSocket,
+ {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}},
+ {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+ ]
+ ]
+ ]
+ )
+
+ assert Config.from_binary(binary) == [
+ http: [
+ dispatch: [
+ {:_,
+ [
+ {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+ {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
+ {Phoenix.Transports.WebSocket,
+ {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}},
+ {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+ ]}
+ ]
+ ]
+ ]
+ end
end
end
diff --git a/test/web/admin_api/views/report_view_test.exs b/test/web/admin_api/views/report_view_test.exs
index f35f36cac..a00c9c579 100644
--- a/test/web/admin_api/views/report_view_test.exs
+++ b/test/web/admin_api/views/report_view_test.exs
@@ -18,8 +18,16 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
expected = %{
content: nil,
- actor: AccountView.render("account.json", %{user: user}),
- account: AccountView.render("account.json", %{user: other_user}),
+ actor:
+ Map.merge(
+ AccountView.render("account.json", %{user: user}),
+ Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user})
+ ),
+ account:
+ Map.merge(
+ AccountView.render("account.json", %{user: other_user}),
+ Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user})
+ ),
statuses: [],
state: "open",
id: activity.id
@@ -42,8 +50,16 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
expected = %{
content: nil,
- actor: AccountView.render("account.json", %{user: user}),
- account: AccountView.render("account.json", %{user: other_user}),
+ actor:
+ Map.merge(
+ AccountView.render("account.json", %{user: user}),
+ Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user})
+ ),
+ account:
+ Map.merge(
+ AccountView.render("account.json", %{user: other_user}),
+ Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: other_user})
+ ),
statuses: [StatusView.render("status.json", %{activity: activity})],
state: "open",
id: report_activity.id
@@ -95,4 +111,20 @@ defmodule Pleroma.Web.AdminAPI.ReportViewTest do
refute "<script> alert('hecked :D:D:D:D:D:D:D') </script>" ==
ReportView.render("show.json", %{report: activity})[:content]
end
+
+ test "doesn't error out when the user doesn't exists" do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.report(user, %{
+ "account_id" => other_user.id,
+ "comment" => ""
+ })
+
+ Pleroma.User.delete(other_user)
+ Pleroma.User.invalidate_cache(other_user)
+
+ assert %{} = ReportView.render("show.json", %{report: activity})
+ end
end
diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs
index 7ff23b63d..6f57bbe1f 100644
--- a/test/web/common_api/common_api_test.exs
+++ b/test/web/common_api/common_api_test.exs
@@ -121,7 +121,7 @@ defmodule Pleroma.Web.CommonAPITest do
})
Enum.each(["public", "private", "unlisted"], fn visibility ->
- assert {:error, {:private_to_public, _}} =
+ assert {:error, "The message visibility must be direct"} =
CommonAPI.post(user, %{
"status" => "suya..",
"visibility" => visibility,
@@ -188,6 +188,11 @@ defmodule Pleroma.Web.CommonAPITest do
assert %User{info: %{pinned_activities: [^id]}} = user
end
+ test "unlisted statuses can be pinned", %{user: user} do
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!", "visibility" => "unlisted"})
+ assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
+ end
+
test "only self-authored can be pinned", %{activity: activity} do
user = insert(:user)
diff --git a/test/web/federator_test.exs b/test/web/federator_test.exs
index 0f43bc8f2..69dd4d747 100644
--- a/test/web/federator_test.exs
+++ b/test/web/federator_test.exs
@@ -12,6 +12,13 @@ defmodule Pleroma.Web.FederatorTest do
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+ config_path = [:instance, :federating]
+ initial_setting = Pleroma.Config.get(config_path)
+
+ Pleroma.Config.put(config_path, true)
+ on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
+
:ok
end
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index de157d529..64f14f794 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -96,56 +96,186 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|> json_response(403) == %{"error" => "This resource requires authentication."}
end
- test "posting a status", %{conn: conn} do
- user = insert(:user)
+ describe "posting statuses" do
+ setup do
+ user = insert(:user)
- idempotency_key = "Pikachu rocks!"
+ conn =
+ build_conn()
+ |> assign(:user, user)
- conn_one =
- conn
- |> assign(:user, user)
- |> put_req_header("idempotency-key", idempotency_key)
- |> post("/api/v1/statuses", %{
- "status" => "cofe",
- "spoiler_text" => "2hu",
- "sensitive" => "false"
- })
+ [conn: conn]
+ end
- {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
- # Six hours
- assert ttl > :timer.seconds(6 * 60 * 60 - 1)
+ test "posting a status", %{conn: conn} do
+ idempotency_key = "Pikachu rocks!"
- assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
- json_response(conn_one, 200)
+ conn_one =
+ conn
+ |> put_req_header("idempotency-key", idempotency_key)
+ |> post("/api/v1/statuses", %{
+ "status" => "cofe",
+ "spoiler_text" => "2hu",
+ "sensitive" => "false"
+ })
- assert Activity.get_by_id(id)
+ {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
+ # Six hours
+ assert ttl > :timer.seconds(6 * 60 * 60 - 1)
- conn_two =
- conn
- |> assign(:user, user)
- |> put_req_header("idempotency-key", idempotency_key)
- |> post("/api/v1/statuses", %{
- "status" => "cofe",
- "spoiler_text" => "2hu",
- "sensitive" => "false"
- })
+ assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
+ json_response(conn_one, 200)
- assert %{"id" => second_id} = json_response(conn_two, 200)
+ assert Activity.get_by_id(id)
- assert id == second_id
+ conn_two =
+ conn
+ |> put_req_header("idempotency-key", idempotency_key)
+ |> post("/api/v1/statuses", %{
+ "status" => "cofe",
+ "spoiler_text" => "2hu",
+ "sensitive" => "false"
+ })
- conn_three =
- conn
- |> assign(:user, user)
- |> post("/api/v1/statuses", %{
- "status" => "cofe",
- "spoiler_text" => "2hu",
- "sensitive" => "false"
- })
+ assert %{"id" => second_id} = json_response(conn_two, 200)
+ assert id == second_id
+
+ conn_three =
+ conn
+ |> post("/api/v1/statuses", %{
+ "status" => "cofe",
+ "spoiler_text" => "2hu",
+ "sensitive" => "false"
+ })
+
+ assert %{"id" => third_id} = json_response(conn_three, 200)
+ refute id == third_id
+ end
+
+ test "replying to a status", %{conn: conn} do
+ user = insert(:user)
+ {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
+
+ conn =
+ conn
+ |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
+
+ assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
+
+ activity = Activity.get_by_id(id)
+
+ assert activity.data["context"] == replied_to.data["context"]
+ assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
+ end
+
+ test "replying to a direct message with visibility other than direct", %{conn: conn} do
+ user = insert(:user)
+ {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
+
+ Enum.each(["public", "private", "unlisted"], fn visibility ->
+ conn =
+ conn
+ |> post("/api/v1/statuses", %{
+ "status" => "@#{user.nickname} hey",
+ "in_reply_to_id" => replied_to.id,
+ "visibility" => visibility
+ })
+
+ assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
+ end)
+ end
+
+ test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
+ conn =
+ conn
+ |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
+
+ assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
+ assert Activity.get_by_id(id)
+ end
+
+ test "posting a sensitive status", %{conn: conn} do
+ conn =
+ conn
+ |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
+
+ assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
+ assert Activity.get_by_id(id)
+ end
+
+ test "posting a fake status", %{conn: conn} do
+ real_conn =
+ conn
+ |> 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
+ |> 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
+ Pleroma.Config.put([:rich_media, :enabled], true)
+
+ conn =
+ conn
+ |> post("/api/v1/statuses", %{
+ "status" => "https://example.com/ogp"
+ })
+
+ assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
+ assert Activity.get_by_id(id)
+ Pleroma.Config.put([:rich_media, :enabled], false)
+ end
+
+ test "posting a direct status", %{conn: conn} do
+ user2 = insert(:user)
+ content = "direct cofe @#{user2.nickname}"
- assert %{"id" => third_id} = json_response(conn_three, 200)
+ conn =
+ conn
+ |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
- refute id == third_id
+ assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
+ assert activity = Activity.get_by_id(id)
+ assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
+ assert activity.data["to"] == [user2.ap_id]
+ assert activity.data["cc"] == []
+ end
end
describe "posting polls" do
@@ -245,100 +375,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
end
end
- test "posting a sensitive status", %{conn: conn} do
- user = insert(:user)
-
- conn =
- conn
- |> assign(:user, user)
- |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
-
- assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
- 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
- Pleroma.Config.put([:rich_media, :enabled], true)
- user = insert(:user)
-
- conn =
- conn
- |> assign(:user, user)
- |> post("/api/v1/statuses", %{
- "status" => "http://example.com/ogp"
- })
-
- assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
- assert Activity.get_by_id(id)
- Pleroma.Config.put([:rich_media, :enabled], false)
- end
-
- test "posting a direct status", %{conn: conn} do
- user1 = insert(:user)
- user2 = insert(:user)
- content = "direct cofe @#{user2.nickname}"
-
- conn =
- conn
- |> assign(:user, user1)
- |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
-
- assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
- 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"] == []
- end
-
test "direct timeline", %{conn: conn} do
user_one = insert(:user)
user_two = insert(:user)
@@ -503,39 +539,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
assert status["id"] == direct.id
end
- test "replying to a status", %{conn: conn} do
- user = insert(:user)
-
- {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
-
- conn =
- conn
- |> assign(:user, user)
- |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
-
- assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
-
- activity = Activity.get_by_id(id)
-
- assert activity.data["context"] == replied_to.data["context"]
- assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
- end
-
- test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
- user = insert(:user)
-
- conn =
- conn
- |> assign(:user, user)
- |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
-
- assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
-
- activity = Activity.get_by_id(id)
-
- assert activity
- end
-
test "verify_credentials", %{conn: conn} do
user = insert(:user)
@@ -1502,6 +1505,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
assert [%{"id" => id}] = json_response(conn, 200)
assert id == to_string(post.id)
end
+
+ test "filters user's statuses by a hashtag", %{conn: conn} do
+ user = insert(:user)
+ {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
+ {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
+
+ conn =
+ conn
+ |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
+
+ assert [%{"id" => id}] = json_response(conn, 200)
+ assert id == to_string(post.id)
+ end
end
describe "user relationships" do
@@ -2654,7 +2670,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
end
test "returns rich-media card", %{conn: conn, user: user} do
- {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
card_data = %{
"image" => "http://ia.media-imdb.com/images/rock.jpg",
@@ -2686,7 +2702,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
# works with private posts
{:ok, activity} =
- CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
+ CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
response_two =
conn
@@ -2698,7 +2714,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
end
test "replaces missing description with an empty string", %{conn: conn, user: user} do
- {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp-missing-data"})
+ {:ok, activity} =
+ CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
response =
conn
diff --git a/test/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs
index ec75150ab..73791a95b 100644
--- a/test/web/mastodon_api/status_view_test.exs
+++ b/test/web/mastodon_api/status_view_test.exs
@@ -444,4 +444,39 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
assert Enum.at(result[:options], 2)[:votes_count] == 1
end
end
+
+ test "embeds a relationship in the account" do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ "status" => "drink more water"
+ })
+
+ result = StatusView.render("status.json", %{activity: activity, for: other_user})
+
+ assert result[:account][:pleroma][:relationship] ==
+ AccountView.render("relationship.json", %{user: other_user, target: user})
+ end
+
+ test "embeds a relationship in the account in reposts" do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ "status" => "˙˙ɐʎns"
+ })
+
+ {:ok, activity, _object} = CommonAPI.repeat(activity.id, other_user)
+
+ result = StatusView.render("status.json", %{activity: activity, for: user})
+
+ assert result[:account][:pleroma][:relationship] ==
+ AccountView.render("relationship.json", %{user: user, target: other_user})
+
+ assert result[:reblog][:account][:pleroma][:relationship] ==
+ AccountView.render("relationship.json", %{user: user, target: user})
+ end
end
diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs
index 7441e5fce..eae44dba5 100644
--- a/test/web/ostatus/ostatus_controller_test.exs
+++ b/test/web/ostatus/ostatus_controller_test.exs
@@ -12,6 +12,13 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+ config_path = [:instance, :federating]
+ initial_setting = Pleroma.Config.get(config_path)
+
+ Pleroma.Config.put(config_path, true)
+ on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
+
:ok
end
diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs
index f6be16862..acce33008 100644
--- a/test/web/ostatus/ostatus_test.exs
+++ b/test/web/ostatus/ostatus_test.exs
@@ -11,8 +11,10 @@ defmodule Pleroma.Web.OStatusTest do
alias Pleroma.User
alias Pleroma.Web.OStatus
alias Pleroma.Web.XML
- import Pleroma.Factory
+
import ExUnit.CaptureLog
+ import Mock
+ import Pleroma.Factory
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -266,10 +268,13 @@ defmodule Pleroma.Web.OStatusTest do
assert favorited_activity.local
end
- test "handle incoming replies" do
+ test_with_mock "handle incoming replies, fetching replied-to activities if we don't have them",
+ OStatus,
+ [:passthrough],
+ [] do
incoming = File.read!("test/fixtures/incoming_note_activity_answer.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming)
- object = Object.normalize(activity.data["object"])
+ object = Object.normalize(activity.data["object"], false)
assert activity.data["type"] == "Create"
assert object.data["type"] == "Note"
@@ -282,6 +287,23 @@ defmodule Pleroma.Web.OStatusTest do
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"]
+
+ assert called(OStatus.fetch_activity_from_url(object.data["inReplyTo"], :_))
+ end
+
+ test_with_mock "handle incoming replies, not fetching replied-to activities beyond max_replies_depth",
+ OStatus,
+ [:passthrough],
+ [] do
+ incoming = File.read!("test/fixtures/incoming_note_activity_answer.xml")
+
+ with_mock Pleroma.Web.Federator,
+ allowed_incoming_reply_depth?: fn _ -> false end do
+ {:ok, [activity]} = OStatus.handle_incoming(incoming)
+ object = Object.normalize(activity.data["object"], false)
+
+ refute called(OStatus.fetch_activity_from_url(object.data["inReplyTo"], :_))
+ end
end
test "handle incoming follows" do
diff --git a/test/web/plugs/federating_plug_test.exs b/test/web/plugs/federating_plug_test.exs
index 530562325..c01e01124 100644
--- a/test/web/plugs/federating_plug_test.exs
+++ b/test/web/plugs/federating_plug_test.exs
@@ -5,6 +5,15 @@
defmodule Pleroma.Web.FederatingPlugTest do
use Pleroma.Web.ConnCase
+ setup_all do
+ config_path = [:instance, :federating]
+ initial_setting = Pleroma.Config.get(config_path)
+
+ on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
+
+ :ok
+ end
+
test "returns and halt the conn when federating is disabled" do
Pleroma.Config.put([:instance, :federating], false)
@@ -14,11 +23,11 @@ defmodule Pleroma.Web.FederatingPlugTest do
assert conn.status == 404
assert conn.halted
-
- Pleroma.Config.put([:instance, :federating], true)
end
test "does nothing when federating is enabled" do
+ Pleroma.Config.put([:instance, :federating], true)
+
conn =
build_conn()
|> Pleroma.Web.FederatingPlug.call(%{})
diff --git a/test/web/rich_media/helpers_test.exs b/test/web/rich_media/helpers_test.exs
index 53b0596f5..c8f442b05 100644
--- a/test/web/rich_media/helpers_test.exs
+++ b/test/web/rich_media/helpers_test.exs
@@ -1,14 +1,19 @@
defmodule Pleroma.Web.RichMedia.HelpersTest do
use Pleroma.DataCase
+ alias Pleroma.Config
alias Pleroma.Object
alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.RichMedia.Helpers
import Pleroma.Factory
import Tesla.Mock
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ rich_media = Config.get([:rich_media, :enabled])
+ on_exit(fn -> Config.put([:rich_media, :enabled], rich_media) end)
+
:ok
end
@@ -21,11 +26,9 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do
"content_type" => "text/markdown"
})
- Pleroma.Config.put([:rich_media, :enabled], true)
+ Config.put([:rich_media, :enabled], true)
assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
-
- Pleroma.Config.put([:rich_media, :enabled], false)
end
test "refuses to crawl malformed URLs" do
@@ -37,11 +40,9 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do
"content_type" => "text/markdown"
})
- Pleroma.Config.put([:rich_media, :enabled], true)
+ Config.put([:rich_media, :enabled], true)
assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
-
- Pleroma.Config.put([:rich_media, :enabled], false)
end
test "crawls valid, complete URLs" do
@@ -49,16 +50,14 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do
{:ok, activity} =
CommonAPI.post(user, %{
- "status" => "[test](http://example.com/ogp)",
+ "status" => "[test](https://example.com/ogp)",
"content_type" => "text/markdown"
})
- Pleroma.Config.put([:rich_media, :enabled], true)
+ Config.put([:rich_media, :enabled], true)
- assert %{page_url: "http://example.com/ogp", rich_media: _} =
+ assert %{page_url: "https://example.com/ogp", rich_media: _} =
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
-
- Pleroma.Config.put([:rich_media, :enabled], false)
end
test "refuses to crawl URLs from posts marked sensitive" do
@@ -74,11 +73,9 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do
assert object.data["sensitive"]
- Pleroma.Config.put([:rich_media, :enabled], true)
+ Config.put([:rich_media, :enabled], true)
assert %{} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
-
- Pleroma.Config.put([:rich_media, :enabled], false)
end
test "refuses to crawl URLs from posts tagged NSFW" do
@@ -93,10 +90,28 @@ defmodule Pleroma.Web.RichMedia.HelpersTest do
assert object.data["sensitive"]
- Pleroma.Config.put([:rich_media, :enabled], true)
+ Config.put([:rich_media, :enabled], true)
assert %{} = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
+ end
+
+ test "refuses to crawl URLs of private network from posts" do
+ user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{"status" => "http://127.0.0.1:4000/notice/9kCP7VNyPJXFOXDrgO"})
+
+ {:ok, activity2} = CommonAPI.post(user, %{"status" => "https://10.111.10.1/notice/9kCP7V"})
+ {:ok, activity3} = CommonAPI.post(user, %{"status" => "https://172.16.32.40/notice/9kCP7V"})
+ {:ok, activity4} = CommonAPI.post(user, %{"status" => "https://192.168.10.40/notice/9kCP7V"})
+ {:ok, activity5} = CommonAPI.post(user, %{"status" => "https://pleroma.local/notice/9kCP7V"})
+
+ Config.put([:rich_media, :enabled], true)
- Pleroma.Config.put([:rich_media, :enabled], false)
+ assert %{} = Helpers.fetch_data_for_activity(activity)
+ assert %{} = Helpers.fetch_data_for_activity(activity2)
+ assert %{} = Helpers.fetch_data_for_activity(activity3)
+ assert %{} = Helpers.fetch_data_for_activity(activity4)
+ assert %{} = Helpers.fetch_data_for_activity(activity5)
end
end
diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs
index 3a9cc1854..bc48341ca 100644
--- a/test/web/rich_media/parser_test.exs
+++ b/test/web/rich_media/parser_test.exs
@@ -11,6 +11,21 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
%{
method: :get,
+ url: "http://example.com/non-ogp"
+ } ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/non_ogp_embed.html")}
+
+ %{
+ method: :get,
+ url: "http://example.com/ogp-missing-title"
+ } ->
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/rich_media/ogp-missing-title.html")
+ }
+
+ %{
+ method: :get,
url: "http://example.com/twitter-card"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}
@@ -38,6 +53,11 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
assert {:error, _} = Pleroma.Web.RichMedia.Parser.parse("http://example.com/empty")
end
+ test "doesn't just add a title" do
+ assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/non-ogp") ==
+ {:error, "Found metadata was invalid or incomplete: %{}"}
+ end
+
test "parses ogp" do
assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp") ==
{:ok,
@@ -51,6 +71,19 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
}}
end
+ test "falls back to <title> when ogp:title is missing" do
+ assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp-missing-title") ==
+ {:ok,
+ %{
+ image: "http://ia.media-imdb.com/images/rock.jpg",
+ title: "The Rock (1996)",
+ description:
+ "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
+ type: "video.movie",
+ url: "http://www.imdb.com/title/tt0117500/"
+ }}
+ end
+
test "parses twitter card" do
assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/twitter-card") ==
{:ok,
diff --git a/test/web/streamer_test.exs b/test/web/streamer_test.exs
index 648e28712..4633d7765 100644
--- a/test/web/streamer_test.exs
+++ b/test/web/streamer_test.exs
@@ -356,4 +356,110 @@ defmodule Pleroma.Web.StreamerTest do
Task.await(task)
end
+
+ describe "direct streams" do
+ setup do
+ GenServer.start(Streamer, %{}, name: Streamer)
+
+ on_exit(fn ->
+ if pid = Process.whereis(Streamer) do
+ Process.exit(pid, :kill)
+ end
+ end)
+
+ :ok
+ end
+
+ test "it sends conversation update to the 'direct' stream", %{} do
+ user = insert(:user)
+ another_user = insert(:user)
+
+ task =
+ Task.async(fn ->
+ assert_receive {:text, _received_event}, 4_000
+ end)
+
+ Streamer.add_socket(
+ "direct",
+ %{transport_pid: task.pid, assigns: %{user: user}}
+ )
+
+ {:ok, _create_activity} =
+ CommonAPI.post(another_user, %{
+ "status" => "hey @#{user.nickname}",
+ "visibility" => "direct"
+ })
+
+ Task.await(task)
+ end
+
+ test "it doesn't send conversation update to the 'direct' streamj when the last message in the conversation is deleted" do
+ user = insert(:user)
+ another_user = insert(:user)
+
+ {:ok, create_activity} =
+ CommonAPI.post(another_user, %{
+ "status" => "hi @#{user.nickname}",
+ "visibility" => "direct"
+ })
+
+ task =
+ Task.async(fn ->
+ assert_receive {:text, received_event}, 4_000
+ assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event)
+
+ refute_receive {:text, _}, 4_000
+ end)
+
+ Streamer.add_socket(
+ "direct",
+ %{transport_pid: task.pid, assigns: %{user: user}}
+ )
+
+ {:ok, _} = CommonAPI.delete(create_activity.id, another_user)
+
+ Task.await(task)
+ end
+
+ test "it sends conversation update to the 'direct' stream when a message is deleted" do
+ user = insert(:user)
+ another_user = insert(:user)
+
+ {:ok, create_activity} =
+ CommonAPI.post(another_user, %{
+ "status" => "hi @#{user.nickname}",
+ "visibility" => "direct"
+ })
+
+ {:ok, create_activity2} =
+ CommonAPI.post(another_user, %{
+ "status" => "hi @#{user.nickname}",
+ "in_reply_to_status_id" => create_activity.id,
+ "visibility" => "direct"
+ })
+
+ task =
+ Task.async(fn ->
+ assert_receive {:text, received_event}, 4_000
+ assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event)
+
+ assert_receive {:text, received_event}, 4_000
+
+ assert %{"event" => "conversation", "payload" => received_payload} =
+ Jason.decode!(received_event)
+
+ assert %{"last_status" => last_status} = Jason.decode!(received_payload)
+ assert last_status["id"] == to_string(create_activity.id)
+ end)
+
+ Streamer.add_socket(
+ "direct",
+ %{transport_pid: task.pid, assigns: %{user: user}}
+ )
+
+ {:ok, _} = CommonAPI.delete(create_activity2.id, another_user)
+
+ Task.await(task)
+ end
+ end
end
diff --git a/test/web/twitter_api/password_controller_test.exs b/test/web/twitter_api/password_controller_test.exs
new file mode 100644
index 000000000..6b9da8204
--- /dev/null
+++ b/test/web/twitter_api/password_controller_test.exs
@@ -0,0 +1,56 @@
+defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.PasswordResetToken
+ alias Pleroma.Web.OAuth.Token
+ import Pleroma.Factory
+
+ describe "GET /api/pleroma/password_reset/token" do
+ test "it returns error when token invalid", %{conn: conn} do
+ response =
+ conn
+ |> get("/api/pleroma/password_reset/token")
+ |> html_response(:ok)
+
+ assert response =~ "<h2>Invalid Token</h2>"
+ end
+
+ test "it shows password reset form", %{conn: conn} do
+ user = insert(:user)
+ {:ok, token} = PasswordResetToken.create_token(user)
+
+ response =
+ conn
+ |> get("/api/pleroma/password_reset/#{token.token}")
+ |> html_response(:ok)
+
+ assert response =~ "<h2>Password Reset for #{user.nickname}</h2>"
+ end
+ end
+
+ describe "POST /api/pleroma/password_reset" do
+ test "it returns HTTP 200", %{conn: conn} do
+ user = insert(:user)
+ {:ok, token} = PasswordResetToken.create_token(user)
+ {:ok, _access_token} = Token.create_token(insert(:oauth_app), user, %{})
+
+ params = %{
+ "password" => "test",
+ password_confirmation: "test",
+ token: token.token
+ }
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post("/api/pleroma/password_reset", %{data: params})
+ |> html_response(:ok)
+
+ assert response =~ "<h2>Password changed!</h2>"
+
+ user = refresh_record(user)
+ assert Comeonin.Pbkdf2.checkpw("test", user.password_hash)
+ assert length(Token.get_user_tokens(user)) == 0
+ end
+ end
+end
diff --git a/test/web/web_finger/web_finger_controller_test.exs b/test/web/web_finger/web_finger_controller_test.exs
index 43fccfc7a..a14ed3126 100644
--- a/test/web/web_finger/web_finger_controller_test.exs
+++ b/test/web/web_finger/web_finger_controller_test.exs
@@ -10,6 +10,12 @@ defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+ config_path = [:instance, :federating]
+ initial_setting = Pleroma.Config.get(config_path)
+
+ Pleroma.Config.put(config_path, true)
+ on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
:ok
end
diff --git a/test/web/websub/websub_controller_test.exs b/test/web/websub/websub_controller_test.exs
index f79745d58..aa7262beb 100644
--- a/test/web/websub/websub_controller_test.exs
+++ b/test/web/websub/websub_controller_test.exs
@@ -9,6 +9,16 @@ defmodule Pleroma.Web.Websub.WebsubControllerTest do
alias Pleroma.Web.Websub
alias Pleroma.Web.Websub.WebsubClientSubscription
+ setup_all do
+ config_path = [:instance, :federating]
+ initial_setting = Pleroma.Config.get(config_path)
+
+ Pleroma.Config.put(config_path, true)
+ on_exit(fn -> Pleroma.Config.put(config_path, initial_setting) end)
+
+ :ok
+ end
+
test "websub subscription request", %{conn: conn} do
user = insert(:user)