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
27 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
30 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
34 test "the home timeline", %{conn: conn} do
36 following = insert(:user)
38 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
42 |> assign(:user, user)
43 |> get("/api/v1/timelines/home")
45 assert Enum.empty?(json_response(conn, 200))
47 {:ok, user} = User.follow(user, following)
51 |> assign(:user, user)
52 |> get("/api/v1/timelines/home")
54 assert [%{"content" => "test"}] = json_response(conn, 200)
57 test "the public timeline", %{conn: conn} do
58 following = insert(:user)
61 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
64 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
68 |> get("/api/v1/timelines/public", %{"local" => "False"})
70 assert length(json_response(conn, 200)) == 2
74 |> get("/api/v1/timelines/public", %{"local" => "True"})
76 assert [%{"content" => "test"}] = json_response(conn, 200)
80 |> get("/api/v1/timelines/public", %{"local" => "1"})
82 assert [%{"content" => "test"}] = json_response(conn, 200)
86 test "the public timeline when public is set to false", %{conn: conn} do
87 public = Pleroma.Config.get([:instance, :public])
88 Pleroma.Config.put([:instance, :public], false)
91 Pleroma.Config.put([:instance, :public], public)
95 |> get("/api/v1/timelines/public", %{"local" => "False"})
96 |> json_response(403) == %{"error" => "This resource requires authentication."}
99 describe "posting statuses" do
105 |> assign(:user, user)
110 test "posting a status", %{conn: conn} do
111 idempotency_key = "Pikachu rocks!"
115 |> put_req_header("idempotency-key", idempotency_key)
116 |> post("/api/v1/statuses", %{
118 "spoiler_text" => "2hu",
119 "sensitive" => "false"
122 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
124 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
126 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
127 json_response(conn_one, 200)
129 assert Activity.get_by_id(id)
133 |> put_req_header("idempotency-key", idempotency_key)
134 |> post("/api/v1/statuses", %{
136 "spoiler_text" => "2hu",
137 "sensitive" => "false"
140 assert %{"id" => second_id} = json_response(conn_two, 200)
141 assert id == second_id
145 |> post("/api/v1/statuses", %{
147 "spoiler_text" => "2hu",
148 "sensitive" => "false"
151 assert %{"id" => third_id} = json_response(conn_three, 200)
152 refute id == third_id
155 test "replying to a status", %{conn: conn} do
157 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
161 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
163 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
165 activity = Activity.get_by_id(id)
167 assert activity.data["context"] == replied_to.data["context"]
168 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
171 test "replying to a direct message with visibility other than direct", %{conn: conn} do
173 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
175 Enum.each(["public", "private", "unlisted"], fn visibility ->
178 |> post("/api/v1/statuses", %{
179 "status" => "@#{user.nickname} hey",
180 "in_reply_to_id" => replied_to.id,
181 "visibility" => visibility
184 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
188 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
191 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
193 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
194 assert Activity.get_by_id(id)
197 test "posting a sensitive status", %{conn: conn} do
200 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
202 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
203 assert Activity.get_by_id(id)
206 test "posting a fake status", %{conn: conn} do
209 |> post("/api/v1/statuses", %{
211 "\"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"
214 real_status = json_response(real_conn, 200)
217 assert Object.get_by_ap_id(real_status["uri"])
221 |> Map.put("id", nil)
222 |> Map.put("url", nil)
223 |> Map.put("uri", nil)
224 |> Map.put("created_at", nil)
225 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
229 |> post("/api/v1/statuses", %{
231 "\"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",
235 fake_status = json_response(fake_conn, 200)
238 refute Object.get_by_ap_id(fake_status["uri"])
242 |> Map.put("id", nil)
243 |> Map.put("url", nil)
244 |> Map.put("uri", nil)
245 |> Map.put("created_at", nil)
246 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
248 assert real_status == fake_status
251 test "posting a status with OGP link preview", %{conn: conn} do
252 Pleroma.Config.put([:rich_media, :enabled], true)
256 |> post("/api/v1/statuses", %{
257 "status" => "https://example.com/ogp"
260 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
261 assert Activity.get_by_id(id)
262 Pleroma.Config.put([:rich_media, :enabled], false)
265 test "posting a direct status", %{conn: conn} do
266 user2 = insert(:user)
267 content = "direct cofe @#{user2.nickname}"
271 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
273 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
274 assert activity = Activity.get_by_id(id)
275 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
276 assert activity.data["to"] == [user2.ap_id]
277 assert activity.data["cc"] == []
281 describe "posting polls" do
282 test "posting a poll", %{conn: conn} do
284 time = NaiveDateTime.utc_now()
288 |> assign(:user, user)
289 |> post("/api/v1/statuses", %{
290 "status" => "Who is the #bestgrill?",
291 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
294 response = json_response(conn, 200)
296 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
297 title in ["Rei", "Asuka", "Misato"]
300 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
301 refute response["poll"]["expred"]
304 test "option limit is enforced", %{conn: conn} do
306 limit = Pleroma.Config.get([:instance, :poll_limits, :max_options])
310 |> assign(:user, user)
311 |> post("/api/v1/statuses", %{
313 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
316 %{"error" => error} = json_response(conn, 422)
317 assert error == "Poll can't contain more than #{limit} options"
320 test "option character limit is enforced", %{conn: conn} do
322 limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars])
326 |> assign(:user, user)
327 |> post("/api/v1/statuses", %{
330 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
335 %{"error" => error} = json_response(conn, 422)
336 assert error == "Poll options cannot be longer than #{limit} characters each"
339 test "minimal date limit is enforced", %{conn: conn} do
341 limit = Pleroma.Config.get([:instance, :poll_limits, :min_expiration])
345 |> assign(:user, user)
346 |> post("/api/v1/statuses", %{
347 "status" => "imagine arbitrary limits",
349 "options" => ["this post was made by pleroma gang"],
350 "expires_in" => limit - 1
354 %{"error" => error} = json_response(conn, 422)
355 assert error == "Expiration date is too soon"
358 test "maximum date limit is enforced", %{conn: conn} do
360 limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration])
364 |> assign(:user, user)
365 |> post("/api/v1/statuses", %{
366 "status" => "imagine arbitrary limits",
368 "options" => ["this post was made by pleroma gang"],
369 "expires_in" => limit + 1
373 %{"error" => error} = json_response(conn, 422)
374 assert error == "Expiration date is too far in the future"
378 test "direct timeline", %{conn: conn} do
379 user_one = insert(:user)
380 user_two = insert(:user)
382 {:ok, user_two} = User.follow(user_two, user_one)
385 CommonAPI.post(user_one, %{
386 "status" => "Hi @#{user_two.nickname}!",
387 "visibility" => "direct"
390 {:ok, _follower_only} =
391 CommonAPI.post(user_one, %{
392 "status" => "Hi @#{user_two.nickname}!",
393 "visibility" => "private"
396 # Only direct should be visible here
399 |> assign(:user, user_two)
400 |> get("api/v1/timelines/direct")
402 [status] = json_response(res_conn, 200)
404 assert %{"visibility" => "direct"} = status
405 assert status["url"] != direct.data["id"]
407 # User should be able to see his own direct message
410 |> assign(:user, user_one)
411 |> get("api/v1/timelines/direct")
413 [status] = json_response(res_conn, 200)
415 assert %{"visibility" => "direct"} = status
417 # Both should be visible here
420 |> assign(:user, user_two)
421 |> get("api/v1/timelines/home")
423 [_s1, _s2] = json_response(res_conn, 200)
426 Enum.each(1..20, fn _ ->
428 CommonAPI.post(user_one, %{
429 "status" => "Hi @#{user_two.nickname}!",
430 "visibility" => "direct"
436 |> assign(:user, user_two)
437 |> get("api/v1/timelines/direct")
439 statuses = json_response(res_conn, 200)
440 assert length(statuses) == 20
444 |> assign(:user, user_two)
445 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
447 [status] = json_response(res_conn, 200)
449 assert status["url"] != direct.data["id"]
452 test "Conversations", %{conn: conn} do
453 user_one = insert(:user)
454 user_two = insert(:user)
455 user_three = insert(:user)
457 {:ok, user_two} = User.follow(user_two, user_one)
460 CommonAPI.post(user_one, %{
461 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
462 "visibility" => "direct"
465 {:ok, _follower_only} =
466 CommonAPI.post(user_one, %{
467 "status" => "Hi @#{user_two.nickname}!",
468 "visibility" => "private"
473 |> assign(:user, user_one)
474 |> get("/api/v1/conversations")
476 assert response = json_response(res_conn, 200)
481 "accounts" => res_accounts,
482 "last_status" => res_last_status,
487 account_ids = Enum.map(res_accounts, & &1["id"])
488 assert length(res_accounts) == 2
489 assert user_two.id in account_ids
490 assert user_three.id in account_ids
491 assert is_binary(res_id)
492 assert unread == true
493 assert res_last_status["id"] == direct.id
495 # Apparently undocumented API endpoint
498 |> assign(:user, user_one)
499 |> post("/api/v1/conversations/#{res_id}/read")
501 assert response = json_response(res_conn, 200)
502 assert length(response["accounts"]) == 2
503 assert response["last_status"]["id"] == direct.id
504 assert response["unread"] == false
506 # (vanilla) Mastodon frontend behaviour
509 |> assign(:user, user_one)
510 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
512 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
515 test "doesn't include DMs from blocked users", %{conn: conn} do
516 blocker = insert(:user)
517 blocked = insert(:user)
519 {:ok, blocker} = User.block(blocker, blocked)
521 {:ok, _blocked_direct} =
522 CommonAPI.post(blocked, %{
523 "status" => "Hi @#{blocker.nickname}!",
524 "visibility" => "direct"
528 CommonAPI.post(user, %{
529 "status" => "Hi @#{blocker.nickname}!",
530 "visibility" => "direct"
535 |> assign(:user, user)
536 |> get("api/v1/timelines/direct")
538 [status] = json_response(res_conn, 200)
539 assert status["id"] == direct.id
542 test "verify_credentials", %{conn: conn} do
547 |> assign(:user, user)
548 |> get("/api/v1/accounts/verify_credentials")
550 response = json_response(conn, 200)
552 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
553 assert response["pleroma"]["chat_token"]
554 assert id == to_string(user.id)
557 test "verify_credentials default scope unlisted", %{conn: conn} do
558 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
562 |> assign(:user, user)
563 |> get("/api/v1/accounts/verify_credentials")
565 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
566 assert id == to_string(user.id)
569 test "apps/verify_credentials", %{conn: conn} do
570 token = insert(:oauth_token)
574 |> assign(:user, token.user)
575 |> assign(:token, token)
576 |> get("/api/v1/apps/verify_credentials")
578 app = Repo.preload(token, :app).app
581 "name" => app.client_name,
582 "website" => app.website,
583 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
586 assert expected == json_response(conn, 200)
589 test "user avatar can be set", %{conn: conn} do
591 avatar_image = File.read!("test/fixtures/avatar_data_uri")
595 |> assign(:user, user)
596 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
598 user = refresh_record(user)
612 assert %{"url" => _} = json_response(conn, 200)
615 test "user avatar can be reset", %{conn: conn} do
620 |> assign(:user, user)
621 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
623 user = User.get_cached_by_id(user.id)
625 assert user.avatar == nil
627 assert %{"url" => nil} = json_response(conn, 200)
630 test "can set profile banner", %{conn: conn} do
635 |> assign(:user, user)
636 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
638 user = refresh_record(user)
639 assert user.info.banner["type"] == "Image"
641 assert %{"url" => _} = json_response(conn, 200)
644 test "can reset profile banner", %{conn: conn} do
649 |> assign(:user, user)
650 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
652 user = refresh_record(user)
653 assert user.info.banner == %{}
655 assert %{"url" => nil} = json_response(conn, 200)
658 test "background image can be set", %{conn: conn} do
663 |> assign(:user, user)
664 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
666 user = refresh_record(user)
667 assert user.info.background["type"] == "Image"
668 assert %{"url" => _} = json_response(conn, 200)
671 test "background image can be reset", %{conn: conn} do
676 |> assign(:user, user)
677 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
679 user = refresh_record(user)
680 assert user.info.background == %{}
681 assert %{"url" => nil} = json_response(conn, 200)
684 test "creates an oauth app", %{conn: conn} do
686 app_attrs = build(:oauth_app)
690 |> assign(:user, user)
691 |> post("/api/v1/apps", %{
692 client_name: app_attrs.client_name,
693 redirect_uris: app_attrs.redirect_uris
696 [app] = Repo.all(App)
699 "name" => app.client_name,
700 "website" => app.website,
701 "client_id" => app.client_id,
702 "client_secret" => app.client_secret,
703 "id" => app.id |> to_string(),
704 "redirect_uri" => app.redirect_uris,
705 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
708 assert expected == json_response(conn, 200)
711 test "get a status", %{conn: conn} do
712 activity = insert(:note_activity)
716 |> get("/api/v1/statuses/#{activity.id}")
718 assert %{"id" => id} = json_response(conn, 200)
719 assert id == to_string(activity.id)
722 describe "deleting a status" do
723 test "when you created it", %{conn: conn} do
724 activity = insert(:note_activity)
725 author = User.get_cached_by_ap_id(activity.data["actor"])
729 |> assign(:user, author)
730 |> delete("/api/v1/statuses/#{activity.id}")
732 assert %{} = json_response(conn, 200)
734 refute Activity.get_by_id(activity.id)
737 test "when you didn't create it", %{conn: conn} do
738 activity = insert(:note_activity)
743 |> assign(:user, user)
744 |> delete("/api/v1/statuses/#{activity.id}")
746 assert %{"error" => _} = json_response(conn, 403)
748 assert Activity.get_by_id(activity.id) == activity
751 test "when you're an admin or moderator", %{conn: conn} do
752 activity1 = insert(:note_activity)
753 activity2 = insert(:note_activity)
754 admin = insert(:user, info: %{is_admin: true})
755 moderator = insert(:user, info: %{is_moderator: true})
759 |> assign(:user, admin)
760 |> delete("/api/v1/statuses/#{activity1.id}")
762 assert %{} = json_response(res_conn, 200)
766 |> assign(:user, moderator)
767 |> delete("/api/v1/statuses/#{activity2.id}")
769 assert %{} = json_response(res_conn, 200)
771 refute Activity.get_by_id(activity1.id)
772 refute Activity.get_by_id(activity2.id)
776 describe "filters" do
777 test "creating a filter", %{conn: conn} do
780 filter = %Pleroma.Filter{
787 |> assign(:user, user)
788 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
790 assert response = json_response(conn, 200)
791 assert response["phrase"] == filter.phrase
792 assert response["context"] == filter.context
793 assert response["irreversible"] == false
794 assert response["id"] != nil
795 assert response["id"] != ""
798 test "fetching a list of filters", %{conn: conn} do
801 query_one = %Pleroma.Filter{
808 query_two = %Pleroma.Filter{
815 {:ok, filter_one} = Pleroma.Filter.create(query_one)
816 {:ok, filter_two} = Pleroma.Filter.create(query_two)
820 |> assign(:user, user)
821 |> get("/api/v1/filters")
822 |> json_response(200)
828 filters: [filter_two, filter_one]
832 test "get a filter", %{conn: conn} do
835 query = %Pleroma.Filter{
842 {:ok, filter} = Pleroma.Filter.create(query)
846 |> assign(:user, user)
847 |> get("/api/v1/filters/#{filter.filter_id}")
849 assert _response = json_response(conn, 200)
852 test "update a filter", %{conn: conn} do
855 query = %Pleroma.Filter{
862 {:ok, _filter} = Pleroma.Filter.create(query)
864 new = %Pleroma.Filter{
871 |> assign(:user, user)
872 |> put("/api/v1/filters/#{query.filter_id}", %{
877 assert response = json_response(conn, 200)
878 assert response["phrase"] == new.phrase
879 assert response["context"] == new.context
882 test "delete a filter", %{conn: conn} do
885 query = %Pleroma.Filter{
892 {:ok, filter} = Pleroma.Filter.create(query)
896 |> assign(:user, user)
897 |> delete("/api/v1/filters/#{filter.filter_id}")
899 assert response = json_response(conn, 200)
900 assert response == %{}
905 test "creating a list", %{conn: conn} do
910 |> assign(:user, user)
911 |> post("/api/v1/lists", %{"title" => "cuties"})
913 assert %{"title" => title} = json_response(conn, 200)
914 assert title == "cuties"
917 test "adding users to a list", %{conn: conn} do
919 other_user = insert(:user)
920 {:ok, list} = Pleroma.List.create("name", user)
924 |> assign(:user, user)
925 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
927 assert %{} == json_response(conn, 200)
928 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
929 assert following == [other_user.follower_address]
932 test "removing users from a list", %{conn: conn} do
934 other_user = insert(:user)
935 third_user = insert(:user)
936 {:ok, list} = Pleroma.List.create("name", user)
937 {:ok, list} = Pleroma.List.follow(list, other_user)
938 {:ok, list} = Pleroma.List.follow(list, third_user)
942 |> assign(:user, user)
943 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
945 assert %{} == json_response(conn, 200)
946 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
947 assert following == [third_user.follower_address]
950 test "listing users in a list", %{conn: conn} do
952 other_user = insert(:user)
953 {:ok, list} = Pleroma.List.create("name", user)
954 {:ok, list} = Pleroma.List.follow(list, other_user)
958 |> assign(:user, user)
959 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
961 assert [%{"id" => id}] = json_response(conn, 200)
962 assert id == to_string(other_user.id)
965 test "retrieving a list", %{conn: conn} do
967 {:ok, list} = Pleroma.List.create("name", user)
971 |> assign(:user, user)
972 |> get("/api/v1/lists/#{list.id}")
974 assert %{"id" => id} = json_response(conn, 200)
975 assert id == to_string(list.id)
978 test "renaming a list", %{conn: conn} do
980 {:ok, list} = Pleroma.List.create("name", user)
984 |> assign(:user, user)
985 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
987 assert %{"title" => name} = json_response(conn, 200)
988 assert name == "newname"
991 test "deleting a list", %{conn: conn} do
993 {:ok, list} = Pleroma.List.create("name", user)
997 |> assign(:user, user)
998 |> delete("/api/v1/lists/#{list.id}")
1000 assert %{} = json_response(conn, 200)
1001 assert is_nil(Repo.get(Pleroma.List, list.id))
1004 test "list timeline", %{conn: conn} do
1005 user = insert(:user)
1006 other_user = insert(:user)
1007 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1008 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1009 {:ok, list} = Pleroma.List.create("name", user)
1010 {:ok, list} = Pleroma.List.follow(list, other_user)
1014 |> assign(:user, user)
1015 |> get("/api/v1/timelines/list/#{list.id}")
1017 assert [%{"id" => id}] = json_response(conn, 200)
1019 assert id == to_string(activity_two.id)
1022 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1023 user = insert(:user)
1024 other_user = insert(:user)
1025 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1027 {:ok, _activity_two} =
1028 CommonAPI.post(other_user, %{
1029 "status" => "Marisa is cute.",
1030 "visibility" => "private"
1033 {:ok, list} = Pleroma.List.create("name", user)
1034 {:ok, list} = Pleroma.List.follow(list, other_user)
1038 |> assign(:user, user)
1039 |> get("/api/v1/timelines/list/#{list.id}")
1041 assert [%{"id" => id}] = json_response(conn, 200)
1043 assert id == to_string(activity_one.id)
1047 describe "notifications" do
1048 test "list of notifications", %{conn: conn} do
1049 user = insert(:user)
1050 other_user = insert(:user)
1052 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1054 {:ok, [_notification]} = Notification.create_notifications(activity)
1058 |> assign(:user, user)
1059 |> get("/api/v1/notifications")
1062 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1064 }\">@<span>#{user.nickname}</span></a></span>"
1066 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1067 assert response == expected_response
1070 test "getting a single notification", %{conn: conn} do
1071 user = insert(:user)
1072 other_user = insert(:user)
1074 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1076 {:ok, [notification]} = Notification.create_notifications(activity)
1080 |> assign(:user, user)
1081 |> get("/api/v1/notifications/#{notification.id}")
1084 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1086 }\">@<span>#{user.nickname}</span></a></span>"
1088 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1089 assert response == expected_response
1092 test "dismissing a single notification", %{conn: conn} do
1093 user = insert(:user)
1094 other_user = insert(:user)
1096 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1098 {:ok, [notification]} = Notification.create_notifications(activity)
1102 |> assign(:user, user)
1103 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1105 assert %{} = json_response(conn, 200)
1108 test "clearing all notifications", %{conn: conn} do
1109 user = insert(:user)
1110 other_user = insert(:user)
1112 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1114 {:ok, [_notification]} = Notification.create_notifications(activity)
1118 |> assign(:user, user)
1119 |> post("/api/v1/notifications/clear")
1121 assert %{} = json_response(conn, 200)
1125 |> assign(:user, user)
1126 |> get("/api/v1/notifications")
1128 assert all = json_response(conn, 200)
1132 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1133 user = insert(:user)
1134 other_user = insert(:user)
1136 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1137 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1138 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1139 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1141 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1142 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1143 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1144 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1148 |> assign(:user, user)
1153 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1155 result = json_response(conn_res, 200)
1156 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1161 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1163 result = json_response(conn_res, 200)
1164 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1169 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1171 result = json_response(conn_res, 200)
1172 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1175 test "filters notifications using exclude_types", %{conn: conn} do
1176 user = insert(:user)
1177 other_user = insert(:user)
1179 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1180 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1181 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1182 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1183 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1185 mention_notification_id =
1186 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1188 favorite_notification_id =
1189 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1191 reblog_notification_id =
1192 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1194 follow_notification_id =
1195 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1199 |> assign(:user, user)
1202 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1204 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1207 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1209 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1212 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1214 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1217 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1219 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1222 test "destroy multiple", %{conn: conn} do
1223 user = insert(:user)
1224 other_user = insert(:user)
1226 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1227 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1228 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1229 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1231 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1232 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1233 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1234 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1238 |> assign(:user, user)
1242 |> get("/api/v1/notifications")
1244 result = json_response(conn_res, 200)
1245 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1249 |> assign(:user, other_user)
1253 |> get("/api/v1/notifications")
1255 result = json_response(conn_res, 200)
1256 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1260 |> delete("/api/v1/notifications/destroy_multiple", %{
1261 "ids" => [notification1_id, notification2_id]
1264 assert json_response(conn_destroy, 200) == %{}
1268 |> get("/api/v1/notifications")
1270 result = json_response(conn_res, 200)
1271 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1274 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1275 user = insert(:user)
1276 user2 = insert(:user)
1278 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1279 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1281 conn = assign(conn, :user, user)
1283 conn = get(conn, "/api/v1/notifications")
1285 assert length(json_response(conn, 200)) == 1
1287 {:ok, user} = User.mute(user, user2)
1289 conn = assign(build_conn(), :user, user)
1290 conn = get(conn, "/api/v1/notifications")
1292 assert json_response(conn, 200) == []
1295 test "see notifications after muting user without notifications", %{conn: conn} do
1296 user = insert(:user)
1297 user2 = insert(:user)
1299 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1300 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1302 conn = assign(conn, :user, user)
1304 conn = get(conn, "/api/v1/notifications")
1306 assert length(json_response(conn, 200)) == 1
1308 {:ok, user} = User.mute(user, user2, false)
1310 conn = assign(build_conn(), :user, user)
1311 conn = get(conn, "/api/v1/notifications")
1313 assert length(json_response(conn, 200)) == 1
1316 test "see notifications after muting user with notifications and with_muted parameter", %{
1319 user = insert(:user)
1320 user2 = insert(:user)
1322 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1323 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1325 conn = assign(conn, :user, user)
1327 conn = get(conn, "/api/v1/notifications")
1329 assert length(json_response(conn, 200)) == 1
1331 {:ok, user} = User.mute(user, user2)
1333 conn = assign(build_conn(), :user, user)
1334 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1336 assert length(json_response(conn, 200)) == 1
1340 describe "reblogging" do
1341 test "reblogs and returns the reblogged status", %{conn: conn} do
1342 activity = insert(:note_activity)
1343 user = insert(:user)
1347 |> assign(:user, user)
1348 |> post("/api/v1/statuses/#{activity.id}/reblog")
1351 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1353 } = json_response(conn, 200)
1355 assert to_string(activity.id) == id
1358 test "reblogged status for another user", %{conn: conn} do
1359 activity = insert(:note_activity)
1360 user1 = insert(:user)
1361 user2 = insert(:user)
1362 user3 = insert(:user)
1363 CommonAPI.favorite(activity.id, user2)
1364 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1365 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1366 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1370 |> assign(:user, user3)
1371 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1374 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1375 "reblogged" => false,
1376 "favourited" => false,
1377 "bookmarked" => false
1378 } = json_response(conn_res, 200)
1382 |> assign(:user, user2)
1383 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1386 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1387 "reblogged" => true,
1388 "favourited" => true,
1389 "bookmarked" => true
1390 } = json_response(conn_res, 200)
1392 assert to_string(activity.id) == id
1395 test "returns 400 error when activity is not exist", %{conn: conn} do
1396 user = insert(:user)
1400 |> assign(:user, user)
1401 |> post("/api/v1/statuses/foo/reblog")
1403 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1407 describe "unreblogging" do
1408 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1409 activity = insert(:note_activity)
1410 user = insert(:user)
1412 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1416 |> assign(:user, user)
1417 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1419 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1421 assert to_string(activity.id) == id
1424 test "returns 400 error when activity is not exist", %{conn: conn} do
1425 user = insert(:user)
1429 |> assign(:user, user)
1430 |> post("/api/v1/statuses/foo/unreblog")
1432 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1436 describe "favoriting" do
1437 test "favs a status and returns it", %{conn: conn} do
1438 activity = insert(:note_activity)
1439 user = insert(:user)
1443 |> assign(:user, user)
1444 |> post("/api/v1/statuses/#{activity.id}/favourite")
1446 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1447 json_response(conn, 200)
1449 assert to_string(activity.id) == id
1452 test "returns 400 error for a wrong id", %{conn: conn} do
1453 user = insert(:user)
1457 |> assign(:user, user)
1458 |> post("/api/v1/statuses/1/favourite")
1460 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1464 describe "unfavoriting" do
1465 test "unfavorites a status and returns it", %{conn: conn} do
1466 activity = insert(:note_activity)
1467 user = insert(:user)
1469 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1473 |> assign(:user, user)
1474 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1476 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1477 json_response(conn, 200)
1479 assert to_string(activity.id) == id
1482 test "returns 400 error for a wrong id", %{conn: conn} do
1483 user = insert(:user)
1487 |> assign(:user, user)
1488 |> post("/api/v1/statuses/1/unfavourite")
1490 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1494 describe "user timelines" do
1495 test "gets a users statuses", %{conn: conn} do
1496 user_one = insert(:user)
1497 user_two = insert(:user)
1498 user_three = insert(:user)
1500 {:ok, user_three} = User.follow(user_three, user_one)
1502 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1504 {:ok, direct_activity} =
1505 CommonAPI.post(user_one, %{
1506 "status" => "Hi, @#{user_two.nickname}.",
1507 "visibility" => "direct"
1510 {:ok, private_activity} =
1511 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1515 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1517 assert [%{"id" => id}] = json_response(resp, 200)
1518 assert id == to_string(activity.id)
1522 |> assign(:user, user_two)
1523 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1525 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1526 assert id_one == to_string(direct_activity.id)
1527 assert id_two == to_string(activity.id)
1531 |> assign(:user, user_three)
1532 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1534 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1535 assert id_one == to_string(private_activity.id)
1536 assert id_two == to_string(activity.id)
1539 test "unimplemented pinned statuses feature", %{conn: conn} do
1540 note = insert(:note_activity)
1541 user = User.get_cached_by_ap_id(note.data["actor"])
1545 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1547 assert json_response(conn, 200) == []
1550 test "gets an users media", %{conn: conn} do
1551 note = insert(:note_activity)
1552 user = User.get_cached_by_ap_id(note.data["actor"])
1554 file = %Plug.Upload{
1555 content_type: "image/jpg",
1556 path: Path.absname("test/fixtures/image.jpg"),
1557 filename: "an_image.jpg"
1561 TwitterAPI.upload(file, user, "json")
1565 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1569 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1571 assert [%{"id" => id}] = json_response(conn, 200)
1572 assert id == to_string(image_post.id)
1576 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1578 assert [%{"id" => id}] = json_response(conn, 200)
1579 assert id == to_string(image_post.id)
1582 test "gets a user's statuses without reblogs", %{conn: conn} do
1583 user = insert(:user)
1584 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1585 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1589 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1591 assert [%{"id" => id}] = json_response(conn, 200)
1592 assert id == to_string(post.id)
1596 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1598 assert [%{"id" => id}] = json_response(conn, 200)
1599 assert id == to_string(post.id)
1602 test "filters user's statuses by a hashtag", %{conn: conn} do
1603 user = insert(:user)
1604 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1605 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1609 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1611 assert [%{"id" => id}] = json_response(conn, 200)
1612 assert id == to_string(post.id)
1616 describe "user relationships" do
1617 test "returns the relationships for the current user", %{conn: conn} do
1618 user = insert(:user)
1619 other_user = insert(:user)
1620 {:ok, user} = User.follow(user, other_user)
1624 |> assign(:user, user)
1625 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1627 assert [relationship] = json_response(conn, 200)
1629 assert to_string(other_user.id) == relationship["id"]
1633 describe "media upload" do
1635 upload_config = Pleroma.Config.get([Pleroma.Upload])
1636 proxy_config = Pleroma.Config.get([:media_proxy])
1639 Pleroma.Config.put([Pleroma.Upload], upload_config)
1640 Pleroma.Config.put([:media_proxy], proxy_config)
1643 user = insert(:user)
1647 |> assign(:user, user)
1649 image = %Plug.Upload{
1650 content_type: "image/jpg",
1651 path: Path.absname("test/fixtures/image.jpg"),
1652 filename: "an_image.jpg"
1655 [conn: conn, image: image]
1658 test "returns uploaded image", %{conn: conn, image: image} do
1659 desc = "Description of the image"
1663 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1664 |> json_response(:ok)
1666 assert media["type"] == "image"
1667 assert media["description"] == desc
1670 object = Repo.get(Object, media["id"])
1671 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1674 test "returns proxied url when media proxy is enabled", %{conn: conn, image: image} do
1675 Pleroma.Config.put([Pleroma.Upload, :base_url], "https://media.pleroma.social")
1677 proxy_url = "https://cache.pleroma.social"
1678 Pleroma.Config.put([:media_proxy, :enabled], true)
1679 Pleroma.Config.put([:media_proxy, :base_url], proxy_url)
1683 |> post("/api/v1/media", %{"file" => image})
1684 |> json_response(:ok)
1686 assert String.starts_with?(media["url"], proxy_url)
1689 test "returns media url when proxy is enabled but media url is whitelisted", %{
1693 media_url = "https://media.pleroma.social"
1694 Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
1696 Pleroma.Config.put([:media_proxy, :enabled], true)
1697 Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
1698 Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
1702 |> post("/api/v1/media", %{"file" => image})
1703 |> json_response(:ok)
1705 assert String.starts_with?(media["url"], media_url)
1709 describe "locked accounts" do
1710 test "/api/v1/follow_requests works" do
1711 user = insert(:user, %{info: %User.Info{locked: true}})
1712 other_user = insert(:user)
1714 {:ok, _activity} = ActivityPub.follow(other_user, user)
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) == false
1723 |> assign(:user, user)
1724 |> get("/api/v1/follow_requests")
1726 assert [relationship] = json_response(conn, 200)
1727 assert to_string(other_user.id) == relationship["id"]
1730 test "/api/v1/follow_requests/:id/authorize works" do
1731 user = insert(:user, %{info: %User.Info{locked: true}})
1732 other_user = insert(:user)
1734 {:ok, _activity} = ActivityPub.follow(other_user, user)
1736 user = User.get_cached_by_id(user.id)
1737 other_user = User.get_cached_by_id(other_user.id)
1739 assert User.following?(other_user, user) == false
1743 |> assign(:user, user)
1744 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1746 assert relationship = json_response(conn, 200)
1747 assert to_string(other_user.id) == relationship["id"]
1749 user = User.get_cached_by_id(user.id)
1750 other_user = User.get_cached_by_id(other_user.id)
1752 assert User.following?(other_user, user) == true
1755 test "verify_credentials", %{conn: conn} do
1756 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1760 |> assign(:user, user)
1761 |> get("/api/v1/accounts/verify_credentials")
1763 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1764 assert id == to_string(user.id)
1767 test "/api/v1/follow_requests/:id/reject works" do
1768 user = insert(:user, %{info: %User.Info{locked: true}})
1769 other_user = insert(:user)
1771 {:ok, _activity} = ActivityPub.follow(other_user, user)
1773 user = User.get_cached_by_id(user.id)
1777 |> assign(:user, user)
1778 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1780 assert relationship = json_response(conn, 200)
1781 assert to_string(other_user.id) == relationship["id"]
1783 user = User.get_cached_by_id(user.id)
1784 other_user = User.get_cached_by_id(other_user.id)
1786 assert User.following?(other_user, user) == false
1790 test "account fetching", %{conn: conn} do
1791 user = insert(:user)
1795 |> get("/api/v1/accounts/#{user.id}")
1797 assert %{"id" => id} = json_response(conn, 200)
1798 assert id == to_string(user.id)
1802 |> get("/api/v1/accounts/-1")
1804 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1807 test "account fetching also works nickname", %{conn: conn} do
1808 user = insert(:user)
1812 |> get("/api/v1/accounts/#{user.nickname}")
1814 assert %{"id" => id} = json_response(conn, 200)
1815 assert id == user.id
1818 test "mascot upload", %{conn: conn} do
1819 user = insert(:user)
1821 non_image_file = %Plug.Upload{
1822 content_type: "audio/mpeg",
1823 path: Path.absname("test/fixtures/sound.mp3"),
1824 filename: "sound.mp3"
1829 |> assign(:user, user)
1830 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1832 assert json_response(conn, 415)
1834 file = %Plug.Upload{
1835 content_type: "image/jpg",
1836 path: Path.absname("test/fixtures/image.jpg"),
1837 filename: "an_image.jpg"
1842 |> assign(:user, user)
1843 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1845 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1848 test "mascot retrieving", %{conn: conn} do
1849 user = insert(:user)
1850 # When user hasn't set a mascot, we should just get pleroma tan back
1853 |> assign(:user, user)
1854 |> get("/api/v1/pleroma/mascot")
1856 assert %{"url" => url} = json_response(conn, 200)
1857 assert url =~ "pleroma-fox-tan-smol"
1859 # When a user sets their mascot, we should get that back
1860 file = %Plug.Upload{
1861 content_type: "image/jpg",
1862 path: Path.absname("test/fixtures/image.jpg"),
1863 filename: "an_image.jpg"
1868 |> assign(:user, user)
1869 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1871 assert json_response(conn, 200)
1873 user = User.get_cached_by_id(user.id)
1877 |> assign(:user, user)
1878 |> get("/api/v1/pleroma/mascot")
1880 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1881 assert url =~ "an_image"
1884 test "hashtag timeline", %{conn: conn} do
1885 following = insert(:user)
1888 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1890 {:ok, [_activity]} =
1891 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1895 |> get("/api/v1/timelines/tag/2hu")
1897 assert [%{"id" => id}] = json_response(nconn, 200)
1899 assert id == to_string(activity.id)
1901 # works for different capitalization too
1904 |> get("/api/v1/timelines/tag/2HU")
1906 assert [%{"id" => id}] = json_response(nconn, 200)
1908 assert id == to_string(activity.id)
1912 test "multi-hashtag timeline", %{conn: conn} do
1913 user = insert(:user)
1915 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1916 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1917 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1921 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1923 [status_none, status_test1, status_test] = json_response(any_test, 200)
1925 assert to_string(activity_test.id) == status_test["id"]
1926 assert to_string(activity_test1.id) == status_test1["id"]
1927 assert to_string(activity_none.id) == status_none["id"]
1931 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1933 assert [status_test1] == json_response(restricted_test, 200)
1935 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1937 assert [status_none] == json_response(all_test, 200)
1940 test "getting followers", %{conn: conn} do
1941 user = insert(:user)
1942 other_user = insert(:user)
1943 {:ok, user} = User.follow(user, other_user)
1947 |> get("/api/v1/accounts/#{other_user.id}/followers")
1949 assert [%{"id" => id}] = json_response(conn, 200)
1950 assert id == to_string(user.id)
1953 test "getting followers, hide_followers", %{conn: conn} do
1954 user = insert(:user)
1955 other_user = insert(:user, %{info: %{hide_followers: true}})
1956 {:ok, _user} = User.follow(user, other_user)
1960 |> get("/api/v1/accounts/#{other_user.id}/followers")
1962 assert [] == json_response(conn, 200)
1965 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1966 user = insert(:user)
1967 other_user = insert(:user, %{info: %{hide_followers: true}})
1968 {:ok, _user} = User.follow(user, other_user)
1972 |> assign(:user, other_user)
1973 |> get("/api/v1/accounts/#{other_user.id}/followers")
1975 refute [] == json_response(conn, 200)
1978 test "getting followers, pagination", %{conn: conn} do
1979 user = insert(:user)
1980 follower1 = insert(:user)
1981 follower2 = insert(:user)
1982 follower3 = insert(:user)
1983 {:ok, _} = User.follow(follower1, user)
1984 {:ok, _} = User.follow(follower2, user)
1985 {:ok, _} = User.follow(follower3, user)
1989 |> assign(:user, user)
1993 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1995 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1996 assert id3 == follower3.id
1997 assert id2 == follower2.id
2001 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
2003 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2004 assert id2 == follower2.id
2005 assert id1 == follower1.id
2009 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
2011 assert [%{"id" => id2}] = json_response(res_conn, 200)
2012 assert id2 == follower2.id
2014 assert [link_header] = get_resp_header(res_conn, "link")
2015 assert link_header =~ ~r/min_id=#{follower2.id}/
2016 assert link_header =~ ~r/max_id=#{follower2.id}/
2019 test "getting following", %{conn: conn} do
2020 user = insert(:user)
2021 other_user = insert(:user)
2022 {:ok, user} = User.follow(user, other_user)
2026 |> get("/api/v1/accounts/#{user.id}/following")
2028 assert [%{"id" => id}] = json_response(conn, 200)
2029 assert id == to_string(other_user.id)
2032 test "getting following, hide_follows", %{conn: conn} do
2033 user = insert(:user, %{info: %{hide_follows: true}})
2034 other_user = insert(:user)
2035 {:ok, user} = User.follow(user, other_user)
2039 |> get("/api/v1/accounts/#{user.id}/following")
2041 assert [] == json_response(conn, 200)
2044 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2045 user = insert(:user, %{info: %{hide_follows: true}})
2046 other_user = insert(:user)
2047 {:ok, user} = User.follow(user, other_user)
2051 |> assign(:user, user)
2052 |> get("/api/v1/accounts/#{user.id}/following")
2054 refute [] == json_response(conn, 200)
2057 test "getting following, pagination", %{conn: conn} do
2058 user = insert(:user)
2059 following1 = insert(:user)
2060 following2 = insert(:user)
2061 following3 = insert(:user)
2062 {:ok, _} = User.follow(user, following1)
2063 {:ok, _} = User.follow(user, following2)
2064 {:ok, _} = User.follow(user, following3)
2068 |> assign(:user, user)
2072 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2074 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2075 assert id3 == following3.id
2076 assert id2 == following2.id
2080 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2082 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2083 assert id2 == following2.id
2084 assert id1 == following1.id
2088 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2090 assert [%{"id" => id2}] = json_response(res_conn, 200)
2091 assert id2 == following2.id
2093 assert [link_header] = get_resp_header(res_conn, "link")
2094 assert link_header =~ ~r/min_id=#{following2.id}/
2095 assert link_header =~ ~r/max_id=#{following2.id}/
2098 test "following / unfollowing a user", %{conn: conn} do
2099 user = insert(:user)
2100 other_user = insert(:user)
2104 |> assign(:user, user)
2105 |> post("/api/v1/accounts/#{other_user.id}/follow")
2107 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2109 user = User.get_cached_by_id(user.id)
2113 |> assign(:user, user)
2114 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2116 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2118 user = User.get_cached_by_id(user.id)
2122 |> assign(:user, user)
2123 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2125 assert %{"id" => id} = json_response(conn, 200)
2126 assert id == to_string(other_user.id)
2129 test "following without reblogs" do
2130 follower = insert(:user)
2131 followed = insert(:user)
2132 other_user = insert(:user)
2136 |> assign(:user, follower)
2137 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2139 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2141 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2142 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2146 |> assign(:user, User.get_cached_by_id(follower.id))
2147 |> get("/api/v1/timelines/home")
2149 assert [] == json_response(conn, 200)
2153 |> assign(:user, follower)
2154 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2156 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2160 |> assign(:user, User.get_cached_by_id(follower.id))
2161 |> get("/api/v1/timelines/home")
2163 expected_activity_id = reblog.id
2164 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2167 test "following / unfollowing errors" do
2168 user = insert(:user)
2172 |> assign(:user, user)
2175 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2176 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2179 user = User.get_cached_by_id(user.id)
2180 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2181 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2183 # self follow via uri
2184 user = User.get_cached_by_id(user.id)
2185 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2186 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2188 # follow non existing user
2189 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2190 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2192 # follow non existing user via uri
2193 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2194 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2196 # unfollow non existing user
2197 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2198 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2201 describe "mute/unmute" do
2202 test "with notifications", %{conn: conn} do
2203 user = insert(:user)
2204 other_user = insert(:user)
2208 |> assign(:user, user)
2209 |> post("/api/v1/accounts/#{other_user.id}/mute")
2211 response = json_response(conn, 200)
2213 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2214 user = User.get_cached_by_id(user.id)
2218 |> assign(:user, user)
2219 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2221 response = json_response(conn, 200)
2222 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2225 test "without notifications", %{conn: conn} do
2226 user = insert(:user)
2227 other_user = insert(:user)
2231 |> assign(:user, user)
2232 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2234 response = json_response(conn, 200)
2236 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2237 user = User.get_cached_by_id(user.id)
2241 |> assign(:user, user)
2242 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2244 response = json_response(conn, 200)
2245 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2249 test "subscribing / unsubscribing to a user", %{conn: conn} do
2250 user = insert(:user)
2251 subscription_target = insert(:user)
2255 |> assign(:user, user)
2256 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2258 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2262 |> assign(:user, user)
2263 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2265 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2268 test "getting a list of mutes", %{conn: conn} do
2269 user = insert(:user)
2270 other_user = insert(:user)
2272 {:ok, user} = User.mute(user, other_user)
2276 |> assign(:user, user)
2277 |> get("/api/v1/mutes")
2279 other_user_id = to_string(other_user.id)
2280 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2283 test "blocking / unblocking a user", %{conn: conn} do
2284 user = insert(:user)
2285 other_user = insert(:user)
2289 |> assign(:user, user)
2290 |> post("/api/v1/accounts/#{other_user.id}/block")
2292 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2294 user = User.get_cached_by_id(user.id)
2298 |> assign(:user, user)
2299 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2301 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2304 test "getting a list of blocks", %{conn: conn} do
2305 user = insert(:user)
2306 other_user = insert(:user)
2308 {:ok, user} = User.block(user, other_user)
2312 |> assign(:user, user)
2313 |> get("/api/v1/blocks")
2315 other_user_id = to_string(other_user.id)
2316 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2319 test "blocking / unblocking a domain", %{conn: conn} do
2320 user = insert(:user)
2321 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2325 |> assign(:user, user)
2326 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2328 assert %{} = json_response(conn, 200)
2329 user = User.get_cached_by_ap_id(user.ap_id)
2330 assert User.blocks?(user, other_user)
2334 |> assign(:user, user)
2335 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2337 assert %{} = json_response(conn, 200)
2338 user = User.get_cached_by_ap_id(user.ap_id)
2339 refute User.blocks?(user, other_user)
2342 test "getting a list of domain blocks", %{conn: conn} do
2343 user = insert(:user)
2345 {:ok, user} = User.block_domain(user, "bad.site")
2346 {:ok, user} = User.block_domain(user, "even.worse.site")
2350 |> assign(:user, user)
2351 |> get("/api/v1/domain_blocks")
2353 domain_blocks = json_response(conn, 200)
2355 assert "bad.site" in domain_blocks
2356 assert "even.worse.site" in domain_blocks
2359 test "unimplemented follow_requests, blocks, domain blocks" do
2360 user = insert(:user)
2362 ["blocks", "domain_blocks", "follow_requests"]
2363 |> Enum.each(fn endpoint ->
2366 |> assign(:user, user)
2367 |> get("/api/v1/#{endpoint}")
2369 assert [] = json_response(conn, 200)
2373 test "returns the favorites of a user", %{conn: conn} do
2374 user = insert(:user)
2375 other_user = insert(:user)
2377 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2378 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2380 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2384 |> assign(:user, user)
2385 |> get("/api/v1/favourites")
2387 assert [status] = json_response(first_conn, 200)
2388 assert status["id"] == to_string(activity.id)
2390 assert [{"link", _link_header}] =
2391 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2393 # Honours query params
2394 {:ok, second_activity} =
2395 CommonAPI.post(other_user, %{
2397 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2400 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2402 last_like = status["id"]
2406 |> assign(:user, user)
2407 |> get("/api/v1/favourites?since_id=#{last_like}")
2409 assert [second_status] = json_response(second_conn, 200)
2410 assert second_status["id"] == to_string(second_activity.id)
2414 |> assign(:user, user)
2415 |> get("/api/v1/favourites?limit=0")
2417 assert [] = json_response(third_conn, 200)
2420 describe "getting favorites timeline of specified user" do
2422 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2423 [current_user: current_user, user: user]
2426 test "returns list of statuses favorited by specified user", %{
2428 current_user: current_user,
2431 [activity | _] = insert_pair(:note_activity)
2432 CommonAPI.favorite(activity.id, user)
2436 |> assign(:user, current_user)
2437 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2438 |> json_response(:ok)
2442 assert length(response) == 1
2443 assert like["id"] == activity.id
2446 test "returns favorites for specified user_id when user is not logged in", %{
2450 activity = insert(:note_activity)
2451 CommonAPI.favorite(activity.id, user)
2455 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2456 |> json_response(:ok)
2458 assert length(response) == 1
2461 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2463 current_user: current_user,
2467 CommonAPI.post(current_user, %{
2468 "status" => "Hi @#{user.nickname}!",
2469 "visibility" => "direct"
2472 CommonAPI.favorite(direct.id, user)
2476 |> assign(:user, current_user)
2477 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2478 |> json_response(:ok)
2480 assert length(response) == 1
2482 anonymous_response =
2484 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2485 |> json_response(:ok)
2487 assert Enum.empty?(anonymous_response)
2490 test "does not return others' favorited DM when user is not one of recipients", %{
2492 current_user: current_user,
2495 user_two = insert(:user)
2498 CommonAPI.post(user_two, %{
2499 "status" => "Hi @#{user.nickname}!",
2500 "visibility" => "direct"
2503 CommonAPI.favorite(direct.id, user)
2507 |> assign(:user, current_user)
2508 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2509 |> json_response(:ok)
2511 assert Enum.empty?(response)
2514 test "paginates favorites using since_id and max_id", %{
2516 current_user: current_user,
2519 activities = insert_list(10, :note_activity)
2521 Enum.each(activities, fn activity ->
2522 CommonAPI.favorite(activity.id, user)
2525 third_activity = Enum.at(activities, 2)
2526 seventh_activity = Enum.at(activities, 6)
2530 |> assign(:user, current_user)
2531 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2532 since_id: third_activity.id,
2533 max_id: seventh_activity.id
2535 |> json_response(:ok)
2537 assert length(response) == 3
2538 refute third_activity in response
2539 refute seventh_activity in response
2542 test "limits favorites using limit parameter", %{
2544 current_user: current_user,
2548 |> insert_list(:note_activity)
2549 |> Enum.each(fn activity ->
2550 CommonAPI.favorite(activity.id, user)
2555 |> assign(:user, current_user)
2556 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2557 |> json_response(:ok)
2559 assert length(response) == 3
2562 test "returns empty response when user does not have any favorited statuses", %{
2564 current_user: current_user,
2569 |> assign(:user, current_user)
2570 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2571 |> json_response(:ok)
2573 assert Enum.empty?(response)
2576 test "returns 404 error when specified user is not exist", %{conn: conn} do
2577 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2579 assert json_response(conn, 404) == %{"error" => "Record not found"}
2582 test "returns 403 error when user has hidden own favorites", %{
2584 current_user: current_user
2586 user = insert(:user, %{info: %{hide_favorites: true}})
2587 activity = insert(:note_activity)
2588 CommonAPI.favorite(activity.id, user)
2592 |> assign(:user, current_user)
2593 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2595 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2598 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2599 user = insert(:user)
2600 activity = insert(:note_activity)
2601 CommonAPI.favorite(activity.id, user)
2605 |> assign(:user, current_user)
2606 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2608 assert user.info.hide_favorites
2609 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2613 test "get instance information", %{conn: conn} do
2614 conn = get(conn, "/api/v1/instance")
2615 assert result = json_response(conn, 200)
2617 email = Pleroma.Config.get([:instance, :email])
2618 # Note: not checking for "max_toot_chars" since it's optional
2624 "email" => from_config_email,
2626 "streaming_api" => _
2631 "registrations" => _,
2635 assert email == from_config_email
2638 test "get instance stats", %{conn: conn} do
2639 user = insert(:user, %{local: true})
2641 user2 = insert(:user, %{local: true})
2642 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2644 insert(:user, %{local: false, nickname: "u@peer1.com"})
2645 insert(:user, %{local: false, nickname: "u@peer2.com"})
2647 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2649 # Stats should count users with missing or nil `info.deactivated` value
2650 user = User.get_cached_by_id(user.id)
2651 info_change = Changeset.change(user.info, %{deactivated: nil})
2655 |> Changeset.change()
2656 |> Changeset.put_embed(:info, info_change)
2657 |> User.update_and_set_cache()
2659 Pleroma.Stats.update_stats()
2661 conn = get(conn, "/api/v1/instance")
2663 assert result = json_response(conn, 200)
2665 stats = result["stats"]
2668 assert stats["user_count"] == 1
2669 assert stats["status_count"] == 1
2670 assert stats["domain_count"] == 2
2673 test "get peers", %{conn: conn} do
2674 insert(:user, %{local: false, nickname: "u@peer1.com"})
2675 insert(:user, %{local: false, nickname: "u@peer2.com"})
2677 Pleroma.Stats.update_stats()
2679 conn = get(conn, "/api/v1/instance/peers")
2681 assert result = json_response(conn, 200)
2683 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2686 test "put settings", %{conn: conn} do
2687 user = insert(:user)
2691 |> assign(:user, user)
2692 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2694 assert _result = json_response(conn, 200)
2696 user = User.get_cached_by_ap_id(user.ap_id)
2697 assert user.info.settings == %{"programming" => "socks"}
2700 describe "pinned statuses" do
2702 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2704 user = insert(:user)
2705 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2707 [user: user, activity: activity]
2710 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2711 {:ok, _} = CommonAPI.pin(activity.id, user)
2715 |> assign(:user, user)
2716 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2717 |> json_response(200)
2719 id_str = to_string(activity.id)
2721 assert [%{"id" => ^id_str, "pinned" => true}] = result
2724 test "pin status", %{conn: conn, user: user, activity: activity} do
2725 id_str = to_string(activity.id)
2727 assert %{"id" => ^id_str, "pinned" => true} =
2729 |> assign(:user, user)
2730 |> post("/api/v1/statuses/#{activity.id}/pin")
2731 |> json_response(200)
2733 assert [%{"id" => ^id_str, "pinned" => true}] =
2735 |> assign(:user, user)
2736 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2737 |> json_response(200)
2740 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2741 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2745 |> assign(:user, user)
2746 |> post("/api/v1/statuses/#{dm.id}/pin")
2748 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2751 test "unpin status", %{conn: conn, user: user, activity: activity} do
2752 {:ok, _} = CommonAPI.pin(activity.id, user)
2754 id_str = to_string(activity.id)
2755 user = refresh_record(user)
2757 assert %{"id" => ^id_str, "pinned" => false} =
2759 |> assign(:user, user)
2760 |> post("/api/v1/statuses/#{activity.id}/unpin")
2761 |> json_response(200)
2765 |> assign(:user, user)
2766 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2767 |> json_response(200)
2770 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2773 |> assign(:user, user)
2774 |> post("/api/v1/statuses/1/unpin")
2776 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2779 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2780 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2782 id_str_one = to_string(activity_one.id)
2784 assert %{"id" => ^id_str_one, "pinned" => true} =
2786 |> assign(:user, user)
2787 |> post("/api/v1/statuses/#{id_str_one}/pin")
2788 |> json_response(200)
2790 user = refresh_record(user)
2792 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2794 |> assign(:user, user)
2795 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2796 |> json_response(400)
2802 Pleroma.Config.put([:rich_media, :enabled], true)
2805 Pleroma.Config.put([:rich_media, :enabled], false)
2808 user = insert(:user)
2812 test "returns rich-media card", %{conn: conn, user: user} do
2813 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2816 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2817 "provider_name" => "www.imdb.com",
2818 "provider_url" => "http://www.imdb.com",
2819 "title" => "The Rock",
2821 "url" => "http://www.imdb.com/title/tt0117500/",
2823 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2826 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2827 "title" => "The Rock",
2828 "type" => "video.movie",
2829 "url" => "http://www.imdb.com/title/tt0117500/",
2831 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2838 |> get("/api/v1/statuses/#{activity.id}/card")
2839 |> json_response(200)
2841 assert response == card_data
2843 # works with private posts
2845 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2849 |> assign(:user, user)
2850 |> get("/api/v1/statuses/#{activity.id}/card")
2851 |> json_response(200)
2853 assert response_two == card_data
2856 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2858 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2862 |> get("/api/v1/statuses/#{activity.id}/card")
2863 |> json_response(:ok)
2865 assert response == %{
2867 "title" => "Pleroma",
2868 "description" => "",
2870 "provider_name" => "pleroma.social",
2871 "provider_url" => "https://pleroma.social",
2872 "url" => "https://pleroma.social/",
2875 "title" => "Pleroma",
2876 "type" => "website",
2877 "url" => "https://pleroma.social/"
2885 user = insert(:user)
2886 for_user = insert(:user)
2889 CommonAPI.post(user, %{
2890 "status" => "heweoo?"
2894 CommonAPI.post(user, %{
2895 "status" => "heweoo!"
2900 |> assign(:user, for_user)
2901 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2903 assert json_response(response1, 200)["bookmarked"] == true
2907 |> assign(:user, for_user)
2908 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2910 assert json_response(response2, 200)["bookmarked"] == true
2914 |> assign(:user, for_user)
2915 |> get("/api/v1/bookmarks")
2917 assert [json_response(response2, 200), json_response(response1, 200)] ==
2918 json_response(bookmarks, 200)
2922 |> assign(:user, for_user)
2923 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2925 assert json_response(response1, 200)["bookmarked"] == false
2929 |> assign(:user, for_user)
2930 |> get("/api/v1/bookmarks")
2932 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2935 describe "conversation muting" do
2937 user = insert(:user)
2938 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2940 [user: user, activity: activity]
2943 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2944 id_str = to_string(activity.id)
2946 assert %{"id" => ^id_str, "muted" => true} =
2948 |> assign(:user, user)
2949 |> post("/api/v1/statuses/#{activity.id}/mute")
2950 |> json_response(200)
2953 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2954 {:ok, _} = CommonAPI.add_mute(user, activity)
2958 |> assign(:user, user)
2959 |> post("/api/v1/statuses/#{activity.id}/mute")
2961 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2964 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2965 {:ok, _} = CommonAPI.add_mute(user, activity)
2967 id_str = to_string(activity.id)
2968 user = refresh_record(user)
2970 assert %{"id" => ^id_str, "muted" => false} =
2972 |> assign(:user, user)
2973 |> post("/api/v1/statuses/#{activity.id}/unmute")
2974 |> json_response(200)
2978 describe "reports" do
2980 reporter = insert(:user)
2981 target_user = insert(:user)
2983 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2985 [reporter: reporter, target_user: target_user, activity: activity]
2988 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2989 assert %{"action_taken" => false, "id" => _} =
2991 |> assign(:user, reporter)
2992 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2993 |> json_response(200)
2996 test "submit a report with statuses and comment", %{
2999 target_user: target_user,
3002 assert %{"action_taken" => false, "id" => _} =
3004 |> assign(:user, reporter)
3005 |> post("/api/v1/reports", %{
3006 "account_id" => target_user.id,
3007 "status_ids" => [activity.id],
3008 "comment" => "bad status!",
3009 "forward" => "false"
3011 |> json_response(200)
3014 test "account_id is required", %{
3019 assert %{"error" => "Valid `account_id` required"} =
3021 |> assign(:user, reporter)
3022 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3023 |> json_response(400)
3026 test "comment must be up to the size specified in the config", %{
3029 target_user: target_user
3031 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
3032 comment = String.pad_trailing("a", max_size + 1, "a")
3034 error = %{"error" => "Comment must be up to #{max_size} characters"}
3038 |> assign(:user, reporter)
3039 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3040 |> json_response(400)
3043 test "returns error when account is not exist", %{
3050 |> assign(:user, reporter)
3051 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3053 assert json_response(conn, 400) == %{"error" => "Account not found"}
3057 describe "link headers" do
3058 test "preserves parameters in link headers", %{conn: conn} do
3059 user = insert(:user)
3060 other_user = insert(:user)
3063 CommonAPI.post(other_user, %{
3064 "status" => "hi @#{user.nickname}",
3065 "visibility" => "public"
3069 CommonAPI.post(other_user, %{
3070 "status" => "hi @#{user.nickname}",
3071 "visibility" => "public"
3074 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3075 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3079 |> assign(:user, user)
3080 |> get("/api/v1/notifications", %{media_only: true})
3082 assert [link_header] = get_resp_header(conn, "link")
3083 assert link_header =~ ~r/media_only=true/
3084 assert link_header =~ ~r/min_id=#{notification2.id}/
3085 assert link_header =~ ~r/max_id=#{notification1.id}/
3089 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3090 # Need to set an old-style integer ID to reproduce the problem
3091 # (these are no longer assigned to new accounts but were preserved
3092 # for existing accounts during the migration to flakeIDs)
3093 user_one = insert(:user, %{id: 1212})
3094 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3098 |> get("/api/v1/accounts/#{user_one.id}")
3102 |> get("/api/v1/accounts/#{user_two.nickname}")
3106 |> get("/api/v1/accounts/#{user_two.id}")
3108 acc_one = json_response(resp_one, 200)
3109 acc_two = json_response(resp_two, 200)
3110 acc_three = json_response(resp_three, 200)
3111 refute acc_one == acc_two
3112 assert acc_two == acc_three
3115 describe "custom emoji" do
3116 test "with tags", %{conn: conn} do
3119 |> get("/api/v1/custom_emojis")
3120 |> json_response(200)
3122 assert Map.has_key?(emoji, "shortcode")
3123 assert Map.has_key?(emoji, "static_url")
3124 assert Map.has_key?(emoji, "tags")
3125 assert is_list(emoji["tags"])
3126 assert Map.has_key?(emoji, "category")
3127 assert Map.has_key?(emoji, "url")
3128 assert Map.has_key?(emoji, "visible_in_picker")
3132 describe "index/2 redirections" do
3133 setup %{conn: conn} do
3137 signing_salt: "cooldude"
3142 |> Plug.Session.call(Plug.Session.init(session_opts))
3145 test_path = "/web/statuses/test"
3146 %{conn: conn, path: test_path}
3149 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3150 conn = get(conn, path)
3152 assert conn.status == 302
3153 assert redirected_to(conn) == "/web/login"
3156 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3157 token = insert(:oauth_token)
3161 |> assign(:user, token.user)
3162 |> put_session(:oauth_token, token.token)
3165 assert conn.status == 200
3168 test "saves referer path to session", %{conn: conn, path: path} do
3169 conn = get(conn, path)
3170 return_to = Plug.Conn.get_session(conn, :return_to)
3172 assert return_to == path
3175 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3176 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3177 auth = insert(:oauth_authorization, app: app)
3181 |> put_session(:return_to, path)
3182 |> get("/web/login", %{code: auth.token})
3184 assert conn.status == 302
3185 assert redirected_to(conn) == path
3188 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3189 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3190 auth = insert(:oauth_authorization, app: app)
3192 conn = get(conn, "/web/login", %{code: auth.token})
3194 assert conn.status == 302
3195 assert redirected_to(conn) == "/web/getting-started"
3199 describe "scheduled activities" do
3200 test "creates a scheduled activity", %{conn: conn} do
3201 user = insert(:user)
3202 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3206 |> assign(:user, user)
3207 |> post("/api/v1/statuses", %{
3208 "status" => "scheduled",
3209 "scheduled_at" => scheduled_at
3212 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3213 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3214 assert [] == Repo.all(Activity)
3217 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3218 user = insert(:user)
3219 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3221 file = %Plug.Upload{
3222 content_type: "image/jpg",
3223 path: Path.absname("test/fixtures/image.jpg"),
3224 filename: "an_image.jpg"
3227 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3231 |> assign(:user, user)
3232 |> post("/api/v1/statuses", %{
3233 "media_ids" => [to_string(upload.id)],
3234 "status" => "scheduled",
3235 "scheduled_at" => scheduled_at
3238 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3239 assert %{"type" => "image"} = media_attachment
3242 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3244 user = insert(:user)
3247 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3251 |> assign(:user, user)
3252 |> post("/api/v1/statuses", %{
3253 "status" => "not scheduled",
3254 "scheduled_at" => scheduled_at
3257 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3258 assert [] == Repo.all(ScheduledActivity)
3261 test "returns error when daily user limit is exceeded", %{conn: conn} do
3262 user = insert(:user)
3265 NaiveDateTime.utc_now()
3266 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3267 |> NaiveDateTime.to_iso8601()
3269 attrs = %{params: %{}, scheduled_at: today}
3270 {:ok, _} = ScheduledActivity.create(user, attrs)
3271 {:ok, _} = ScheduledActivity.create(user, attrs)
3275 |> assign(:user, user)
3276 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3278 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3281 test "returns error when total user limit is exceeded", %{conn: conn} do
3282 user = insert(:user)
3285 NaiveDateTime.utc_now()
3286 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3287 |> NaiveDateTime.to_iso8601()
3290 NaiveDateTime.utc_now()
3291 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3292 |> NaiveDateTime.to_iso8601()
3294 attrs = %{params: %{}, scheduled_at: today}
3295 {:ok, _} = ScheduledActivity.create(user, attrs)
3296 {:ok, _} = ScheduledActivity.create(user, attrs)
3297 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3301 |> assign(:user, user)
3302 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3304 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3307 test "shows scheduled activities", %{conn: conn} do
3308 user = insert(:user)
3309 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3310 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3311 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3312 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3316 |> assign(:user, user)
3321 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3323 result = json_response(conn_res, 200)
3324 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3329 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3331 result = json_response(conn_res, 200)
3332 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3337 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3339 result = json_response(conn_res, 200)
3340 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3343 test "shows a scheduled activity", %{conn: conn} do
3344 user = insert(:user)
3345 scheduled_activity = insert(:scheduled_activity, user: user)
3349 |> assign(:user, user)
3350 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3352 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3353 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3357 |> assign(:user, user)
3358 |> get("/api/v1/scheduled_statuses/404")
3360 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3363 test "updates a scheduled activity", %{conn: conn} do
3364 user = insert(:user)
3365 scheduled_activity = insert(:scheduled_activity, user: user)
3368 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3372 |> assign(:user, user)
3373 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3374 scheduled_at: new_scheduled_at
3377 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3378 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3382 |> assign(:user, user)
3383 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3385 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3388 test "deletes a scheduled activity", %{conn: conn} do
3389 user = insert(:user)
3390 scheduled_activity = insert(:scheduled_activity, user: user)
3394 |> assign(:user, user)
3395 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3397 assert %{} = json_response(res_conn, 200)
3398 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3402 |> assign(:user, user)
3403 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3405 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3409 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3410 user1 = insert(:user)
3411 user2 = insert(:user)
3412 user3 = insert(:user)
3414 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3416 # Reply to status from another user
3419 |> assign(:user, user2)
3420 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3422 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3424 activity = Activity.get_by_id_with_object(id)
3426 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3427 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3429 # Reblog from the third user
3432 |> assign(:user, user3)
3433 |> post("/api/v1/statuses/#{activity.id}/reblog")
3435 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3436 json_response(conn2, 200)
3438 assert to_string(activity.id) == id
3440 # Getting third user status
3443 |> assign(:user, user3)
3444 |> get("api/v1/timelines/home")
3446 [reblogged_activity] = json_response(conn3, 200)
3448 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3450 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3451 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3454 describe "create account by app" do
3455 test "Account registration via Application", %{conn: conn} do
3458 |> post("/api/v1/apps", %{
3459 client_name: "client_name",
3460 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3461 scopes: "read, write, follow"
3465 "client_id" => client_id,
3466 "client_secret" => client_secret,
3468 "name" => "client_name",
3469 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3472 } = json_response(conn, 200)
3476 |> post("/oauth/token", %{
3477 grant_type: "client_credentials",
3478 client_id: client_id,
3479 client_secret: client_secret
3482 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3483 json_response(conn, 200)
3486 token_from_db = Repo.get_by(Token, token: token)
3487 assert token_from_db
3489 assert scope == "read write follow"
3493 |> put_req_header("authorization", "Bearer " <> token)
3494 |> post("/api/v1/accounts", %{
3496 email: "lain@example.org",
3497 password: "PlzDontHackLain",
3502 "access_token" => token,
3503 "created_at" => _created_at,
3505 "token_type" => "Bearer"
3506 } = json_response(conn, 200)
3508 token_from_db = Repo.get_by(Token, token: token)
3509 assert token_from_db
3510 token_from_db = Repo.preload(token_from_db, :user)
3511 assert token_from_db.user
3513 assert token_from_db.user.info.confirmation_pending
3516 test "rate limit", %{conn: conn} do
3517 app_token = insert(:oauth_token, user: nil)
3520 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3521 |> Map.put(:remote_ip, {15, 15, 15, 15})
3526 |> post("/api/v1/accounts", %{
3527 username: "#{i}lain",
3528 email: "#{i}lain@example.org",
3529 password: "PlzDontHackLain",
3534 "access_token" => token,
3535 "created_at" => _created_at,
3537 "token_type" => "Bearer"
3538 } = json_response(conn, 200)
3540 token_from_db = Repo.get_by(Token, token: token)
3541 assert token_from_db
3542 token_from_db = Repo.preload(token_from_db, :user)
3543 assert token_from_db.user
3545 assert token_from_db.user.info.confirmation_pending
3550 |> post("/api/v1/accounts", %{
3552 email: "6lain@example.org",
3553 password: "PlzDontHackLain",
3557 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3561 describe "GET /api/v1/polls/:id" do
3562 test "returns poll entity for object id", %{conn: conn} do
3563 user = insert(:user)
3566 CommonAPI.post(user, %{
3567 "status" => "Pleroma does",
3568 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3571 object = Object.normalize(activity)
3575 |> assign(:user, user)
3576 |> get("/api/v1/polls/#{object.id}")
3578 response = json_response(conn, 200)
3579 id = to_string(object.id)
3580 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3583 test "does not expose polls for private statuses", %{conn: conn} do
3584 user = insert(:user)
3585 other_user = insert(:user)
3588 CommonAPI.post(user, %{
3589 "status" => "Pleroma does",
3590 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3591 "visibility" => "private"
3594 object = Object.normalize(activity)
3598 |> assign(:user, other_user)
3599 |> get("/api/v1/polls/#{object.id}")
3601 assert json_response(conn, 404)
3605 describe "POST /api/v1/polls/:id/votes" do
3606 test "votes are added to the poll", %{conn: conn} do
3607 user = insert(:user)
3608 other_user = insert(:user)
3611 CommonAPI.post(user, %{
3612 "status" => "A very delicious sandwich",
3614 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3620 object = Object.normalize(activity)
3624 |> assign(:user, other_user)
3625 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3627 assert json_response(conn, 200)
3628 object = Object.get_by_id(object.id)
3630 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3635 test "author can't vote", %{conn: conn} do
3636 user = insert(:user)
3639 CommonAPI.post(user, %{
3640 "status" => "Am I cute?",
3641 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3644 object = Object.normalize(activity)
3647 |> assign(:user, user)
3648 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3649 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3651 object = Object.get_by_id(object.id)
3653 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3656 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3657 user = insert(:user)
3658 other_user = insert(:user)
3661 CommonAPI.post(user, %{
3662 "status" => "The glass is",
3663 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3666 object = Object.normalize(activity)
3669 |> assign(:user, other_user)
3670 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3671 |> json_response(422) == %{"error" => "Too many choices"}
3673 object = Object.get_by_id(object.id)
3675 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3680 test "does not allow choice index to be greater than options count", %{conn: conn} do
3681 user = insert(:user)
3682 other_user = insert(:user)
3685 CommonAPI.post(user, %{
3686 "status" => "Am I cute?",
3687 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3690 object = Object.normalize(activity)
3694 |> assign(:user, other_user)
3695 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3697 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3700 test "returns 404 error when object is not exist", %{conn: conn} do
3701 user = insert(:user)
3705 |> assign(:user, user)
3706 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3708 assert json_response(conn, 404) == %{"error" => "Record not found"}
3711 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3712 user = insert(:user)
3713 other_user = insert(:user)
3716 CommonAPI.post(user, %{
3717 "status" => "Am I cute?",
3718 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3719 "visibility" => "private"
3722 object = Object.normalize(activity)
3726 |> assign(:user, other_user)
3727 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3729 assert json_response(conn, 404) == %{"error" => "Record not found"}
3733 describe "GET /api/v1/statuses/:id/favourited_by" do
3735 user = insert(:user)
3736 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3740 |> assign(:user, user)
3742 [conn: conn, activity: activity]
3745 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3746 other_user = insert(:user)
3747 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3751 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3752 |> json_response(:ok)
3754 [%{"id" => id}] = response
3756 assert id == other_user.id
3759 test "returns empty array when status has not been favorited yet", %{
3765 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3766 |> json_response(:ok)
3768 assert Enum.empty?(response)
3772 describe "GET /api/v1/statuses/:id/reblogged_by" do
3774 user = insert(:user)
3775 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3779 |> assign(:user, user)
3781 [conn: conn, activity: activity]
3784 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3785 other_user = insert(:user)
3786 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3790 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3791 |> json_response(:ok)
3793 [%{"id" => id}] = response
3795 assert id == other_user.id
3798 test "returns empty array when status has not been reblogged yet", %{
3804 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3805 |> json_response(:ok)
3807 assert Enum.empty?(response)