1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.Notification
13 alias Pleroma.ScheduledActivity
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.MastodonAPI.FilterView
18 alias Pleroma.Web.OAuth.App
19 alias Pleroma.Web.OAuth.Token
20 alias Pleroma.Web.OStatus
21 alias Pleroma.Web.Push
22 alias Pleroma.Web.TwitterAPI.TwitterAPI
23 import Pleroma.Factory
24 import ExUnit.CaptureLog
26 import Swoosh.TestAssertions
28 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
31 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
35 test "the home timeline", %{conn: conn} do
37 following = insert(:user)
39 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
43 |> assign(:user, user)
44 |> get("/api/v1/timelines/home")
46 assert Enum.empty?(json_response(conn, 200))
48 {:ok, user} = User.follow(user, following)
52 |> assign(:user, user)
53 |> get("/api/v1/timelines/home")
55 assert [%{"content" => "test"}] = json_response(conn, 200)
58 test "the public timeline", %{conn: conn} do
59 following = insert(:user)
62 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
65 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
69 |> get("/api/v1/timelines/public", %{"local" => "False"})
71 assert length(json_response(conn, 200)) == 2
75 |> get("/api/v1/timelines/public", %{"local" => "True"})
77 assert [%{"content" => "test"}] = json_response(conn, 200)
81 |> get("/api/v1/timelines/public", %{"local" => "1"})
83 assert [%{"content" => "test"}] = json_response(conn, 200)
87 test "the public timeline when public is set to false", %{conn: conn} do
88 public = Pleroma.Config.get([:instance, :public])
89 Pleroma.Config.put([:instance, :public], false)
92 Pleroma.Config.put([:instance, :public], public)
96 |> get("/api/v1/timelines/public", %{"local" => "False"})
97 |> json_response(403) == %{"error" => "This resource requires authentication."}
100 describe "posting statuses" do
106 |> assign(:user, user)
111 test "posting a status", %{conn: conn} do
112 idempotency_key = "Pikachu rocks!"
116 |> put_req_header("idempotency-key", idempotency_key)
117 |> post("/api/v1/statuses", %{
119 "spoiler_text" => "2hu",
120 "sensitive" => "false"
123 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
125 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
127 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
128 json_response(conn_one, 200)
130 assert Activity.get_by_id(id)
134 |> put_req_header("idempotency-key", idempotency_key)
135 |> post("/api/v1/statuses", %{
137 "spoiler_text" => "2hu",
138 "sensitive" => "false"
141 assert %{"id" => second_id} = json_response(conn_two, 200)
142 assert id == second_id
146 |> post("/api/v1/statuses", %{
148 "spoiler_text" => "2hu",
149 "sensitive" => "false"
152 assert %{"id" => third_id} = json_response(conn_three, 200)
153 refute id == third_id
156 test "replying to a status", %{conn: conn} do
158 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
162 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
164 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
166 activity = Activity.get_by_id(id)
168 assert activity.data["context"] == replied_to.data["context"]
169 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
172 test "replying to a direct message with visibility other than direct", %{conn: conn} do
174 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
176 Enum.each(["public", "private", "unlisted"], fn visibility ->
179 |> post("/api/v1/statuses", %{
180 "status" => "@#{user.nickname} hey",
181 "in_reply_to_id" => replied_to.id,
182 "visibility" => visibility
185 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
189 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
192 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
194 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
195 assert Activity.get_by_id(id)
198 test "posting a sensitive status", %{conn: conn} do
201 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
203 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
204 assert Activity.get_by_id(id)
207 test "posting a fake status", %{conn: conn} do
210 |> post("/api/v1/statuses", %{
212 "\"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"
215 real_status = json_response(real_conn, 200)
218 assert Object.get_by_ap_id(real_status["uri"])
222 |> Map.put("id", nil)
223 |> Map.put("url", nil)
224 |> Map.put("uri", nil)
225 |> Map.put("created_at", nil)
226 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
230 |> post("/api/v1/statuses", %{
232 "\"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",
236 fake_status = json_response(fake_conn, 200)
239 refute Object.get_by_ap_id(fake_status["uri"])
243 |> Map.put("id", nil)
244 |> Map.put("url", nil)
245 |> Map.put("uri", nil)
246 |> Map.put("created_at", nil)
247 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
249 assert real_status == fake_status
252 test "posting a status with OGP link preview", %{conn: conn} do
253 Pleroma.Config.put([:rich_media, :enabled], true)
257 |> post("/api/v1/statuses", %{
258 "status" => "https://example.com/ogp"
261 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
262 assert Activity.get_by_id(id)
263 Pleroma.Config.put([:rich_media, :enabled], false)
266 test "posting a direct status", %{conn: conn} do
267 user2 = insert(:user)
268 content = "direct cofe @#{user2.nickname}"
272 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
274 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
275 assert activity = Activity.get_by_id(id)
276 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
277 assert activity.data["to"] == [user2.ap_id]
278 assert activity.data["cc"] == []
282 describe "posting polls" do
283 test "posting a poll", %{conn: conn} do
285 time = NaiveDateTime.utc_now()
289 |> assign(:user, user)
290 |> post("/api/v1/statuses", %{
291 "status" => "Who is the #bestgrill?",
292 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
295 response = json_response(conn, 200)
297 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
298 title in ["Rei", "Asuka", "Misato"]
301 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
302 refute response["poll"]["expred"]
305 test "option limit is enforced", %{conn: conn} do
307 limit = Pleroma.Config.get([:instance, :poll_limits, :max_options])
311 |> assign(:user, user)
312 |> post("/api/v1/statuses", %{
314 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
317 %{"error" => error} = json_response(conn, 422)
318 assert error == "Poll can't contain more than #{limit} options"
321 test "option character limit is enforced", %{conn: conn} do
323 limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars])
327 |> assign(:user, user)
328 |> post("/api/v1/statuses", %{
331 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
336 %{"error" => error} = json_response(conn, 422)
337 assert error == "Poll options cannot be longer than #{limit} characters each"
340 test "minimal date limit is enforced", %{conn: conn} do
342 limit = Pleroma.Config.get([:instance, :poll_limits, :min_expiration])
346 |> assign(:user, user)
347 |> post("/api/v1/statuses", %{
348 "status" => "imagine arbitrary limits",
350 "options" => ["this post was made by pleroma gang"],
351 "expires_in" => limit - 1
355 %{"error" => error} = json_response(conn, 422)
356 assert error == "Expiration date is too soon"
359 test "maximum date limit is enforced", %{conn: conn} do
361 limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration])
365 |> assign(:user, user)
366 |> post("/api/v1/statuses", %{
367 "status" => "imagine arbitrary limits",
369 "options" => ["this post was made by pleroma gang"],
370 "expires_in" => limit + 1
374 %{"error" => error} = json_response(conn, 422)
375 assert error == "Expiration date is too far in the future"
379 test "direct timeline", %{conn: conn} do
380 user_one = insert(:user)
381 user_two = insert(:user)
383 {:ok, user_two} = User.follow(user_two, user_one)
386 CommonAPI.post(user_one, %{
387 "status" => "Hi @#{user_two.nickname}!",
388 "visibility" => "direct"
391 {:ok, _follower_only} =
392 CommonAPI.post(user_one, %{
393 "status" => "Hi @#{user_two.nickname}!",
394 "visibility" => "private"
397 # Only direct should be visible here
400 |> assign(:user, user_two)
401 |> get("api/v1/timelines/direct")
403 [status] = json_response(res_conn, 200)
405 assert %{"visibility" => "direct"} = status
406 assert status["url"] != direct.data["id"]
408 # User should be able to see his own direct message
411 |> assign(:user, user_one)
412 |> get("api/v1/timelines/direct")
414 [status] = json_response(res_conn, 200)
416 assert %{"visibility" => "direct"} = status
418 # Both should be visible here
421 |> assign(:user, user_two)
422 |> get("api/v1/timelines/home")
424 [_s1, _s2] = json_response(res_conn, 200)
427 Enum.each(1..20, fn _ ->
429 CommonAPI.post(user_one, %{
430 "status" => "Hi @#{user_two.nickname}!",
431 "visibility" => "direct"
437 |> assign(:user, user_two)
438 |> get("api/v1/timelines/direct")
440 statuses = json_response(res_conn, 200)
441 assert length(statuses) == 20
445 |> assign(:user, user_two)
446 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
448 [status] = json_response(res_conn, 200)
450 assert status["url"] != direct.data["id"]
453 test "Conversations", %{conn: conn} do
454 user_one = insert(:user)
455 user_two = insert(:user)
456 user_three = insert(:user)
458 {:ok, user_two} = User.follow(user_two, user_one)
461 CommonAPI.post(user_one, %{
462 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
463 "visibility" => "direct"
466 {:ok, _follower_only} =
467 CommonAPI.post(user_one, %{
468 "status" => "Hi @#{user_two.nickname}!",
469 "visibility" => "private"
474 |> assign(:user, user_one)
475 |> get("/api/v1/conversations")
477 assert response = json_response(res_conn, 200)
482 "accounts" => res_accounts,
483 "last_status" => res_last_status,
488 account_ids = Enum.map(res_accounts, & &1["id"])
489 assert length(res_accounts) == 2
490 assert user_two.id in account_ids
491 assert user_three.id in account_ids
492 assert is_binary(res_id)
493 assert unread == true
494 assert res_last_status["id"] == direct.id
496 # Apparently undocumented API endpoint
499 |> assign(:user, user_one)
500 |> post("/api/v1/conversations/#{res_id}/read")
502 assert response = json_response(res_conn, 200)
503 assert length(response["accounts"]) == 2
504 assert response["last_status"]["id"] == direct.id
505 assert response["unread"] == false
507 # (vanilla) Mastodon frontend behaviour
510 |> assign(:user, user_one)
511 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
513 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
516 test "doesn't include DMs from blocked users", %{conn: conn} do
517 blocker = insert(:user)
518 blocked = insert(:user)
520 {:ok, blocker} = User.block(blocker, blocked)
522 {:ok, _blocked_direct} =
523 CommonAPI.post(blocked, %{
524 "status" => "Hi @#{blocker.nickname}!",
525 "visibility" => "direct"
529 CommonAPI.post(user, %{
530 "status" => "Hi @#{blocker.nickname}!",
531 "visibility" => "direct"
536 |> assign(:user, user)
537 |> get("api/v1/timelines/direct")
539 [status] = json_response(res_conn, 200)
540 assert status["id"] == direct.id
543 test "verify_credentials", %{conn: conn} do
548 |> assign(:user, user)
549 |> get("/api/v1/accounts/verify_credentials")
551 response = json_response(conn, 200)
553 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
554 assert response["pleroma"]["chat_token"]
555 assert id == to_string(user.id)
558 test "verify_credentials default scope unlisted", %{conn: conn} do
559 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
563 |> assign(:user, user)
564 |> get("/api/v1/accounts/verify_credentials")
566 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
567 assert id == to_string(user.id)
570 test "apps/verify_credentials", %{conn: conn} do
571 token = insert(:oauth_token)
575 |> assign(:user, token.user)
576 |> assign(:token, token)
577 |> get("/api/v1/apps/verify_credentials")
579 app = Repo.preload(token, :app).app
582 "name" => app.client_name,
583 "website" => app.website,
584 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
587 assert expected == json_response(conn, 200)
590 test "user avatar can be set", %{conn: conn} do
592 avatar_image = File.read!("test/fixtures/avatar_data_uri")
596 |> assign(:user, user)
597 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
599 user = refresh_record(user)
613 assert %{"url" => _} = json_response(conn, 200)
616 test "user avatar can be reset", %{conn: conn} do
621 |> assign(:user, user)
622 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
624 user = User.get_cached_by_id(user.id)
626 assert user.avatar == nil
628 assert %{"url" => nil} = json_response(conn, 200)
631 test "can set profile banner", %{conn: conn} do
636 |> assign(:user, user)
637 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
639 user = refresh_record(user)
640 assert user.info.banner["type"] == "Image"
642 assert %{"url" => _} = json_response(conn, 200)
645 test "can reset profile banner", %{conn: conn} do
650 |> assign(:user, user)
651 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
653 user = refresh_record(user)
654 assert user.info.banner == %{}
656 assert %{"url" => nil} = json_response(conn, 200)
659 test "background image can be set", %{conn: conn} do
664 |> assign(:user, user)
665 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
667 user = refresh_record(user)
668 assert user.info.background["type"] == "Image"
669 assert %{"url" => _} = json_response(conn, 200)
672 test "background image can be reset", %{conn: conn} do
677 |> assign(:user, user)
678 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
680 user = refresh_record(user)
681 assert user.info.background == %{}
682 assert %{"url" => nil} = json_response(conn, 200)
685 test "creates an oauth app", %{conn: conn} do
687 app_attrs = build(:oauth_app)
691 |> assign(:user, user)
692 |> post("/api/v1/apps", %{
693 client_name: app_attrs.client_name,
694 redirect_uris: app_attrs.redirect_uris
697 [app] = Repo.all(App)
700 "name" => app.client_name,
701 "website" => app.website,
702 "client_id" => app.client_id,
703 "client_secret" => app.client_secret,
704 "id" => app.id |> to_string(),
705 "redirect_uri" => app.redirect_uris,
706 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
709 assert expected == json_response(conn, 200)
712 test "get a status", %{conn: conn} do
713 activity = insert(:note_activity)
717 |> get("/api/v1/statuses/#{activity.id}")
719 assert %{"id" => id} = json_response(conn, 200)
720 assert id == to_string(activity.id)
723 describe "deleting a status" do
724 test "when you created it", %{conn: conn} do
725 activity = insert(:note_activity)
726 author = User.get_cached_by_ap_id(activity.data["actor"])
730 |> assign(:user, author)
731 |> delete("/api/v1/statuses/#{activity.id}")
733 assert %{} = json_response(conn, 200)
735 refute Activity.get_by_id(activity.id)
738 test "when you didn't create it", %{conn: conn} do
739 activity = insert(:note_activity)
744 |> assign(:user, user)
745 |> delete("/api/v1/statuses/#{activity.id}")
747 assert %{"error" => _} = json_response(conn, 403)
749 assert Activity.get_by_id(activity.id) == activity
752 test "when you're an admin or moderator", %{conn: conn} do
753 activity1 = insert(:note_activity)
754 activity2 = insert(:note_activity)
755 admin = insert(:user, info: %{is_admin: true})
756 moderator = insert(:user, info: %{is_moderator: true})
760 |> assign(:user, admin)
761 |> delete("/api/v1/statuses/#{activity1.id}")
763 assert %{} = json_response(res_conn, 200)
767 |> assign(:user, moderator)
768 |> delete("/api/v1/statuses/#{activity2.id}")
770 assert %{} = json_response(res_conn, 200)
772 refute Activity.get_by_id(activity1.id)
773 refute Activity.get_by_id(activity2.id)
777 describe "filters" do
778 test "creating a filter", %{conn: conn} do
781 filter = %Pleroma.Filter{
788 |> assign(:user, user)
789 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
791 assert response = json_response(conn, 200)
792 assert response["phrase"] == filter.phrase
793 assert response["context"] == filter.context
794 assert response["irreversible"] == false
795 assert response["id"] != nil
796 assert response["id"] != ""
799 test "fetching a list of filters", %{conn: conn} do
802 query_one = %Pleroma.Filter{
809 query_two = %Pleroma.Filter{
816 {:ok, filter_one} = Pleroma.Filter.create(query_one)
817 {:ok, filter_two} = Pleroma.Filter.create(query_two)
821 |> assign(:user, user)
822 |> get("/api/v1/filters")
823 |> json_response(200)
829 filters: [filter_two, filter_one]
833 test "get a filter", %{conn: conn} do
836 query = %Pleroma.Filter{
843 {:ok, filter} = Pleroma.Filter.create(query)
847 |> assign(:user, user)
848 |> get("/api/v1/filters/#{filter.filter_id}")
850 assert _response = json_response(conn, 200)
853 test "update a filter", %{conn: conn} do
856 query = %Pleroma.Filter{
863 {:ok, _filter} = Pleroma.Filter.create(query)
865 new = %Pleroma.Filter{
872 |> assign(:user, user)
873 |> put("/api/v1/filters/#{query.filter_id}", %{
878 assert response = json_response(conn, 200)
879 assert response["phrase"] == new.phrase
880 assert response["context"] == new.context
883 test "delete a filter", %{conn: conn} do
886 query = %Pleroma.Filter{
893 {:ok, filter} = Pleroma.Filter.create(query)
897 |> assign(:user, user)
898 |> delete("/api/v1/filters/#{filter.filter_id}")
900 assert response = json_response(conn, 200)
901 assert response == %{}
906 test "creating a list", %{conn: conn} do
911 |> assign(:user, user)
912 |> post("/api/v1/lists", %{"title" => "cuties"})
914 assert %{"title" => title} = json_response(conn, 200)
915 assert title == "cuties"
918 test "adding users to a list", %{conn: conn} do
920 other_user = insert(:user)
921 {:ok, list} = Pleroma.List.create("name", user)
925 |> assign(:user, user)
926 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
928 assert %{} == json_response(conn, 200)
929 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
930 assert following == [other_user.follower_address]
933 test "removing users from a list", %{conn: conn} do
935 other_user = insert(:user)
936 third_user = insert(:user)
937 {:ok, list} = Pleroma.List.create("name", user)
938 {:ok, list} = Pleroma.List.follow(list, other_user)
939 {:ok, list} = Pleroma.List.follow(list, third_user)
943 |> assign(:user, user)
944 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
946 assert %{} == json_response(conn, 200)
947 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
948 assert following == [third_user.follower_address]
951 test "listing users in a list", %{conn: conn} do
953 other_user = insert(:user)
954 {:ok, list} = Pleroma.List.create("name", user)
955 {:ok, list} = Pleroma.List.follow(list, other_user)
959 |> assign(:user, user)
960 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
962 assert [%{"id" => id}] = json_response(conn, 200)
963 assert id == to_string(other_user.id)
966 test "retrieving a list", %{conn: conn} do
968 {:ok, list} = Pleroma.List.create("name", user)
972 |> assign(:user, user)
973 |> get("/api/v1/lists/#{list.id}")
975 assert %{"id" => id} = json_response(conn, 200)
976 assert id == to_string(list.id)
979 test "renaming a list", %{conn: conn} do
981 {:ok, list} = Pleroma.List.create("name", user)
985 |> assign(:user, user)
986 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
988 assert %{"title" => name} = json_response(conn, 200)
989 assert name == "newname"
992 test "deleting a list", %{conn: conn} do
994 {:ok, list} = Pleroma.List.create("name", user)
998 |> assign(:user, user)
999 |> delete("/api/v1/lists/#{list.id}")
1001 assert %{} = json_response(conn, 200)
1002 assert is_nil(Repo.get(Pleroma.List, list.id))
1005 test "list timeline", %{conn: conn} do
1006 user = insert(:user)
1007 other_user = insert(:user)
1008 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1009 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1010 {:ok, list} = Pleroma.List.create("name", user)
1011 {:ok, list} = Pleroma.List.follow(list, other_user)
1015 |> assign(:user, user)
1016 |> get("/api/v1/timelines/list/#{list.id}")
1018 assert [%{"id" => id}] = json_response(conn, 200)
1020 assert id == to_string(activity_two.id)
1023 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1024 user = insert(:user)
1025 other_user = insert(:user)
1026 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1028 {:ok, _activity_two} =
1029 CommonAPI.post(other_user, %{
1030 "status" => "Marisa is cute.",
1031 "visibility" => "private"
1034 {:ok, list} = Pleroma.List.create("name", user)
1035 {:ok, list} = Pleroma.List.follow(list, other_user)
1039 |> assign(:user, user)
1040 |> get("/api/v1/timelines/list/#{list.id}")
1042 assert [%{"id" => id}] = json_response(conn, 200)
1044 assert id == to_string(activity_one.id)
1048 describe "notifications" do
1049 test "list of notifications", %{conn: conn} do
1050 user = insert(:user)
1051 other_user = insert(:user)
1053 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1055 {:ok, [_notification]} = Notification.create_notifications(activity)
1059 |> assign(:user, user)
1060 |> get("/api/v1/notifications")
1063 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1065 }\">@<span>#{user.nickname}</span></a></span>"
1067 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1068 assert response == expected_response
1071 test "getting a single notification", %{conn: conn} do
1072 user = insert(:user)
1073 other_user = insert(:user)
1075 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1077 {:ok, [notification]} = Notification.create_notifications(activity)
1081 |> assign(:user, user)
1082 |> get("/api/v1/notifications/#{notification.id}")
1085 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1087 }\">@<span>#{user.nickname}</span></a></span>"
1089 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1090 assert response == expected_response
1093 test "dismissing a single notification", %{conn: conn} do
1094 user = insert(:user)
1095 other_user = insert(:user)
1097 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1099 {:ok, [notification]} = Notification.create_notifications(activity)
1103 |> assign(:user, user)
1104 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1106 assert %{} = json_response(conn, 200)
1109 test "clearing all notifications", %{conn: conn} do
1110 user = insert(:user)
1111 other_user = insert(:user)
1113 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1115 {:ok, [_notification]} = Notification.create_notifications(activity)
1119 |> assign(:user, user)
1120 |> post("/api/v1/notifications/clear")
1122 assert %{} = json_response(conn, 200)
1126 |> assign(:user, user)
1127 |> get("/api/v1/notifications")
1129 assert all = json_response(conn, 200)
1133 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1134 user = insert(:user)
1135 other_user = insert(:user)
1137 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1138 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1139 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1140 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1142 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1143 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1144 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1145 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1149 |> assign(:user, user)
1154 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1156 result = json_response(conn_res, 200)
1157 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1162 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1164 result = json_response(conn_res, 200)
1165 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1170 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1172 result = json_response(conn_res, 200)
1173 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1176 test "filters notifications using exclude_types", %{conn: conn} do
1177 user = insert(:user)
1178 other_user = insert(:user)
1180 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1181 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1182 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1183 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1184 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1186 mention_notification_id =
1187 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1189 favorite_notification_id =
1190 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1192 reblog_notification_id =
1193 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1195 follow_notification_id =
1196 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1200 |> assign(:user, user)
1203 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1205 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1208 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1210 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1213 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1215 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1218 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1220 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1223 test "destroy multiple", %{conn: conn} do
1224 user = insert(:user)
1225 other_user = insert(:user)
1227 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1228 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1229 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1230 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1232 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1233 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1234 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1235 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1239 |> assign(:user, user)
1243 |> get("/api/v1/notifications")
1245 result = json_response(conn_res, 200)
1246 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1250 |> assign(:user, other_user)
1254 |> get("/api/v1/notifications")
1256 result = json_response(conn_res, 200)
1257 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1261 |> delete("/api/v1/notifications/destroy_multiple", %{
1262 "ids" => [notification1_id, notification2_id]
1265 assert json_response(conn_destroy, 200) == %{}
1269 |> get("/api/v1/notifications")
1271 result = json_response(conn_res, 200)
1272 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1275 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1276 user = insert(:user)
1277 user2 = insert(:user)
1279 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1280 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1282 conn = assign(conn, :user, user)
1284 conn = get(conn, "/api/v1/notifications")
1286 assert length(json_response(conn, 200)) == 1
1288 {:ok, user} = User.mute(user, user2)
1290 conn = assign(build_conn(), :user, user)
1291 conn = get(conn, "/api/v1/notifications")
1293 assert json_response(conn, 200) == []
1296 test "see notifications after muting user without notifications", %{conn: conn} do
1297 user = insert(:user)
1298 user2 = insert(:user)
1300 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1301 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1303 conn = assign(conn, :user, user)
1305 conn = get(conn, "/api/v1/notifications")
1307 assert length(json_response(conn, 200)) == 1
1309 {:ok, user} = User.mute(user, user2, false)
1311 conn = assign(build_conn(), :user, user)
1312 conn = get(conn, "/api/v1/notifications")
1314 assert length(json_response(conn, 200)) == 1
1317 test "see notifications after muting user with notifications and with_muted parameter", %{
1320 user = insert(:user)
1321 user2 = insert(:user)
1323 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1324 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1326 conn = assign(conn, :user, user)
1328 conn = get(conn, "/api/v1/notifications")
1330 assert length(json_response(conn, 200)) == 1
1332 {:ok, user} = User.mute(user, user2)
1334 conn = assign(build_conn(), :user, user)
1335 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1337 assert length(json_response(conn, 200)) == 1
1341 describe "reblogging" do
1342 test "reblogs and returns the reblogged status", %{conn: conn} do
1343 activity = insert(:note_activity)
1344 user = insert(:user)
1348 |> assign(:user, user)
1349 |> post("/api/v1/statuses/#{activity.id}/reblog")
1352 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1354 } = json_response(conn, 200)
1356 assert to_string(activity.id) == id
1359 test "reblogged status for another user", %{conn: conn} do
1360 activity = insert(:note_activity)
1361 user1 = insert(:user)
1362 user2 = insert(:user)
1363 user3 = insert(:user)
1364 CommonAPI.favorite(activity.id, user2)
1365 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1366 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1367 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1371 |> assign(:user, user3)
1372 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1375 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1376 "reblogged" => false,
1377 "favourited" => false,
1378 "bookmarked" => false
1379 } = json_response(conn_res, 200)
1383 |> assign(:user, user2)
1384 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1387 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1388 "reblogged" => true,
1389 "favourited" => true,
1390 "bookmarked" => true
1391 } = json_response(conn_res, 200)
1393 assert to_string(activity.id) == id
1396 test "returns 400 error when activity is not exist", %{conn: conn} do
1397 user = insert(:user)
1401 |> assign(:user, user)
1402 |> post("/api/v1/statuses/foo/reblog")
1404 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1408 describe "unreblogging" do
1409 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1410 activity = insert(:note_activity)
1411 user = insert(:user)
1413 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1417 |> assign(:user, user)
1418 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1420 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1422 assert to_string(activity.id) == id
1425 test "returns 400 error when activity is not exist", %{conn: conn} do
1426 user = insert(:user)
1430 |> assign(:user, user)
1431 |> post("/api/v1/statuses/foo/unreblog")
1433 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1437 describe "favoriting" do
1438 test "favs a status and returns it", %{conn: conn} do
1439 activity = insert(:note_activity)
1440 user = insert(:user)
1444 |> assign(:user, user)
1445 |> post("/api/v1/statuses/#{activity.id}/favourite")
1447 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1448 json_response(conn, 200)
1450 assert to_string(activity.id) == id
1453 test "returns 400 error for a wrong id", %{conn: conn} do
1454 user = insert(:user)
1458 |> assign(:user, user)
1459 |> post("/api/v1/statuses/1/favourite")
1461 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1465 describe "unfavoriting" do
1466 test "unfavorites a status and returns it", %{conn: conn} do
1467 activity = insert(:note_activity)
1468 user = insert(:user)
1470 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1474 |> assign(:user, user)
1475 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1477 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1478 json_response(conn, 200)
1480 assert to_string(activity.id) == id
1483 test "returns 400 error for a wrong id", %{conn: conn} do
1484 user = insert(:user)
1488 |> assign(:user, user)
1489 |> post("/api/v1/statuses/1/unfavourite")
1491 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1495 describe "user timelines" do
1496 test "gets a users statuses", %{conn: conn} do
1497 user_one = insert(:user)
1498 user_two = insert(:user)
1499 user_three = insert(:user)
1501 {:ok, user_three} = User.follow(user_three, user_one)
1503 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1505 {:ok, direct_activity} =
1506 CommonAPI.post(user_one, %{
1507 "status" => "Hi, @#{user_two.nickname}.",
1508 "visibility" => "direct"
1511 {:ok, private_activity} =
1512 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1516 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1518 assert [%{"id" => id}] = json_response(resp, 200)
1519 assert id == to_string(activity.id)
1523 |> assign(:user, user_two)
1524 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1526 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1527 assert id_one == to_string(direct_activity.id)
1528 assert id_two == to_string(activity.id)
1532 |> assign(:user, user_three)
1533 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1535 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1536 assert id_one == to_string(private_activity.id)
1537 assert id_two == to_string(activity.id)
1540 test "unimplemented pinned statuses feature", %{conn: conn} do
1541 note = insert(:note_activity)
1542 user = User.get_cached_by_ap_id(note.data["actor"])
1546 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1548 assert json_response(conn, 200) == []
1551 test "gets an users media", %{conn: conn} do
1552 note = insert(:note_activity)
1553 user = User.get_cached_by_ap_id(note.data["actor"])
1555 file = %Plug.Upload{
1556 content_type: "image/jpg",
1557 path: Path.absname("test/fixtures/image.jpg"),
1558 filename: "an_image.jpg"
1562 TwitterAPI.upload(file, user, "json")
1566 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1570 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1572 assert [%{"id" => id}] = json_response(conn, 200)
1573 assert id == to_string(image_post.id)
1577 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1579 assert [%{"id" => id}] = json_response(conn, 200)
1580 assert id == to_string(image_post.id)
1583 test "gets a user's statuses without reblogs", %{conn: conn} do
1584 user = insert(:user)
1585 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1586 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1590 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1592 assert [%{"id" => id}] = json_response(conn, 200)
1593 assert id == to_string(post.id)
1597 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1599 assert [%{"id" => id}] = json_response(conn, 200)
1600 assert id == to_string(post.id)
1603 test "filters user's statuses by a hashtag", %{conn: conn} do
1604 user = insert(:user)
1605 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1606 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1610 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1612 assert [%{"id" => id}] = json_response(conn, 200)
1613 assert id == to_string(post.id)
1617 describe "user relationships" do
1618 test "returns the relationships for the current user", %{conn: conn} do
1619 user = insert(:user)
1620 other_user = insert(:user)
1621 {:ok, user} = User.follow(user, other_user)
1625 |> assign(:user, user)
1626 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1628 assert [relationship] = json_response(conn, 200)
1630 assert to_string(other_user.id) == relationship["id"]
1634 describe "media upload" do
1636 upload_config = Pleroma.Config.get([Pleroma.Upload])
1637 proxy_config = Pleroma.Config.get([:media_proxy])
1640 Pleroma.Config.put([Pleroma.Upload], upload_config)
1641 Pleroma.Config.put([:media_proxy], proxy_config)
1644 user = insert(:user)
1648 |> assign(:user, user)
1650 image = %Plug.Upload{
1651 content_type: "image/jpg",
1652 path: Path.absname("test/fixtures/image.jpg"),
1653 filename: "an_image.jpg"
1656 [conn: conn, image: image]
1659 test "returns uploaded image", %{conn: conn, image: image} do
1660 desc = "Description of the image"
1664 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1665 |> json_response(:ok)
1667 assert media["type"] == "image"
1668 assert media["description"] == desc
1671 object = Repo.get(Object, media["id"])
1672 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1676 describe "locked accounts" do
1677 test "/api/v1/follow_requests works" do
1678 user = insert(:user, %{info: %User.Info{locked: true}})
1679 other_user = insert(:user)
1681 {:ok, _activity} = ActivityPub.follow(other_user, user)
1683 user = User.get_cached_by_id(user.id)
1684 other_user = User.get_cached_by_id(other_user.id)
1686 assert User.following?(other_user, user) == false
1690 |> assign(:user, user)
1691 |> get("/api/v1/follow_requests")
1693 assert [relationship] = json_response(conn, 200)
1694 assert to_string(other_user.id) == relationship["id"]
1697 test "/api/v1/follow_requests/:id/authorize works" do
1698 user = insert(:user, %{info: %User.Info{locked: true}})
1699 other_user = insert(:user)
1701 {:ok, _activity} = ActivityPub.follow(other_user, user)
1703 user = User.get_cached_by_id(user.id)
1704 other_user = User.get_cached_by_id(other_user.id)
1706 assert User.following?(other_user, user) == false
1710 |> assign(:user, user)
1711 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1713 assert relationship = json_response(conn, 200)
1714 assert to_string(other_user.id) == relationship["id"]
1716 user = User.get_cached_by_id(user.id)
1717 other_user = User.get_cached_by_id(other_user.id)
1719 assert User.following?(other_user, user) == true
1722 test "verify_credentials", %{conn: conn} do
1723 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1727 |> assign(:user, user)
1728 |> get("/api/v1/accounts/verify_credentials")
1730 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1731 assert id == to_string(user.id)
1734 test "/api/v1/follow_requests/:id/reject works" do
1735 user = insert(:user, %{info: %User.Info{locked: true}})
1736 other_user = insert(:user)
1738 {:ok, _activity} = ActivityPub.follow(other_user, user)
1740 user = User.get_cached_by_id(user.id)
1744 |> assign(:user, user)
1745 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1747 assert relationship = json_response(conn, 200)
1748 assert to_string(other_user.id) == relationship["id"]
1750 user = User.get_cached_by_id(user.id)
1751 other_user = User.get_cached_by_id(other_user.id)
1753 assert User.following?(other_user, user) == false
1757 test "account fetching", %{conn: conn} do
1758 user = insert(:user)
1762 |> get("/api/v1/accounts/#{user.id}")
1764 assert %{"id" => id} = json_response(conn, 200)
1765 assert id == to_string(user.id)
1769 |> get("/api/v1/accounts/-1")
1771 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1774 test "account fetching also works nickname", %{conn: conn} do
1775 user = insert(:user)
1779 |> get("/api/v1/accounts/#{user.nickname}")
1781 assert %{"id" => id} = json_response(conn, 200)
1782 assert id == user.id
1785 test "mascot upload", %{conn: conn} do
1786 user = insert(:user)
1788 non_image_file = %Plug.Upload{
1789 content_type: "audio/mpeg",
1790 path: Path.absname("test/fixtures/sound.mp3"),
1791 filename: "sound.mp3"
1796 |> assign(:user, user)
1797 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1799 assert json_response(conn, 415)
1801 file = %Plug.Upload{
1802 content_type: "image/jpg",
1803 path: Path.absname("test/fixtures/image.jpg"),
1804 filename: "an_image.jpg"
1809 |> assign(:user, user)
1810 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1812 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1815 test "mascot retrieving", %{conn: conn} do
1816 user = insert(:user)
1817 # When user hasn't set a mascot, we should just get pleroma tan back
1820 |> assign(:user, user)
1821 |> get("/api/v1/pleroma/mascot")
1823 assert %{"url" => url} = json_response(conn, 200)
1824 assert url =~ "pleroma-fox-tan-smol"
1826 # When a user sets their mascot, we should get that back
1827 file = %Plug.Upload{
1828 content_type: "image/jpg",
1829 path: Path.absname("test/fixtures/image.jpg"),
1830 filename: "an_image.jpg"
1835 |> assign(:user, user)
1836 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1838 assert json_response(conn, 200)
1840 user = User.get_cached_by_id(user.id)
1844 |> assign(:user, user)
1845 |> get("/api/v1/pleroma/mascot")
1847 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1848 assert url =~ "an_image"
1851 test "hashtag timeline", %{conn: conn} do
1852 following = insert(:user)
1855 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1857 {:ok, [_activity]} =
1858 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1862 |> get("/api/v1/timelines/tag/2hu")
1864 assert [%{"id" => id}] = json_response(nconn, 200)
1866 assert id == to_string(activity.id)
1868 # works for different capitalization too
1871 |> get("/api/v1/timelines/tag/2HU")
1873 assert [%{"id" => id}] = json_response(nconn, 200)
1875 assert id == to_string(activity.id)
1879 test "multi-hashtag timeline", %{conn: conn} do
1880 user = insert(:user)
1882 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1883 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1884 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1888 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1890 [status_none, status_test1, status_test] = json_response(any_test, 200)
1892 assert to_string(activity_test.id) == status_test["id"]
1893 assert to_string(activity_test1.id) == status_test1["id"]
1894 assert to_string(activity_none.id) == status_none["id"]
1898 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1900 assert [status_test1] == json_response(restricted_test, 200)
1902 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1904 assert [status_none] == json_response(all_test, 200)
1907 test "getting followers", %{conn: conn} do
1908 user = insert(:user)
1909 other_user = insert(:user)
1910 {:ok, user} = User.follow(user, other_user)
1914 |> get("/api/v1/accounts/#{other_user.id}/followers")
1916 assert [%{"id" => id}] = json_response(conn, 200)
1917 assert id == to_string(user.id)
1920 test "getting followers, hide_followers", %{conn: conn} do
1921 user = insert(:user)
1922 other_user = insert(:user, %{info: %{hide_followers: true}})
1923 {:ok, _user} = User.follow(user, other_user)
1927 |> get("/api/v1/accounts/#{other_user.id}/followers")
1929 assert [] == json_response(conn, 200)
1932 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1933 user = insert(:user)
1934 other_user = insert(:user, %{info: %{hide_followers: true}})
1935 {:ok, _user} = User.follow(user, other_user)
1939 |> assign(:user, other_user)
1940 |> get("/api/v1/accounts/#{other_user.id}/followers")
1942 refute [] == json_response(conn, 200)
1945 test "getting followers, pagination", %{conn: conn} do
1946 user = insert(:user)
1947 follower1 = insert(:user)
1948 follower2 = insert(:user)
1949 follower3 = insert(:user)
1950 {:ok, _} = User.follow(follower1, user)
1951 {:ok, _} = User.follow(follower2, user)
1952 {:ok, _} = User.follow(follower3, user)
1956 |> assign(:user, user)
1960 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1962 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1963 assert id3 == follower3.id
1964 assert id2 == follower2.id
1968 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1970 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1971 assert id2 == follower2.id
1972 assert id1 == follower1.id
1976 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1978 assert [%{"id" => id2}] = json_response(res_conn, 200)
1979 assert id2 == follower2.id
1981 assert [link_header] = get_resp_header(res_conn, "link")
1982 assert link_header =~ ~r/min_id=#{follower2.id}/
1983 assert link_header =~ ~r/max_id=#{follower2.id}/
1986 test "getting following", %{conn: conn} do
1987 user = insert(:user)
1988 other_user = insert(:user)
1989 {:ok, user} = User.follow(user, other_user)
1993 |> get("/api/v1/accounts/#{user.id}/following")
1995 assert [%{"id" => id}] = json_response(conn, 200)
1996 assert id == to_string(other_user.id)
1999 test "getting following, hide_follows", %{conn: conn} do
2000 user = insert(:user, %{info: %{hide_follows: true}})
2001 other_user = insert(:user)
2002 {:ok, user} = User.follow(user, other_user)
2006 |> get("/api/v1/accounts/#{user.id}/following")
2008 assert [] == json_response(conn, 200)
2011 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2012 user = insert(:user, %{info: %{hide_follows: true}})
2013 other_user = insert(:user)
2014 {:ok, user} = User.follow(user, other_user)
2018 |> assign(:user, user)
2019 |> get("/api/v1/accounts/#{user.id}/following")
2021 refute [] == json_response(conn, 200)
2024 test "getting following, pagination", %{conn: conn} do
2025 user = insert(:user)
2026 following1 = insert(:user)
2027 following2 = insert(:user)
2028 following3 = insert(:user)
2029 {:ok, _} = User.follow(user, following1)
2030 {:ok, _} = User.follow(user, following2)
2031 {:ok, _} = User.follow(user, following3)
2035 |> assign(:user, user)
2039 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2041 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2042 assert id3 == following3.id
2043 assert id2 == following2.id
2047 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2049 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2050 assert id2 == following2.id
2051 assert id1 == following1.id
2055 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2057 assert [%{"id" => id2}] = json_response(res_conn, 200)
2058 assert id2 == following2.id
2060 assert [link_header] = get_resp_header(res_conn, "link")
2061 assert link_header =~ ~r/min_id=#{following2.id}/
2062 assert link_header =~ ~r/max_id=#{following2.id}/
2065 test "following / unfollowing a user", %{conn: conn} do
2066 user = insert(:user)
2067 other_user = insert(:user)
2071 |> assign(:user, user)
2072 |> post("/api/v1/accounts/#{other_user.id}/follow")
2074 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2076 user = User.get_cached_by_id(user.id)
2080 |> assign(:user, user)
2081 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2083 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2085 user = User.get_cached_by_id(user.id)
2089 |> assign(:user, user)
2090 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2092 assert %{"id" => id} = json_response(conn, 200)
2093 assert id == to_string(other_user.id)
2096 test "following without reblogs" do
2097 follower = insert(:user)
2098 followed = insert(:user)
2099 other_user = insert(:user)
2103 |> assign(:user, follower)
2104 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2106 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2108 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2109 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2113 |> assign(:user, User.get_cached_by_id(follower.id))
2114 |> get("/api/v1/timelines/home")
2116 assert [] == json_response(conn, 200)
2120 |> assign(:user, follower)
2121 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2123 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2127 |> assign(:user, User.get_cached_by_id(follower.id))
2128 |> get("/api/v1/timelines/home")
2130 expected_activity_id = reblog.id
2131 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2134 test "following / unfollowing errors" do
2135 user = insert(:user)
2139 |> assign(:user, user)
2142 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2143 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2146 user = User.get_cached_by_id(user.id)
2147 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2148 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2150 # self follow via uri
2151 user = User.get_cached_by_id(user.id)
2152 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2153 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2155 # follow non existing user
2156 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2157 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2159 # follow non existing user via uri
2160 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2161 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2163 # unfollow non existing user
2164 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2165 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2168 describe "mute/unmute" do
2169 test "with notifications", %{conn: conn} do
2170 user = insert(:user)
2171 other_user = insert(:user)
2175 |> assign(:user, user)
2176 |> post("/api/v1/accounts/#{other_user.id}/mute")
2178 response = json_response(conn, 200)
2180 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2181 user = User.get_cached_by_id(user.id)
2185 |> assign(:user, user)
2186 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2188 response = json_response(conn, 200)
2189 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2192 test "without notifications", %{conn: conn} do
2193 user = insert(:user)
2194 other_user = insert(:user)
2198 |> assign(:user, user)
2199 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2201 response = json_response(conn, 200)
2203 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2204 user = User.get_cached_by_id(user.id)
2208 |> assign(:user, user)
2209 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2211 response = json_response(conn, 200)
2212 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2216 test "subscribing / unsubscribing to a user", %{conn: conn} do
2217 user = insert(:user)
2218 subscription_target = insert(:user)
2222 |> assign(:user, user)
2223 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2225 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2229 |> assign(:user, user)
2230 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2232 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2235 test "getting a list of mutes", %{conn: conn} do
2236 user = insert(:user)
2237 other_user = insert(:user)
2239 {:ok, user} = User.mute(user, other_user)
2243 |> assign(:user, user)
2244 |> get("/api/v1/mutes")
2246 other_user_id = to_string(other_user.id)
2247 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2250 test "blocking / unblocking a user", %{conn: conn} do
2251 user = insert(:user)
2252 other_user = insert(:user)
2256 |> assign(:user, user)
2257 |> post("/api/v1/accounts/#{other_user.id}/block")
2259 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2261 user = User.get_cached_by_id(user.id)
2265 |> assign(:user, user)
2266 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2268 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2271 test "getting a list of blocks", %{conn: conn} do
2272 user = insert(:user)
2273 other_user = insert(:user)
2275 {:ok, user} = User.block(user, other_user)
2279 |> assign(:user, user)
2280 |> get("/api/v1/blocks")
2282 other_user_id = to_string(other_user.id)
2283 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2286 test "blocking / unblocking a domain", %{conn: conn} do
2287 user = insert(:user)
2288 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2292 |> assign(:user, user)
2293 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2295 assert %{} = json_response(conn, 200)
2296 user = User.get_cached_by_ap_id(user.ap_id)
2297 assert User.blocks?(user, other_user)
2301 |> assign(:user, user)
2302 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2304 assert %{} = json_response(conn, 200)
2305 user = User.get_cached_by_ap_id(user.ap_id)
2306 refute User.blocks?(user, other_user)
2309 test "getting a list of domain blocks", %{conn: conn} do
2310 user = insert(:user)
2312 {:ok, user} = User.block_domain(user, "bad.site")
2313 {:ok, user} = User.block_domain(user, "even.worse.site")
2317 |> assign(:user, user)
2318 |> get("/api/v1/domain_blocks")
2320 domain_blocks = json_response(conn, 200)
2322 assert "bad.site" in domain_blocks
2323 assert "even.worse.site" in domain_blocks
2326 test "unimplemented follow_requests, blocks, domain blocks" do
2327 user = insert(:user)
2329 ["blocks", "domain_blocks", "follow_requests"]
2330 |> Enum.each(fn endpoint ->
2333 |> assign(:user, user)
2334 |> get("/api/v1/#{endpoint}")
2336 assert [] = json_response(conn, 200)
2340 test "returns the favorites of a user", %{conn: conn} do
2341 user = insert(:user)
2342 other_user = insert(:user)
2344 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2345 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2347 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2351 |> assign(:user, user)
2352 |> get("/api/v1/favourites")
2354 assert [status] = json_response(first_conn, 200)
2355 assert status["id"] == to_string(activity.id)
2357 assert [{"link", _link_header}] =
2358 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2360 # Honours query params
2361 {:ok, second_activity} =
2362 CommonAPI.post(other_user, %{
2364 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2367 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2369 last_like = status["id"]
2373 |> assign(:user, user)
2374 |> get("/api/v1/favourites?since_id=#{last_like}")
2376 assert [second_status] = json_response(second_conn, 200)
2377 assert second_status["id"] == to_string(second_activity.id)
2381 |> assign(:user, user)
2382 |> get("/api/v1/favourites?limit=0")
2384 assert [] = json_response(third_conn, 200)
2387 describe "getting favorites timeline of specified user" do
2389 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2390 [current_user: current_user, user: user]
2393 test "returns list of statuses favorited by specified user", %{
2395 current_user: current_user,
2398 [activity | _] = insert_pair(:note_activity)
2399 CommonAPI.favorite(activity.id, user)
2403 |> assign(:user, current_user)
2404 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2405 |> json_response(:ok)
2409 assert length(response) == 1
2410 assert like["id"] == activity.id
2413 test "returns favorites for specified user_id when user is not logged in", %{
2417 activity = insert(:note_activity)
2418 CommonAPI.favorite(activity.id, user)
2422 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2423 |> json_response(:ok)
2425 assert length(response) == 1
2428 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2430 current_user: current_user,
2434 CommonAPI.post(current_user, %{
2435 "status" => "Hi @#{user.nickname}!",
2436 "visibility" => "direct"
2439 CommonAPI.favorite(direct.id, user)
2443 |> assign(:user, current_user)
2444 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2445 |> json_response(:ok)
2447 assert length(response) == 1
2449 anonymous_response =
2451 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2452 |> json_response(:ok)
2454 assert Enum.empty?(anonymous_response)
2457 test "does not return others' favorited DM when user is not one of recipients", %{
2459 current_user: current_user,
2462 user_two = insert(:user)
2465 CommonAPI.post(user_two, %{
2466 "status" => "Hi @#{user.nickname}!",
2467 "visibility" => "direct"
2470 CommonAPI.favorite(direct.id, user)
2474 |> assign(:user, current_user)
2475 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2476 |> json_response(:ok)
2478 assert Enum.empty?(response)
2481 test "paginates favorites using since_id and max_id", %{
2483 current_user: current_user,
2486 activities = insert_list(10, :note_activity)
2488 Enum.each(activities, fn activity ->
2489 CommonAPI.favorite(activity.id, user)
2492 third_activity = Enum.at(activities, 2)
2493 seventh_activity = Enum.at(activities, 6)
2497 |> assign(:user, current_user)
2498 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2499 since_id: third_activity.id,
2500 max_id: seventh_activity.id
2502 |> json_response(:ok)
2504 assert length(response) == 3
2505 refute third_activity in response
2506 refute seventh_activity in response
2509 test "limits favorites using limit parameter", %{
2511 current_user: current_user,
2515 |> insert_list(:note_activity)
2516 |> Enum.each(fn activity ->
2517 CommonAPI.favorite(activity.id, user)
2522 |> assign(:user, current_user)
2523 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2524 |> json_response(:ok)
2526 assert length(response) == 3
2529 test "returns empty response when user does not have any favorited statuses", %{
2531 current_user: current_user,
2536 |> assign(:user, current_user)
2537 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2538 |> json_response(:ok)
2540 assert Enum.empty?(response)
2543 test "returns 404 error when specified user is not exist", %{conn: conn} do
2544 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2546 assert json_response(conn, 404) == %{"error" => "Record not found"}
2549 test "returns 403 error when user has hidden own favorites", %{
2551 current_user: current_user
2553 user = insert(:user, %{info: %{hide_favorites: true}})
2554 activity = insert(:note_activity)
2555 CommonAPI.favorite(activity.id, user)
2559 |> assign(:user, current_user)
2560 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2562 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2565 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2566 user = insert(:user)
2567 activity = insert(:note_activity)
2568 CommonAPI.favorite(activity.id, user)
2572 |> assign(:user, current_user)
2573 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2575 assert user.info.hide_favorites
2576 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2580 test "get instance information", %{conn: conn} do
2581 conn = get(conn, "/api/v1/instance")
2582 assert result = json_response(conn, 200)
2584 email = Pleroma.Config.get([:instance, :email])
2585 # Note: not checking for "max_toot_chars" since it's optional
2591 "email" => from_config_email,
2593 "streaming_api" => _
2598 "registrations" => _,
2602 assert email == from_config_email
2605 test "get instance stats", %{conn: conn} do
2606 user = insert(:user, %{local: true})
2608 user2 = insert(:user, %{local: true})
2609 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2611 insert(:user, %{local: false, nickname: "u@peer1.com"})
2612 insert(:user, %{local: false, nickname: "u@peer2.com"})
2614 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2616 # Stats should count users with missing or nil `info.deactivated` value
2617 user = User.get_cached_by_id(user.id)
2618 info_change = Changeset.change(user.info, %{deactivated: nil})
2622 |> Changeset.change()
2623 |> Changeset.put_embed(:info, info_change)
2624 |> User.update_and_set_cache()
2626 Pleroma.Stats.update_stats()
2628 conn = get(conn, "/api/v1/instance")
2630 assert result = json_response(conn, 200)
2632 stats = result["stats"]
2635 assert stats["user_count"] == 1
2636 assert stats["status_count"] == 1
2637 assert stats["domain_count"] == 2
2640 test "get peers", %{conn: conn} do
2641 insert(:user, %{local: false, nickname: "u@peer1.com"})
2642 insert(:user, %{local: false, nickname: "u@peer2.com"})
2644 Pleroma.Stats.update_stats()
2646 conn = get(conn, "/api/v1/instance/peers")
2648 assert result = json_response(conn, 200)
2650 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2653 test "put settings", %{conn: conn} do
2654 user = insert(:user)
2658 |> assign(:user, user)
2659 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2661 assert _result = json_response(conn, 200)
2663 user = User.get_cached_by_ap_id(user.ap_id)
2664 assert user.info.settings == %{"programming" => "socks"}
2667 describe "pinned statuses" do
2669 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2671 user = insert(:user)
2672 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2674 [user: user, activity: activity]
2677 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2678 {:ok, _} = CommonAPI.pin(activity.id, user)
2682 |> assign(:user, user)
2683 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2684 |> json_response(200)
2686 id_str = to_string(activity.id)
2688 assert [%{"id" => ^id_str, "pinned" => true}] = result
2691 test "pin status", %{conn: conn, user: user, activity: activity} do
2692 id_str = to_string(activity.id)
2694 assert %{"id" => ^id_str, "pinned" => true} =
2696 |> assign(:user, user)
2697 |> post("/api/v1/statuses/#{activity.id}/pin")
2698 |> json_response(200)
2700 assert [%{"id" => ^id_str, "pinned" => true}] =
2702 |> assign(:user, user)
2703 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2704 |> json_response(200)
2707 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2708 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2712 |> assign(:user, user)
2713 |> post("/api/v1/statuses/#{dm.id}/pin")
2715 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2718 test "unpin status", %{conn: conn, user: user, activity: activity} do
2719 {:ok, _} = CommonAPI.pin(activity.id, user)
2721 id_str = to_string(activity.id)
2722 user = refresh_record(user)
2724 assert %{"id" => ^id_str, "pinned" => false} =
2726 |> assign(:user, user)
2727 |> post("/api/v1/statuses/#{activity.id}/unpin")
2728 |> json_response(200)
2732 |> assign(:user, user)
2733 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2734 |> json_response(200)
2737 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2740 |> assign(:user, user)
2741 |> post("/api/v1/statuses/1/unpin")
2743 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2746 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2747 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2749 id_str_one = to_string(activity_one.id)
2751 assert %{"id" => ^id_str_one, "pinned" => true} =
2753 |> assign(:user, user)
2754 |> post("/api/v1/statuses/#{id_str_one}/pin")
2755 |> json_response(200)
2757 user = refresh_record(user)
2759 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2761 |> assign(:user, user)
2762 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2763 |> json_response(400)
2769 Pleroma.Config.put([:rich_media, :enabled], true)
2772 Pleroma.Config.put([:rich_media, :enabled], false)
2775 user = insert(:user)
2779 test "returns rich-media card", %{conn: conn, user: user} do
2780 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2783 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2784 "provider_name" => "example.com",
2785 "provider_url" => "https://example.com",
2786 "title" => "The Rock",
2788 "url" => "https://example.com/ogp",
2790 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2793 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2794 "title" => "The Rock",
2795 "type" => "video.movie",
2796 "url" => "https://example.com/ogp",
2798 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2805 |> get("/api/v1/statuses/#{activity.id}/card")
2806 |> json_response(200)
2808 assert response == card_data
2810 # works with private posts
2812 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2816 |> assign(:user, user)
2817 |> get("/api/v1/statuses/#{activity.id}/card")
2818 |> json_response(200)
2820 assert response_two == card_data
2823 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2825 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2829 |> get("/api/v1/statuses/#{activity.id}/card")
2830 |> json_response(:ok)
2832 assert response == %{
2834 "title" => "Pleroma",
2835 "description" => "",
2837 "provider_name" => "example.com",
2838 "provider_url" => "https://example.com",
2839 "url" => "https://example.com/ogp-missing-data",
2842 "title" => "Pleroma",
2843 "type" => "website",
2844 "url" => "https://example.com/ogp-missing-data"
2852 user = insert(:user)
2853 for_user = insert(:user)
2856 CommonAPI.post(user, %{
2857 "status" => "heweoo?"
2861 CommonAPI.post(user, %{
2862 "status" => "heweoo!"
2867 |> assign(:user, for_user)
2868 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2870 assert json_response(response1, 200)["bookmarked"] == true
2874 |> assign(:user, for_user)
2875 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2877 assert json_response(response2, 200)["bookmarked"] == true
2881 |> assign(:user, for_user)
2882 |> get("/api/v1/bookmarks")
2884 assert [json_response(response2, 200), json_response(response1, 200)] ==
2885 json_response(bookmarks, 200)
2889 |> assign(:user, for_user)
2890 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2892 assert json_response(response1, 200)["bookmarked"] == false
2896 |> assign(:user, for_user)
2897 |> get("/api/v1/bookmarks")
2899 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2902 describe "conversation muting" do
2904 post_user = insert(:user)
2905 user = insert(:user)
2907 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2909 [user: user, activity: activity]
2912 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2913 id_str = to_string(activity.id)
2915 assert %{"id" => ^id_str, "muted" => true} =
2917 |> assign(:user, user)
2918 |> post("/api/v1/statuses/#{activity.id}/mute")
2919 |> json_response(200)
2922 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2923 {:ok, _} = CommonAPI.add_mute(user, activity)
2927 |> assign(:user, user)
2928 |> post("/api/v1/statuses/#{activity.id}/mute")
2930 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2933 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2934 {:ok, _} = CommonAPI.add_mute(user, activity)
2936 id_str = to_string(activity.id)
2937 user = refresh_record(user)
2939 assert %{"id" => ^id_str, "muted" => false} =
2941 |> assign(:user, user)
2942 |> post("/api/v1/statuses/#{activity.id}/unmute")
2943 |> json_response(200)
2947 describe "reports" do
2949 reporter = insert(:user)
2950 target_user = insert(:user)
2952 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2954 [reporter: reporter, target_user: target_user, activity: activity]
2957 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2958 assert %{"action_taken" => false, "id" => _} =
2960 |> assign(:user, reporter)
2961 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2962 |> json_response(200)
2965 test "submit a report with statuses and comment", %{
2968 target_user: target_user,
2971 assert %{"action_taken" => false, "id" => _} =
2973 |> assign(:user, reporter)
2974 |> post("/api/v1/reports", %{
2975 "account_id" => target_user.id,
2976 "status_ids" => [activity.id],
2977 "comment" => "bad status!",
2978 "forward" => "false"
2980 |> json_response(200)
2983 test "account_id is required", %{
2988 assert %{"error" => "Valid `account_id` required"} =
2990 |> assign(:user, reporter)
2991 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2992 |> json_response(400)
2995 test "comment must be up to the size specified in the config", %{
2998 target_user: target_user
3000 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
3001 comment = String.pad_trailing("a", max_size + 1, "a")
3003 error = %{"error" => "Comment must be up to #{max_size} characters"}
3007 |> assign(:user, reporter)
3008 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3009 |> json_response(400)
3012 test "returns error when account is not exist", %{
3019 |> assign(:user, reporter)
3020 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3022 assert json_response(conn, 400) == %{"error" => "Account not found"}
3026 describe "link headers" do
3027 test "preserves parameters in link headers", %{conn: conn} do
3028 user = insert(:user)
3029 other_user = insert(:user)
3032 CommonAPI.post(other_user, %{
3033 "status" => "hi @#{user.nickname}",
3034 "visibility" => "public"
3038 CommonAPI.post(other_user, %{
3039 "status" => "hi @#{user.nickname}",
3040 "visibility" => "public"
3043 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3044 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3048 |> assign(:user, user)
3049 |> get("/api/v1/notifications", %{media_only: true})
3051 assert [link_header] = get_resp_header(conn, "link")
3052 assert link_header =~ ~r/media_only=true/
3053 assert link_header =~ ~r/min_id=#{notification2.id}/
3054 assert link_header =~ ~r/max_id=#{notification1.id}/
3058 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3059 # Need to set an old-style integer ID to reproduce the problem
3060 # (these are no longer assigned to new accounts but were preserved
3061 # for existing accounts during the migration to flakeIDs)
3062 user_one = insert(:user, %{id: 1212})
3063 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3067 |> get("/api/v1/accounts/#{user_one.id}")
3071 |> get("/api/v1/accounts/#{user_two.nickname}")
3075 |> get("/api/v1/accounts/#{user_two.id}")
3077 acc_one = json_response(resp_one, 200)
3078 acc_two = json_response(resp_two, 200)
3079 acc_three = json_response(resp_three, 200)
3080 refute acc_one == acc_two
3081 assert acc_two == acc_three
3084 describe "custom emoji" do
3085 test "with tags", %{conn: conn} do
3088 |> get("/api/v1/custom_emojis")
3089 |> json_response(200)
3091 assert Map.has_key?(emoji, "shortcode")
3092 assert Map.has_key?(emoji, "static_url")
3093 assert Map.has_key?(emoji, "tags")
3094 assert is_list(emoji["tags"])
3095 assert Map.has_key?(emoji, "category")
3096 assert Map.has_key?(emoji, "url")
3097 assert Map.has_key?(emoji, "visible_in_picker")
3101 describe "index/2 redirections" do
3102 setup %{conn: conn} do
3106 signing_salt: "cooldude"
3111 |> Plug.Session.call(Plug.Session.init(session_opts))
3114 test_path = "/web/statuses/test"
3115 %{conn: conn, path: test_path}
3118 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3119 conn = get(conn, path)
3121 assert conn.status == 302
3122 assert redirected_to(conn) == "/web/login"
3125 test "redirects not logged-in users to the login page on private instances", %{
3129 is_public = Pleroma.Config.get([:instance, :public])
3130 Pleroma.Config.put([:instance, :public], false)
3132 conn = get(conn, path)
3134 assert conn.status == 302
3135 assert redirected_to(conn) == "/web/login"
3137 Pleroma.Config.put([:instance, :public], is_public)
3140 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3141 token = insert(:oauth_token)
3145 |> assign(:user, token.user)
3146 |> put_session(:oauth_token, token.token)
3149 assert conn.status == 200
3152 test "saves referer path to session", %{conn: conn, path: path} do
3153 conn = get(conn, path)
3154 return_to = Plug.Conn.get_session(conn, :return_to)
3156 assert return_to == path
3159 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3160 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3161 auth = insert(:oauth_authorization, app: app)
3165 |> put_session(:return_to, path)
3166 |> get("/web/login", %{code: auth.token})
3168 assert conn.status == 302
3169 assert redirected_to(conn) == path
3172 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3173 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3174 auth = insert(:oauth_authorization, app: app)
3176 conn = get(conn, "/web/login", %{code: auth.token})
3178 assert conn.status == 302
3179 assert redirected_to(conn) == "/web/getting-started"
3183 describe "scheduled activities" do
3184 test "creates a scheduled activity", %{conn: conn} do
3185 user = insert(:user)
3186 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3190 |> assign(:user, user)
3191 |> post("/api/v1/statuses", %{
3192 "status" => "scheduled",
3193 "scheduled_at" => scheduled_at
3196 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3197 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3198 assert [] == Repo.all(Activity)
3201 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3202 user = insert(:user)
3203 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3205 file = %Plug.Upload{
3206 content_type: "image/jpg",
3207 path: Path.absname("test/fixtures/image.jpg"),
3208 filename: "an_image.jpg"
3211 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3215 |> assign(:user, user)
3216 |> post("/api/v1/statuses", %{
3217 "media_ids" => [to_string(upload.id)],
3218 "status" => "scheduled",
3219 "scheduled_at" => scheduled_at
3222 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3223 assert %{"type" => "image"} = media_attachment
3226 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3228 user = insert(:user)
3231 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3235 |> assign(:user, user)
3236 |> post("/api/v1/statuses", %{
3237 "status" => "not scheduled",
3238 "scheduled_at" => scheduled_at
3241 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3242 assert [] == Repo.all(ScheduledActivity)
3245 test "returns error when daily user limit is exceeded", %{conn: conn} do
3246 user = insert(:user)
3249 NaiveDateTime.utc_now()
3250 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3251 |> NaiveDateTime.to_iso8601()
3253 attrs = %{params: %{}, scheduled_at: today}
3254 {:ok, _} = ScheduledActivity.create(user, attrs)
3255 {:ok, _} = ScheduledActivity.create(user, attrs)
3259 |> assign(:user, user)
3260 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3262 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3265 test "returns error when total user limit is exceeded", %{conn: conn} do
3266 user = insert(:user)
3269 NaiveDateTime.utc_now()
3270 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3271 |> NaiveDateTime.to_iso8601()
3274 NaiveDateTime.utc_now()
3275 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3276 |> NaiveDateTime.to_iso8601()
3278 attrs = %{params: %{}, scheduled_at: today}
3279 {:ok, _} = ScheduledActivity.create(user, attrs)
3280 {:ok, _} = ScheduledActivity.create(user, attrs)
3281 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3285 |> assign(:user, user)
3286 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3288 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3291 test "shows scheduled activities", %{conn: conn} do
3292 user = insert(:user)
3293 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3294 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3295 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3296 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3300 |> assign(:user, user)
3305 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3307 result = json_response(conn_res, 200)
3308 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3313 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3315 result = json_response(conn_res, 200)
3316 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3321 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3323 result = json_response(conn_res, 200)
3324 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3327 test "shows a scheduled activity", %{conn: conn} do
3328 user = insert(:user)
3329 scheduled_activity = insert(:scheduled_activity, user: user)
3333 |> assign(:user, user)
3334 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3336 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3337 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3341 |> assign(:user, user)
3342 |> get("/api/v1/scheduled_statuses/404")
3344 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3347 test "updates a scheduled activity", %{conn: conn} do
3348 user = insert(:user)
3349 scheduled_activity = insert(:scheduled_activity, user: user)
3352 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3356 |> assign(:user, user)
3357 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3358 scheduled_at: new_scheduled_at
3361 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3362 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3366 |> assign(:user, user)
3367 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3369 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3372 test "deletes a scheduled activity", %{conn: conn} do
3373 user = insert(:user)
3374 scheduled_activity = insert(:scheduled_activity, user: user)
3378 |> assign(:user, user)
3379 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3381 assert %{} = json_response(res_conn, 200)
3382 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3386 |> assign(:user, user)
3387 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3389 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3393 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3394 user1 = insert(:user)
3395 user2 = insert(:user)
3396 user3 = insert(:user)
3398 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3400 # Reply to status from another user
3403 |> assign(:user, user2)
3404 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3406 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3408 activity = Activity.get_by_id_with_object(id)
3410 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3411 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3413 # Reblog from the third user
3416 |> assign(:user, user3)
3417 |> post("/api/v1/statuses/#{activity.id}/reblog")
3419 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3420 json_response(conn2, 200)
3422 assert to_string(activity.id) == id
3424 # Getting third user status
3427 |> assign(:user, user3)
3428 |> get("api/v1/timelines/home")
3430 [reblogged_activity] = json_response(conn3, 200)
3432 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3434 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3435 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3438 describe "create account by app" do
3439 test "Account registration via Application", %{conn: conn} do
3442 |> post("/api/v1/apps", %{
3443 client_name: "client_name",
3444 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3445 scopes: "read, write, follow"
3449 "client_id" => client_id,
3450 "client_secret" => client_secret,
3452 "name" => "client_name",
3453 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3456 } = json_response(conn, 200)
3460 |> post("/oauth/token", %{
3461 grant_type: "client_credentials",
3462 client_id: client_id,
3463 client_secret: client_secret
3466 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3467 json_response(conn, 200)
3470 token_from_db = Repo.get_by(Token, token: token)
3471 assert token_from_db
3473 assert scope == "read write follow"
3477 |> put_req_header("authorization", "Bearer " <> token)
3478 |> post("/api/v1/accounts", %{
3480 email: "lain@example.org",
3481 password: "PlzDontHackLain",
3486 "access_token" => token,
3487 "created_at" => _created_at,
3489 "token_type" => "Bearer"
3490 } = json_response(conn, 200)
3492 token_from_db = Repo.get_by(Token, token: token)
3493 assert token_from_db
3494 token_from_db = Repo.preload(token_from_db, :user)
3495 assert token_from_db.user
3497 assert token_from_db.user.info.confirmation_pending
3500 test "rate limit", %{conn: conn} do
3501 app_token = insert(:oauth_token, user: nil)
3504 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3505 |> Map.put(:remote_ip, {15, 15, 15, 15})
3510 |> post("/api/v1/accounts", %{
3511 username: "#{i}lain",
3512 email: "#{i}lain@example.org",
3513 password: "PlzDontHackLain",
3518 "access_token" => token,
3519 "created_at" => _created_at,
3521 "token_type" => "Bearer"
3522 } = json_response(conn, 200)
3524 token_from_db = Repo.get_by(Token, token: token)
3525 assert token_from_db
3526 token_from_db = Repo.preload(token_from_db, :user)
3527 assert token_from_db.user
3529 assert token_from_db.user.info.confirmation_pending
3534 |> post("/api/v1/accounts", %{
3536 email: "6lain@example.org",
3537 password: "PlzDontHackLain",
3541 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3545 describe "GET /api/v1/polls/:id" do
3546 test "returns poll entity for object id", %{conn: conn} do
3547 user = insert(:user)
3550 CommonAPI.post(user, %{
3551 "status" => "Pleroma does",
3552 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3555 object = Object.normalize(activity)
3559 |> assign(:user, user)
3560 |> get("/api/v1/polls/#{object.id}")
3562 response = json_response(conn, 200)
3563 id = to_string(object.id)
3564 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3567 test "does not expose polls for private statuses", %{conn: conn} do
3568 user = insert(:user)
3569 other_user = insert(:user)
3572 CommonAPI.post(user, %{
3573 "status" => "Pleroma does",
3574 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3575 "visibility" => "private"
3578 object = Object.normalize(activity)
3582 |> assign(:user, other_user)
3583 |> get("/api/v1/polls/#{object.id}")
3585 assert json_response(conn, 404)
3589 describe "POST /api/v1/polls/:id/votes" do
3590 test "votes are added to the poll", %{conn: conn} do
3591 user = insert(:user)
3592 other_user = insert(:user)
3595 CommonAPI.post(user, %{
3596 "status" => "A very delicious sandwich",
3598 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3604 object = Object.normalize(activity)
3608 |> assign(:user, other_user)
3609 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3611 assert json_response(conn, 200)
3612 object = Object.get_by_id(object.id)
3614 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3619 test "author can't vote", %{conn: conn} do
3620 user = insert(:user)
3623 CommonAPI.post(user, %{
3624 "status" => "Am I cute?",
3625 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3628 object = Object.normalize(activity)
3631 |> assign(:user, user)
3632 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3633 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3635 object = Object.get_by_id(object.id)
3637 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3640 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3641 user = insert(:user)
3642 other_user = insert(:user)
3645 CommonAPI.post(user, %{
3646 "status" => "The glass is",
3647 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3650 object = Object.normalize(activity)
3653 |> assign(:user, other_user)
3654 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3655 |> json_response(422) == %{"error" => "Too many choices"}
3657 object = Object.get_by_id(object.id)
3659 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3664 test "does not allow choice index to be greater than options count", %{conn: conn} do
3665 user = insert(:user)
3666 other_user = insert(:user)
3669 CommonAPI.post(user, %{
3670 "status" => "Am I cute?",
3671 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3674 object = Object.normalize(activity)
3678 |> assign(:user, other_user)
3679 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3681 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3684 test "returns 404 error when object is not exist", %{conn: conn} do
3685 user = insert(:user)
3689 |> assign(:user, user)
3690 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3692 assert json_response(conn, 404) == %{"error" => "Record not found"}
3695 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3696 user = insert(:user)
3697 other_user = insert(:user)
3700 CommonAPI.post(user, %{
3701 "status" => "Am I cute?",
3702 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3703 "visibility" => "private"
3706 object = Object.normalize(activity)
3710 |> assign(:user, other_user)
3711 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3713 assert json_response(conn, 404) == %{"error" => "Record not found"}
3717 describe "GET /api/v1/statuses/:id/favourited_by" do
3719 user = insert(:user)
3720 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3724 |> assign(:user, user)
3726 [conn: conn, activity: activity]
3729 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3730 other_user = insert(:user)
3731 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3735 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3736 |> json_response(:ok)
3738 [%{"id" => id}] = response
3740 assert id == other_user.id
3743 test "returns empty array when status has not been favorited yet", %{
3749 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3750 |> json_response(:ok)
3752 assert Enum.empty?(response)
3755 test "does not return users who have favorited the status but are blocked", %{
3756 conn: %{assigns: %{user: user}} = conn,
3759 other_user = insert(:user)
3760 {:ok, user} = User.block(user, other_user)
3762 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3766 |> assign(:user, user)
3767 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3768 |> json_response(:ok)
3770 assert Enum.empty?(response)
3773 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3774 other_user = insert(:user)
3775 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3779 |> assign(:user, nil)
3780 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3781 |> json_response(:ok)
3783 [%{"id" => id}] = response
3784 assert id == other_user.id
3788 describe "GET /api/v1/statuses/:id/reblogged_by" do
3790 user = insert(:user)
3791 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3795 |> assign(:user, user)
3797 [conn: conn, activity: activity]
3800 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3801 other_user = insert(:user)
3802 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3806 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3807 |> json_response(:ok)
3809 [%{"id" => id}] = response
3811 assert id == other_user.id
3814 test "returns empty array when status has not been reblogged yet", %{
3820 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3821 |> json_response(:ok)
3823 assert Enum.empty?(response)
3826 test "does not return users who have reblogged the status but are blocked", %{
3827 conn: %{assigns: %{user: user}} = conn,
3830 other_user = insert(:user)
3831 {:ok, user} = User.block(user, other_user)
3833 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3837 |> assign(:user, user)
3838 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3839 |> json_response(:ok)
3841 assert Enum.empty?(response)
3844 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3845 other_user = insert(:user)
3846 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3850 |> assign(:user, nil)
3851 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3852 |> json_response(:ok)
3854 [%{"id" => id}] = response
3855 assert id == other_user.id
3859 describe "POST /auth/password, with valid parameters" do
3860 setup %{conn: conn} do
3861 user = insert(:user)
3862 conn = post(conn, "/auth/password?email=#{user.email}")
3863 %{conn: conn, user: user}
3866 test "it returns 204", %{conn: conn} do
3867 assert json_response(conn, :no_content)
3870 test "it creates a PasswordResetToken record for user", %{user: user} do
3871 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3875 test "it sends an email to user", %{user: user} do
3876 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3878 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3879 notify_email = Pleroma.Config.get([:instance, :notify_email])
3880 instance_name = Pleroma.Config.get([:instance, :name])
3883 from: {instance_name, notify_email},
3884 to: {user.name, user.email},
3885 html_body: email.html_body
3890 describe "POST /auth/password, with invalid parameters" do
3892 user = insert(:user)
3896 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3897 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3898 assert conn.status == 404
3899 assert conn.resp_body == ""
3902 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3903 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3904 conn = post(conn, "/auth/password?email=#{user.email}")
3905 assert conn.status == 400
3906 assert conn.resp_body == ""
3910 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3912 setting = Pleroma.Config.get([:instance, :account_activation_required])
3915 Pleroma.Config.put([:instance, :account_activation_required], true)
3916 on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
3919 user = insert(:user)
3920 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3924 |> Changeset.change()
3925 |> Changeset.put_embed(:info, info_change)
3928 assert user.info.confirmation_pending
3933 test "resend account confirmation email", %{conn: conn, user: user} do
3935 |> assign(:user, user)
3936 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3937 |> json_response(:no_content)
3939 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3940 notify_email = Pleroma.Config.get([:instance, :notify_email])
3941 instance_name = Pleroma.Config.get([:instance, :name])
3944 from: {instance_name, notify_email},
3945 to: {user.name, user.email},
3946 html_body: email.html_body