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 user = insert(:user)
2905 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2907 [user: user, activity: activity]
2910 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2911 id_str = to_string(activity.id)
2913 assert %{"id" => ^id_str, "muted" => true} =
2915 |> assign(:user, user)
2916 |> post("/api/v1/statuses/#{activity.id}/mute")
2917 |> json_response(200)
2920 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2921 {:ok, _} = CommonAPI.add_mute(user, activity)
2925 |> assign(:user, user)
2926 |> post("/api/v1/statuses/#{activity.id}/mute")
2928 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2931 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2932 {:ok, _} = CommonAPI.add_mute(user, activity)
2934 id_str = to_string(activity.id)
2935 user = refresh_record(user)
2937 assert %{"id" => ^id_str, "muted" => false} =
2939 |> assign(:user, user)
2940 |> post("/api/v1/statuses/#{activity.id}/unmute")
2941 |> json_response(200)
2945 describe "reports" do
2947 reporter = insert(:user)
2948 target_user = insert(:user)
2950 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2952 [reporter: reporter, target_user: target_user, activity: activity]
2955 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2956 assert %{"action_taken" => false, "id" => _} =
2958 |> assign(:user, reporter)
2959 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2960 |> json_response(200)
2963 test "submit a report with statuses and comment", %{
2966 target_user: target_user,
2969 assert %{"action_taken" => false, "id" => _} =
2971 |> assign(:user, reporter)
2972 |> post("/api/v1/reports", %{
2973 "account_id" => target_user.id,
2974 "status_ids" => [activity.id],
2975 "comment" => "bad status!",
2976 "forward" => "false"
2978 |> json_response(200)
2981 test "account_id is required", %{
2986 assert %{"error" => "Valid `account_id` required"} =
2988 |> assign(:user, reporter)
2989 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2990 |> json_response(400)
2993 test "comment must be up to the size specified in the config", %{
2996 target_user: target_user
2998 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2999 comment = String.pad_trailing("a", max_size + 1, "a")
3001 error = %{"error" => "Comment must be up to #{max_size} characters"}
3005 |> assign(:user, reporter)
3006 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3007 |> json_response(400)
3010 test "returns error when account is not exist", %{
3017 |> assign(:user, reporter)
3018 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3020 assert json_response(conn, 400) == %{"error" => "Account not found"}
3024 describe "link headers" do
3025 test "preserves parameters in link headers", %{conn: conn} do
3026 user = insert(:user)
3027 other_user = insert(:user)
3030 CommonAPI.post(other_user, %{
3031 "status" => "hi @#{user.nickname}",
3032 "visibility" => "public"
3036 CommonAPI.post(other_user, %{
3037 "status" => "hi @#{user.nickname}",
3038 "visibility" => "public"
3041 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3042 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3046 |> assign(:user, user)
3047 |> get("/api/v1/notifications", %{media_only: true})
3049 assert [link_header] = get_resp_header(conn, "link")
3050 assert link_header =~ ~r/media_only=true/
3051 assert link_header =~ ~r/min_id=#{notification2.id}/
3052 assert link_header =~ ~r/max_id=#{notification1.id}/
3056 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3057 # Need to set an old-style integer ID to reproduce the problem
3058 # (these are no longer assigned to new accounts but were preserved
3059 # for existing accounts during the migration to flakeIDs)
3060 user_one = insert(:user, %{id: 1212})
3061 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3065 |> get("/api/v1/accounts/#{user_one.id}")
3069 |> get("/api/v1/accounts/#{user_two.nickname}")
3073 |> get("/api/v1/accounts/#{user_two.id}")
3075 acc_one = json_response(resp_one, 200)
3076 acc_two = json_response(resp_two, 200)
3077 acc_three = json_response(resp_three, 200)
3078 refute acc_one == acc_two
3079 assert acc_two == acc_three
3082 describe "custom emoji" do
3083 test "with tags", %{conn: conn} do
3086 |> get("/api/v1/custom_emojis")
3087 |> json_response(200)
3089 assert Map.has_key?(emoji, "shortcode")
3090 assert Map.has_key?(emoji, "static_url")
3091 assert Map.has_key?(emoji, "tags")
3092 assert is_list(emoji["tags"])
3093 assert Map.has_key?(emoji, "category")
3094 assert Map.has_key?(emoji, "url")
3095 assert Map.has_key?(emoji, "visible_in_picker")
3099 describe "index/2 redirections" do
3100 setup %{conn: conn} do
3104 signing_salt: "cooldude"
3109 |> Plug.Session.call(Plug.Session.init(session_opts))
3112 test_path = "/web/statuses/test"
3113 %{conn: conn, path: test_path}
3116 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3117 conn = get(conn, path)
3119 assert conn.status == 302
3120 assert redirected_to(conn) == "/web/login"
3123 test "redirects not logged-in users to the login page on private instances", %{
3127 is_public = Pleroma.Config.get([:instance, :public])
3128 Pleroma.Config.put([:instance, :public], false)
3130 conn = get(conn, path)
3132 assert conn.status == 302
3133 assert redirected_to(conn) == "/web/login"
3135 Pleroma.Config.put([:instance, :public], is_public)
3138 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3139 token = insert(:oauth_token)
3143 |> assign(:user, token.user)
3144 |> put_session(:oauth_token, token.token)
3147 assert conn.status == 200
3150 test "saves referer path to session", %{conn: conn, path: path} do
3151 conn = get(conn, path)
3152 return_to = Plug.Conn.get_session(conn, :return_to)
3154 assert return_to == path
3157 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3158 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3159 auth = insert(:oauth_authorization, app: app)
3163 |> put_session(:return_to, path)
3164 |> get("/web/login", %{code: auth.token})
3166 assert conn.status == 302
3167 assert redirected_to(conn) == path
3170 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3171 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3172 auth = insert(:oauth_authorization, app: app)
3174 conn = get(conn, "/web/login", %{code: auth.token})
3176 assert conn.status == 302
3177 assert redirected_to(conn) == "/web/getting-started"
3181 describe "scheduled activities" do
3182 test "creates a scheduled activity", %{conn: conn} do
3183 user = insert(:user)
3184 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3188 |> assign(:user, user)
3189 |> post("/api/v1/statuses", %{
3190 "status" => "scheduled",
3191 "scheduled_at" => scheduled_at
3194 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3195 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3196 assert [] == Repo.all(Activity)
3199 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3200 user = insert(:user)
3201 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3203 file = %Plug.Upload{
3204 content_type: "image/jpg",
3205 path: Path.absname("test/fixtures/image.jpg"),
3206 filename: "an_image.jpg"
3209 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3213 |> assign(:user, user)
3214 |> post("/api/v1/statuses", %{
3215 "media_ids" => [to_string(upload.id)],
3216 "status" => "scheduled",
3217 "scheduled_at" => scheduled_at
3220 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3221 assert %{"type" => "image"} = media_attachment
3224 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3226 user = insert(:user)
3229 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3233 |> assign(:user, user)
3234 |> post("/api/v1/statuses", %{
3235 "status" => "not scheduled",
3236 "scheduled_at" => scheduled_at
3239 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3240 assert [] == Repo.all(ScheduledActivity)
3243 test "returns error when daily user limit is exceeded", %{conn: conn} do
3244 user = insert(:user)
3247 NaiveDateTime.utc_now()
3248 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3249 |> NaiveDateTime.to_iso8601()
3251 attrs = %{params: %{}, scheduled_at: today}
3252 {:ok, _} = ScheduledActivity.create(user, attrs)
3253 {:ok, _} = ScheduledActivity.create(user, attrs)
3257 |> assign(:user, user)
3258 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3260 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3263 test "returns error when total user limit is exceeded", %{conn: conn} do
3264 user = insert(:user)
3267 NaiveDateTime.utc_now()
3268 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3269 |> NaiveDateTime.to_iso8601()
3272 NaiveDateTime.utc_now()
3273 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3274 |> NaiveDateTime.to_iso8601()
3276 attrs = %{params: %{}, scheduled_at: today}
3277 {:ok, _} = ScheduledActivity.create(user, attrs)
3278 {:ok, _} = ScheduledActivity.create(user, attrs)
3279 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3283 |> assign(:user, user)
3284 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3286 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3289 test "shows scheduled activities", %{conn: conn} do
3290 user = insert(:user)
3291 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3292 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3293 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3294 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3298 |> assign(:user, user)
3303 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3305 result = json_response(conn_res, 200)
3306 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3311 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3313 result = json_response(conn_res, 200)
3314 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3319 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3321 result = json_response(conn_res, 200)
3322 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3325 test "shows a scheduled activity", %{conn: conn} do
3326 user = insert(:user)
3327 scheduled_activity = insert(:scheduled_activity, user: user)
3331 |> assign(:user, user)
3332 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3334 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3335 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3339 |> assign(:user, user)
3340 |> get("/api/v1/scheduled_statuses/404")
3342 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3345 test "updates a scheduled activity", %{conn: conn} do
3346 user = insert(:user)
3347 scheduled_activity = insert(:scheduled_activity, user: user)
3350 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3354 |> assign(:user, user)
3355 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3356 scheduled_at: new_scheduled_at
3359 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3360 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3364 |> assign(:user, user)
3365 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3367 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3370 test "deletes a scheduled activity", %{conn: conn} do
3371 user = insert(:user)
3372 scheduled_activity = insert(:scheduled_activity, user: user)
3376 |> assign(:user, user)
3377 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3379 assert %{} = json_response(res_conn, 200)
3380 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3384 |> assign(:user, user)
3385 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3387 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3391 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3392 user1 = insert(:user)
3393 user2 = insert(:user)
3394 user3 = insert(:user)
3396 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3398 # Reply to status from another user
3401 |> assign(:user, user2)
3402 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3404 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3406 activity = Activity.get_by_id_with_object(id)
3408 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3409 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3411 # Reblog from the third user
3414 |> assign(:user, user3)
3415 |> post("/api/v1/statuses/#{activity.id}/reblog")
3417 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3418 json_response(conn2, 200)
3420 assert to_string(activity.id) == id
3422 # Getting third user status
3425 |> assign(:user, user3)
3426 |> get("api/v1/timelines/home")
3428 [reblogged_activity] = json_response(conn3, 200)
3430 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3432 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3433 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3436 describe "create account by app" do
3437 test "Account registration via Application", %{conn: conn} do
3440 |> post("/api/v1/apps", %{
3441 client_name: "client_name",
3442 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3443 scopes: "read, write, follow"
3447 "client_id" => client_id,
3448 "client_secret" => client_secret,
3450 "name" => "client_name",
3451 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3454 } = json_response(conn, 200)
3458 |> post("/oauth/token", %{
3459 grant_type: "client_credentials",
3460 client_id: client_id,
3461 client_secret: client_secret
3464 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3465 json_response(conn, 200)
3468 token_from_db = Repo.get_by(Token, token: token)
3469 assert token_from_db
3471 assert scope == "read write follow"
3475 |> put_req_header("authorization", "Bearer " <> token)
3476 |> post("/api/v1/accounts", %{
3478 email: "lain@example.org",
3479 password: "PlzDontHackLain",
3484 "access_token" => token,
3485 "created_at" => _created_at,
3487 "token_type" => "Bearer"
3488 } = json_response(conn, 200)
3490 token_from_db = Repo.get_by(Token, token: token)
3491 assert token_from_db
3492 token_from_db = Repo.preload(token_from_db, :user)
3493 assert token_from_db.user
3495 assert token_from_db.user.info.confirmation_pending
3498 test "rate limit", %{conn: conn} do
3499 app_token = insert(:oauth_token, user: nil)
3502 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3503 |> Map.put(:remote_ip, {15, 15, 15, 15})
3508 |> post("/api/v1/accounts", %{
3509 username: "#{i}lain",
3510 email: "#{i}lain@example.org",
3511 password: "PlzDontHackLain",
3516 "access_token" => token,
3517 "created_at" => _created_at,
3519 "token_type" => "Bearer"
3520 } = json_response(conn, 200)
3522 token_from_db = Repo.get_by(Token, token: token)
3523 assert token_from_db
3524 token_from_db = Repo.preload(token_from_db, :user)
3525 assert token_from_db.user
3527 assert token_from_db.user.info.confirmation_pending
3532 |> post("/api/v1/accounts", %{
3534 email: "6lain@example.org",
3535 password: "PlzDontHackLain",
3539 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3543 describe "GET /api/v1/polls/:id" do
3544 test "returns poll entity for object id", %{conn: conn} do
3545 user = insert(:user)
3548 CommonAPI.post(user, %{
3549 "status" => "Pleroma does",
3550 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3553 object = Object.normalize(activity)
3557 |> assign(:user, user)
3558 |> get("/api/v1/polls/#{object.id}")
3560 response = json_response(conn, 200)
3561 id = to_string(object.id)
3562 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3565 test "does not expose polls for private statuses", %{conn: conn} do
3566 user = insert(:user)
3567 other_user = insert(:user)
3570 CommonAPI.post(user, %{
3571 "status" => "Pleroma does",
3572 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3573 "visibility" => "private"
3576 object = Object.normalize(activity)
3580 |> assign(:user, other_user)
3581 |> get("/api/v1/polls/#{object.id}")
3583 assert json_response(conn, 404)
3587 describe "POST /api/v1/polls/:id/votes" do
3588 test "votes are added to the poll", %{conn: conn} do
3589 user = insert(:user)
3590 other_user = insert(:user)
3593 CommonAPI.post(user, %{
3594 "status" => "A very delicious sandwich",
3596 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3602 object = Object.normalize(activity)
3606 |> assign(:user, other_user)
3607 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3609 assert json_response(conn, 200)
3610 object = Object.get_by_id(object.id)
3612 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3617 test "author can't vote", %{conn: conn} do
3618 user = insert(:user)
3621 CommonAPI.post(user, %{
3622 "status" => "Am I cute?",
3623 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3626 object = Object.normalize(activity)
3629 |> assign(:user, user)
3630 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3631 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3633 object = Object.get_by_id(object.id)
3635 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3638 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3639 user = insert(:user)
3640 other_user = insert(:user)
3643 CommonAPI.post(user, %{
3644 "status" => "The glass is",
3645 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3648 object = Object.normalize(activity)
3651 |> assign(:user, other_user)
3652 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3653 |> json_response(422) == %{"error" => "Too many choices"}
3655 object = Object.get_by_id(object.id)
3657 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3662 test "does not allow choice index to be greater than options count", %{conn: conn} do
3663 user = insert(:user)
3664 other_user = insert(:user)
3667 CommonAPI.post(user, %{
3668 "status" => "Am I cute?",
3669 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3672 object = Object.normalize(activity)
3676 |> assign(:user, other_user)
3677 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3679 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3682 test "returns 404 error when object is not exist", %{conn: conn} do
3683 user = insert(:user)
3687 |> assign(:user, user)
3688 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3690 assert json_response(conn, 404) == %{"error" => "Record not found"}
3693 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3694 user = insert(:user)
3695 other_user = insert(:user)
3698 CommonAPI.post(user, %{
3699 "status" => "Am I cute?",
3700 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3701 "visibility" => "private"
3704 object = Object.normalize(activity)
3708 |> assign(:user, other_user)
3709 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3711 assert json_response(conn, 404) == %{"error" => "Record not found"}
3715 describe "GET /api/v1/statuses/:id/favourited_by" do
3717 user = insert(:user)
3718 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3722 |> assign(:user, user)
3724 [conn: conn, activity: activity]
3727 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3728 other_user = insert(:user)
3729 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3733 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3734 |> json_response(:ok)
3736 [%{"id" => id}] = response
3738 assert id == other_user.id
3741 test "returns empty array when status has not been favorited yet", %{
3747 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3748 |> json_response(:ok)
3750 assert Enum.empty?(response)
3753 test "does not return users who have favorited the status but are blocked", %{
3754 conn: %{assigns: %{user: user}} = conn,
3757 other_user = insert(:user)
3758 {:ok, user} = User.block(user, other_user)
3760 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3764 |> assign(:user, user)
3765 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3766 |> json_response(:ok)
3768 assert Enum.empty?(response)
3771 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3772 other_user = insert(:user)
3773 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3777 |> assign(:user, nil)
3778 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3779 |> json_response(:ok)
3781 [%{"id" => id}] = response
3782 assert id == other_user.id
3786 describe "GET /api/v1/statuses/:id/reblogged_by" do
3788 user = insert(:user)
3789 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3793 |> assign(:user, user)
3795 [conn: conn, activity: activity]
3798 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3799 other_user = insert(:user)
3800 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3804 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3805 |> json_response(:ok)
3807 [%{"id" => id}] = response
3809 assert id == other_user.id
3812 test "returns empty array when status has not been reblogged yet", %{
3818 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3819 |> json_response(:ok)
3821 assert Enum.empty?(response)
3824 test "does not return users who have reblogged the status but are blocked", %{
3825 conn: %{assigns: %{user: user}} = conn,
3828 other_user = insert(:user)
3829 {:ok, user} = User.block(user, other_user)
3831 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3835 |> assign(:user, user)
3836 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3837 |> json_response(:ok)
3839 assert Enum.empty?(response)
3842 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3843 other_user = insert(:user)
3844 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3848 |> assign(:user, nil)
3849 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3850 |> json_response(:ok)
3852 [%{"id" => id}] = response
3853 assert id == other_user.id
3857 describe "POST /auth/password, with valid parameters" do
3858 setup %{conn: conn} do
3859 user = insert(:user)
3860 conn = post(conn, "/auth/password?email=#{user.email}")
3861 %{conn: conn, user: user}
3864 test "it returns 204", %{conn: conn} do
3865 assert json_response(conn, :no_content)
3868 test "it creates a PasswordResetToken record for user", %{user: user} do
3869 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3873 test "it sends an email to user", %{user: user} do
3874 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3876 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3877 notify_email = Pleroma.Config.get([:instance, :notify_email])
3878 instance_name = Pleroma.Config.get([:instance, :name])
3881 from: {instance_name, notify_email},
3882 to: {user.name, user.email},
3883 html_body: email.html_body
3888 describe "POST /auth/password, with invalid parameters" do
3890 user = insert(:user)
3894 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3895 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3896 assert conn.status == 404
3897 assert conn.resp_body == ""
3900 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3901 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3902 conn = post(conn, "/auth/password?email=#{user.email}")
3903 assert conn.status == 400
3904 assert conn.resp_body == ""
3908 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3910 setting = Pleroma.Config.get([:instance, :account_activation_required])
3913 Pleroma.Config.put([:instance, :account_activation_required], true)
3914 on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
3917 user = insert(:user)
3918 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3922 |> Changeset.change()
3923 |> Changeset.put_embed(:info, info_change)
3926 assert user.info.confirmation_pending
3931 test "resend account confirmation email", %{conn: conn, user: user} do
3933 |> assign(:user, user)
3934 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3935 |> json_response(:no_content)
3937 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3938 notify_email = Pleroma.Config.get([:instance, :notify_email])
3939 instance_name = Pleroma.Config.get([:instance, :name])
3942 from: {instance_name, notify_email},
3943 to: {user.name, user.email},
3944 html_body: email.html_body