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.ActivityExpiration
12 alias Pleroma.Notification
15 alias Pleroma.ScheduledActivity
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.CommonAPI
19 alias Pleroma.Web.MastodonAPI.FilterView
20 alias Pleroma.Web.OAuth.App
21 alias Pleroma.Web.OAuth.Token
22 alias Pleroma.Web.OStatus
23 alias Pleroma.Web.Push
24 import Pleroma.Factory
25 import ExUnit.CaptureLog
27 import Swoosh.TestAssertions
29 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
32 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
36 clear_config([:instance, :public])
37 clear_config([:rich_media, :enabled])
39 test "the home timeline", %{conn: conn} do
41 following = insert(:user)
43 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
47 |> assign(:user, user)
48 |> get("/api/v1/timelines/home")
50 assert Enum.empty?(json_response(conn, 200))
52 {:ok, user} = User.follow(user, following)
56 |> assign(:user, user)
57 |> get("/api/v1/timelines/home")
59 assert [%{"content" => "test"}] = json_response(conn, 200)
62 test "the public timeline", %{conn: conn} do
63 following = insert(:user)
66 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
69 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
73 |> get("/api/v1/timelines/public", %{"local" => "False"})
75 assert length(json_response(conn, 200)) == 2
79 |> get("/api/v1/timelines/public", %{"local" => "True"})
81 assert [%{"content" => "test"}] = json_response(conn, 200)
85 |> get("/api/v1/timelines/public", %{"local" => "1"})
87 assert [%{"content" => "test"}] = json_response(conn, 200)
91 test "the public timeline when public is set to false", %{conn: conn} do
92 Config.put([:instance, :public], false)
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
154 # An activity that will expire:
156 expires_in = 120 * 60
160 |> post("api/v1/statuses", %{
161 "status" => "oolong",
162 "expires_in" => expires_in
165 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
166 assert activity = Activity.get_by_id(fourth_id)
167 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
169 estimated_expires_at =
170 NaiveDateTime.utc_now()
171 |> NaiveDateTime.add(expires_in)
172 |> NaiveDateTime.truncate(:second)
174 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
175 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
177 assert fourth_response["pleroma"]["expires_at"] ==
178 NaiveDateTime.to_iso8601(expiration.scheduled_at)
181 test "replying to a status", %{conn: conn} do
183 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
187 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
189 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
191 activity = Activity.get_by_id(id)
193 assert activity.data["context"] == replied_to.data["context"]
194 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
197 test "replying to a direct message with visibility other than direct", %{conn: conn} do
199 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
201 Enum.each(["public", "private", "unlisted"], fn visibility ->
204 |> post("/api/v1/statuses", %{
205 "status" => "@#{user.nickname} hey",
206 "in_reply_to_id" => replied_to.id,
207 "visibility" => visibility
210 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
214 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
217 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
219 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
220 assert Activity.get_by_id(id)
223 test "posting a sensitive status", %{conn: conn} do
226 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
228 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
229 assert Activity.get_by_id(id)
232 test "posting a fake status", %{conn: conn} do
235 |> post("/api/v1/statuses", %{
237 "\"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"
240 real_status = json_response(real_conn, 200)
243 assert Object.get_by_ap_id(real_status["uri"])
247 |> Map.put("id", nil)
248 |> Map.put("url", nil)
249 |> Map.put("uri", nil)
250 |> Map.put("created_at", nil)
251 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
255 |> post("/api/v1/statuses", %{
257 "\"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",
261 fake_status = json_response(fake_conn, 200)
264 refute Object.get_by_ap_id(fake_status["uri"])
268 |> Map.put("id", nil)
269 |> Map.put("url", nil)
270 |> Map.put("uri", nil)
271 |> Map.put("created_at", nil)
272 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
274 assert real_status == fake_status
277 test "posting a status with OGP link preview", %{conn: conn} do
278 Config.put([:rich_media, :enabled], true)
282 |> post("/api/v1/statuses", %{
283 "status" => "https://example.com/ogp"
286 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
287 assert Activity.get_by_id(id)
290 test "posting a direct status", %{conn: conn} do
291 user2 = insert(:user)
292 content = "direct cofe @#{user2.nickname}"
296 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
298 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
299 assert activity = Activity.get_by_id(id)
300 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
301 assert activity.data["to"] == [user2.ap_id]
302 assert activity.data["cc"] == []
306 describe "posting polls" do
307 test "posting a poll", %{conn: conn} do
309 time = NaiveDateTime.utc_now()
313 |> assign(:user, user)
314 |> post("/api/v1/statuses", %{
315 "status" => "Who is the #bestgrill?",
316 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
319 response = json_response(conn, 200)
321 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
322 title in ["Rei", "Asuka", "Misato"]
325 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
326 refute response["poll"]["expred"]
329 test "option limit is enforced", %{conn: conn} do
331 limit = Config.get([:instance, :poll_limits, :max_options])
335 |> assign(:user, user)
336 |> post("/api/v1/statuses", %{
338 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
341 %{"error" => error} = json_response(conn, 422)
342 assert error == "Poll can't contain more than #{limit} options"
345 test "option character limit is enforced", %{conn: conn} do
347 limit = Config.get([:instance, :poll_limits, :max_option_chars])
351 |> assign(:user, user)
352 |> post("/api/v1/statuses", %{
355 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
360 %{"error" => error} = json_response(conn, 422)
361 assert error == "Poll options cannot be longer than #{limit} characters each"
364 test "minimal date limit is enforced", %{conn: conn} do
366 limit = Config.get([:instance, :poll_limits, :min_expiration])
370 |> assign(:user, user)
371 |> post("/api/v1/statuses", %{
372 "status" => "imagine arbitrary limits",
374 "options" => ["this post was made by pleroma gang"],
375 "expires_in" => limit - 1
379 %{"error" => error} = json_response(conn, 422)
380 assert error == "Expiration date is too soon"
383 test "maximum date limit is enforced", %{conn: conn} do
385 limit = Config.get([:instance, :poll_limits, :max_expiration])
389 |> assign(:user, user)
390 |> post("/api/v1/statuses", %{
391 "status" => "imagine arbitrary limits",
393 "options" => ["this post was made by pleroma gang"],
394 "expires_in" => limit + 1
398 %{"error" => error} = json_response(conn, 422)
399 assert error == "Expiration date is too far in the future"
403 test "direct timeline", %{conn: conn} do
404 user_one = insert(:user)
405 user_two = insert(:user)
407 {:ok, user_two} = User.follow(user_two, user_one)
410 CommonAPI.post(user_one, %{
411 "status" => "Hi @#{user_two.nickname}!",
412 "visibility" => "direct"
415 {:ok, _follower_only} =
416 CommonAPI.post(user_one, %{
417 "status" => "Hi @#{user_two.nickname}!",
418 "visibility" => "private"
421 # Only direct should be visible here
424 |> assign(:user, user_two)
425 |> get("api/v1/timelines/direct")
427 [status] = json_response(res_conn, 200)
429 assert %{"visibility" => "direct"} = status
430 assert status["url"] != direct.data["id"]
432 # User should be able to see their own direct message
435 |> assign(:user, user_one)
436 |> get("api/v1/timelines/direct")
438 [status] = json_response(res_conn, 200)
440 assert %{"visibility" => "direct"} = status
442 # Both should be visible here
445 |> assign(:user, user_two)
446 |> get("api/v1/timelines/home")
448 [_s1, _s2] = json_response(res_conn, 200)
451 Enum.each(1..20, fn _ ->
453 CommonAPI.post(user_one, %{
454 "status" => "Hi @#{user_two.nickname}!",
455 "visibility" => "direct"
461 |> assign(:user, user_two)
462 |> get("api/v1/timelines/direct")
464 statuses = json_response(res_conn, 200)
465 assert length(statuses) == 20
469 |> assign(:user, user_two)
470 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
472 [status] = json_response(res_conn, 200)
474 assert status["url"] != direct.data["id"]
477 test "Conversations", %{conn: conn} do
478 user_one = insert(:user)
479 user_two = insert(:user)
480 user_three = insert(:user)
482 {:ok, user_two} = User.follow(user_two, user_one)
485 CommonAPI.post(user_one, %{
486 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
487 "visibility" => "direct"
490 {:ok, _follower_only} =
491 CommonAPI.post(user_one, %{
492 "status" => "Hi @#{user_two.nickname}!",
493 "visibility" => "private"
498 |> assign(:user, user_one)
499 |> get("/api/v1/conversations")
501 assert response = json_response(res_conn, 200)
506 "accounts" => res_accounts,
507 "last_status" => res_last_status,
512 account_ids = Enum.map(res_accounts, & &1["id"])
513 assert length(res_accounts) == 2
514 assert user_two.id in account_ids
515 assert user_three.id in account_ids
516 assert is_binary(res_id)
517 assert unread == true
518 assert res_last_status["id"] == direct.id
520 # Apparently undocumented API endpoint
523 |> assign(:user, user_one)
524 |> post("/api/v1/conversations/#{res_id}/read")
526 assert response = json_response(res_conn, 200)
527 assert length(response["accounts"]) == 2
528 assert response["last_status"]["id"] == direct.id
529 assert response["unread"] == false
531 # (vanilla) Mastodon frontend behaviour
534 |> assign(:user, user_one)
535 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
537 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
540 test "doesn't include DMs from blocked users", %{conn: conn} do
541 blocker = insert(:user)
542 blocked = insert(:user)
544 {:ok, blocker} = User.block(blocker, blocked)
546 {:ok, _blocked_direct} =
547 CommonAPI.post(blocked, %{
548 "status" => "Hi @#{blocker.nickname}!",
549 "visibility" => "direct"
553 CommonAPI.post(user, %{
554 "status" => "Hi @#{blocker.nickname}!",
555 "visibility" => "direct"
560 |> assign(:user, user)
561 |> get("api/v1/timelines/direct")
563 [status] = json_response(res_conn, 200)
564 assert status["id"] == direct.id
567 test "verify_credentials", %{conn: conn} do
572 |> assign(:user, user)
573 |> get("/api/v1/accounts/verify_credentials")
575 response = json_response(conn, 200)
577 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
578 assert response["pleroma"]["chat_token"]
579 assert id == to_string(user.id)
582 test "verify_credentials default scope unlisted", %{conn: conn} do
583 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
587 |> assign(:user, user)
588 |> get("/api/v1/accounts/verify_credentials")
590 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
591 assert id == to_string(user.id)
594 test "apps/verify_credentials", %{conn: conn} do
595 token = insert(:oauth_token)
599 |> assign(:user, token.user)
600 |> assign(:token, token)
601 |> get("/api/v1/apps/verify_credentials")
603 app = Repo.preload(token, :app).app
606 "name" => app.client_name,
607 "website" => app.website,
608 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
611 assert expected == json_response(conn, 200)
614 test "user avatar can be set", %{conn: conn} do
616 avatar_image = File.read!("test/fixtures/avatar_data_uri")
620 |> assign(:user, user)
621 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
623 user = refresh_record(user)
637 assert %{"url" => _} = json_response(conn, 200)
640 test "user avatar can be reset", %{conn: conn} do
645 |> assign(:user, user)
646 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
648 user = User.get_cached_by_id(user.id)
650 assert user.avatar == nil
652 assert %{"url" => nil} = json_response(conn, 200)
655 test "can set profile banner", %{conn: conn} do
660 |> assign(:user, user)
661 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
663 user = refresh_record(user)
664 assert user.info.banner["type"] == "Image"
666 assert %{"url" => _} = json_response(conn, 200)
669 test "can reset profile banner", %{conn: conn} do
674 |> assign(:user, user)
675 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
677 user = refresh_record(user)
678 assert user.info.banner == %{}
680 assert %{"url" => nil} = json_response(conn, 200)
683 test "background image can be set", %{conn: conn} do
688 |> assign(:user, user)
689 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
691 user = refresh_record(user)
692 assert user.info.background["type"] == "Image"
693 assert %{"url" => _} = json_response(conn, 200)
696 test "background image can be reset", %{conn: conn} do
701 |> assign(:user, user)
702 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
704 user = refresh_record(user)
705 assert user.info.background == %{}
706 assert %{"url" => nil} = json_response(conn, 200)
709 test "creates an oauth app", %{conn: conn} do
711 app_attrs = build(:oauth_app)
715 |> assign(:user, user)
716 |> post("/api/v1/apps", %{
717 client_name: app_attrs.client_name,
718 redirect_uris: app_attrs.redirect_uris
721 [app] = Repo.all(App)
724 "name" => app.client_name,
725 "website" => app.website,
726 "client_id" => app.client_id,
727 "client_secret" => app.client_secret,
728 "id" => app.id |> to_string(),
729 "redirect_uri" => app.redirect_uris,
730 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
733 assert expected == json_response(conn, 200)
736 test "get a status", %{conn: conn} do
737 activity = insert(:note_activity)
741 |> get("/api/v1/statuses/#{activity.id}")
743 assert %{"id" => id} = json_response(conn, 200)
744 assert id == to_string(activity.id)
747 describe "deleting a status" do
748 test "when you created it", %{conn: conn} do
749 activity = insert(:note_activity)
750 author = User.get_cached_by_ap_id(activity.data["actor"])
754 |> assign(:user, author)
755 |> delete("/api/v1/statuses/#{activity.id}")
757 assert %{} = json_response(conn, 200)
759 refute Activity.get_by_id(activity.id)
762 test "when you didn't create it", %{conn: conn} do
763 activity = insert(:note_activity)
768 |> assign(:user, user)
769 |> delete("/api/v1/statuses/#{activity.id}")
771 assert %{"error" => _} = json_response(conn, 403)
773 assert Activity.get_by_id(activity.id) == activity
776 test "when you're an admin or moderator", %{conn: conn} do
777 activity1 = insert(:note_activity)
778 activity2 = insert(:note_activity)
779 admin = insert(:user, info: %{is_admin: true})
780 moderator = insert(:user, info: %{is_moderator: true})
784 |> assign(:user, admin)
785 |> delete("/api/v1/statuses/#{activity1.id}")
787 assert %{} = json_response(res_conn, 200)
791 |> assign(:user, moderator)
792 |> delete("/api/v1/statuses/#{activity2.id}")
794 assert %{} = json_response(res_conn, 200)
796 refute Activity.get_by_id(activity1.id)
797 refute Activity.get_by_id(activity2.id)
801 describe "filters" do
802 test "creating a filter", %{conn: conn} do
805 filter = %Pleroma.Filter{
812 |> assign(:user, user)
813 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
815 assert response = json_response(conn, 200)
816 assert response["phrase"] == filter.phrase
817 assert response["context"] == filter.context
818 assert response["irreversible"] == false
819 assert response["id"] != nil
820 assert response["id"] != ""
823 test "fetching a list of filters", %{conn: conn} do
826 query_one = %Pleroma.Filter{
833 query_two = %Pleroma.Filter{
840 {:ok, filter_one} = Pleroma.Filter.create(query_one)
841 {:ok, filter_two} = Pleroma.Filter.create(query_two)
845 |> assign(:user, user)
846 |> get("/api/v1/filters")
847 |> json_response(200)
853 filters: [filter_two, filter_one]
857 test "get a filter", %{conn: conn} do
860 query = %Pleroma.Filter{
867 {:ok, filter} = Pleroma.Filter.create(query)
871 |> assign(:user, user)
872 |> get("/api/v1/filters/#{filter.filter_id}")
874 assert _response = json_response(conn, 200)
877 test "update a filter", %{conn: conn} do
880 query = %Pleroma.Filter{
887 {:ok, _filter} = Pleroma.Filter.create(query)
889 new = %Pleroma.Filter{
896 |> assign(:user, user)
897 |> put("/api/v1/filters/#{query.filter_id}", %{
902 assert response = json_response(conn, 200)
903 assert response["phrase"] == new.phrase
904 assert response["context"] == new.context
907 test "delete a filter", %{conn: conn} do
910 query = %Pleroma.Filter{
917 {:ok, filter} = Pleroma.Filter.create(query)
921 |> assign(:user, user)
922 |> delete("/api/v1/filters/#{filter.filter_id}")
924 assert response = json_response(conn, 200)
925 assert response == %{}
930 test "creating a list", %{conn: conn} do
935 |> assign(:user, user)
936 |> post("/api/v1/lists", %{"title" => "cuties"})
938 assert %{"title" => title} = json_response(conn, 200)
939 assert title == "cuties"
942 test "adding users to a list", %{conn: conn} do
944 other_user = insert(:user)
945 {:ok, list} = Pleroma.List.create("name", user)
949 |> assign(:user, user)
950 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
952 assert %{} == json_response(conn, 200)
953 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
954 assert following == [other_user.follower_address]
957 test "removing users from a list", %{conn: conn} do
959 other_user = insert(:user)
960 third_user = insert(:user)
961 {:ok, list} = Pleroma.List.create("name", user)
962 {:ok, list} = Pleroma.List.follow(list, other_user)
963 {:ok, list} = Pleroma.List.follow(list, third_user)
967 |> assign(:user, user)
968 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
970 assert %{} == json_response(conn, 200)
971 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
972 assert following == [third_user.follower_address]
975 test "listing users in a list", %{conn: conn} do
977 other_user = insert(:user)
978 {:ok, list} = Pleroma.List.create("name", user)
979 {:ok, list} = Pleroma.List.follow(list, other_user)
983 |> assign(:user, user)
984 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
986 assert [%{"id" => id}] = json_response(conn, 200)
987 assert id == to_string(other_user.id)
990 test "retrieving a list", %{conn: conn} do
992 {:ok, list} = Pleroma.List.create("name", user)
996 |> assign(:user, user)
997 |> get("/api/v1/lists/#{list.id}")
999 assert %{"id" => id} = json_response(conn, 200)
1000 assert id == to_string(list.id)
1003 test "renaming a list", %{conn: conn} do
1004 user = insert(:user)
1005 {:ok, list} = Pleroma.List.create("name", user)
1009 |> assign(:user, user)
1010 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
1012 assert %{"title" => name} = json_response(conn, 200)
1013 assert name == "newname"
1016 test "deleting a list", %{conn: conn} do
1017 user = insert(:user)
1018 {:ok, list} = Pleroma.List.create("name", user)
1022 |> assign(:user, user)
1023 |> delete("/api/v1/lists/#{list.id}")
1025 assert %{} = json_response(conn, 200)
1026 assert is_nil(Repo.get(Pleroma.List, list.id))
1029 test "list timeline", %{conn: conn} do
1030 user = insert(:user)
1031 other_user = insert(:user)
1032 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1033 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1034 {:ok, list} = Pleroma.List.create("name", user)
1035 {:ok, list} = Pleroma.List.follow(list, other_user)
1039 |> assign(:user, user)
1040 |> get("/api/v1/timelines/list/#{list.id}")
1042 assert [%{"id" => id}] = json_response(conn, 200)
1044 assert id == to_string(activity_two.id)
1047 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1048 user = insert(:user)
1049 other_user = insert(:user)
1050 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1052 {:ok, _activity_two} =
1053 CommonAPI.post(other_user, %{
1054 "status" => "Marisa is cute.",
1055 "visibility" => "private"
1058 {:ok, list} = Pleroma.List.create("name", user)
1059 {:ok, list} = Pleroma.List.follow(list, other_user)
1063 |> assign(:user, user)
1064 |> get("/api/v1/timelines/list/#{list.id}")
1066 assert [%{"id" => id}] = json_response(conn, 200)
1068 assert id == to_string(activity_one.id)
1072 describe "notifications" do
1073 test "list of notifications", %{conn: conn} do
1074 user = insert(:user)
1075 other_user = insert(:user)
1077 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1079 {:ok, [_notification]} = Notification.create_notifications(activity)
1083 |> assign(:user, user)
1084 |> get("/api/v1/notifications")
1087 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1089 }\">@<span>#{user.nickname}</span></a></span>"
1091 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1092 assert response == expected_response
1095 test "getting a single notification", %{conn: conn} do
1096 user = insert(:user)
1097 other_user = insert(:user)
1099 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1101 {:ok, [notification]} = Notification.create_notifications(activity)
1105 |> assign(:user, user)
1106 |> get("/api/v1/notifications/#{notification.id}")
1109 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1111 }\">@<span>#{user.nickname}</span></a></span>"
1113 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1114 assert response == expected_response
1117 test "dismissing a single notification", %{conn: conn} do
1118 user = insert(:user)
1119 other_user = insert(:user)
1121 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1123 {:ok, [notification]} = Notification.create_notifications(activity)
1127 |> assign(:user, user)
1128 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1130 assert %{} = json_response(conn, 200)
1133 test "clearing all notifications", %{conn: conn} do
1134 user = insert(:user)
1135 other_user = insert(:user)
1137 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1139 {:ok, [_notification]} = Notification.create_notifications(activity)
1143 |> assign(:user, user)
1144 |> post("/api/v1/notifications/clear")
1146 assert %{} = json_response(conn, 200)
1150 |> assign(:user, user)
1151 |> get("/api/v1/notifications")
1153 assert all = json_response(conn, 200)
1157 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1158 user = insert(:user)
1159 other_user = insert(:user)
1161 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1162 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1163 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1164 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1166 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1167 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1168 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1169 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1173 |> assign(:user, user)
1178 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1180 result = json_response(conn_res, 200)
1181 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1186 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1188 result = json_response(conn_res, 200)
1189 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1194 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1196 result = json_response(conn_res, 200)
1197 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1200 test "filters notifications using exclude_types", %{conn: conn} do
1201 user = insert(:user)
1202 other_user = insert(:user)
1204 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1205 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1206 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1207 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1208 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1210 mention_notification_id =
1211 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1213 favorite_notification_id =
1214 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1216 reblog_notification_id =
1217 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1219 follow_notification_id =
1220 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1224 |> assign(:user, user)
1227 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1229 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1232 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1234 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1237 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1239 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1242 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1244 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1247 test "destroy multiple", %{conn: conn} do
1248 user = insert(:user)
1249 other_user = insert(:user)
1251 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1252 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1253 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1254 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1256 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1257 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1258 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1259 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1263 |> assign(:user, user)
1267 |> get("/api/v1/notifications")
1269 result = json_response(conn_res, 200)
1270 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1274 |> assign(:user, other_user)
1278 |> get("/api/v1/notifications")
1280 result = json_response(conn_res, 200)
1281 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1285 |> delete("/api/v1/notifications/destroy_multiple", %{
1286 "ids" => [notification1_id, notification2_id]
1289 assert json_response(conn_destroy, 200) == %{}
1293 |> get("/api/v1/notifications")
1295 result = json_response(conn_res, 200)
1296 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1299 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1300 user = insert(:user)
1301 user2 = insert(:user)
1303 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1304 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1306 conn = assign(conn, :user, user)
1308 conn = get(conn, "/api/v1/notifications")
1310 assert length(json_response(conn, 200)) == 1
1312 {:ok, user} = User.mute(user, user2)
1314 conn = assign(build_conn(), :user, user)
1315 conn = get(conn, "/api/v1/notifications")
1317 assert json_response(conn, 200) == []
1320 test "see notifications after muting user without notifications", %{conn: conn} do
1321 user = insert(:user)
1322 user2 = insert(:user)
1324 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1325 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1327 conn = assign(conn, :user, user)
1329 conn = get(conn, "/api/v1/notifications")
1331 assert length(json_response(conn, 200)) == 1
1333 {:ok, user} = User.mute(user, user2, false)
1335 conn = assign(build_conn(), :user, user)
1336 conn = get(conn, "/api/v1/notifications")
1338 assert length(json_response(conn, 200)) == 1
1341 test "see notifications after muting user with notifications and with_muted parameter", %{
1344 user = insert(:user)
1345 user2 = insert(:user)
1347 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1348 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1350 conn = assign(conn, :user, user)
1352 conn = get(conn, "/api/v1/notifications")
1354 assert length(json_response(conn, 200)) == 1
1356 {:ok, user} = User.mute(user, user2)
1358 conn = assign(build_conn(), :user, user)
1359 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1361 assert length(json_response(conn, 200)) == 1
1365 describe "reblogging" do
1366 test "reblogs and returns the reblogged status", %{conn: conn} do
1367 activity = insert(:note_activity)
1368 user = insert(:user)
1372 |> assign(:user, user)
1373 |> post("/api/v1/statuses/#{activity.id}/reblog")
1376 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1378 } = json_response(conn, 200)
1380 assert to_string(activity.id) == id
1383 test "reblogged status for another user", %{conn: conn} do
1384 activity = insert(:note_activity)
1385 user1 = insert(:user)
1386 user2 = insert(:user)
1387 user3 = insert(:user)
1388 CommonAPI.favorite(activity.id, user2)
1389 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1390 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1391 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1395 |> assign(:user, user3)
1396 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1399 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1400 "reblogged" => false,
1401 "favourited" => false,
1402 "bookmarked" => false
1403 } = json_response(conn_res, 200)
1407 |> assign(:user, user2)
1408 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1411 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1412 "reblogged" => true,
1413 "favourited" => true,
1414 "bookmarked" => true
1415 } = json_response(conn_res, 200)
1417 assert to_string(activity.id) == id
1420 test "returns 400 error when activity is not exist", %{conn: conn} do
1421 user = insert(:user)
1425 |> assign(:user, user)
1426 |> post("/api/v1/statuses/foo/reblog")
1428 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1432 describe "unreblogging" do
1433 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1434 activity = insert(:note_activity)
1435 user = insert(:user)
1437 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1441 |> assign(:user, user)
1442 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1444 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1446 assert to_string(activity.id) == id
1449 test "returns 400 error when activity is not exist", %{conn: conn} do
1450 user = insert(:user)
1454 |> assign(:user, user)
1455 |> post("/api/v1/statuses/foo/unreblog")
1457 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1461 describe "favoriting" do
1462 test "favs a status and returns it", %{conn: conn} do
1463 activity = insert(:note_activity)
1464 user = insert(:user)
1468 |> assign(:user, user)
1469 |> post("/api/v1/statuses/#{activity.id}/favourite")
1471 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1472 json_response(conn, 200)
1474 assert to_string(activity.id) == id
1477 test "returns 400 error for a wrong id", %{conn: conn} do
1478 user = insert(:user)
1482 |> assign(:user, user)
1483 |> post("/api/v1/statuses/1/favourite")
1485 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1489 describe "unfavoriting" do
1490 test "unfavorites a status and returns it", %{conn: conn} do
1491 activity = insert(:note_activity)
1492 user = insert(:user)
1494 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1498 |> assign(:user, user)
1499 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1501 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1502 json_response(conn, 200)
1504 assert to_string(activity.id) == id
1507 test "returns 400 error for a wrong id", %{conn: conn} do
1508 user = insert(:user)
1512 |> assign(:user, user)
1513 |> post("/api/v1/statuses/1/unfavourite")
1515 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1519 describe "user timelines" do
1520 test "gets a users statuses", %{conn: conn} do
1521 user_one = insert(:user)
1522 user_two = insert(:user)
1523 user_three = insert(:user)
1525 {:ok, user_three} = User.follow(user_three, user_one)
1527 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1529 {:ok, direct_activity} =
1530 CommonAPI.post(user_one, %{
1531 "status" => "Hi, @#{user_two.nickname}.",
1532 "visibility" => "direct"
1535 {:ok, private_activity} =
1536 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1540 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1542 assert [%{"id" => id}] = json_response(resp, 200)
1543 assert id == to_string(activity.id)
1547 |> assign(:user, user_two)
1548 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1550 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1551 assert id_one == to_string(direct_activity.id)
1552 assert id_two == to_string(activity.id)
1556 |> assign(:user, user_three)
1557 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1559 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1560 assert id_one == to_string(private_activity.id)
1561 assert id_two == to_string(activity.id)
1564 test "unimplemented pinned statuses feature", %{conn: conn} do
1565 note = insert(:note_activity)
1566 user = User.get_cached_by_ap_id(note.data["actor"])
1570 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1572 assert json_response(conn, 200) == []
1575 test "gets an users media", %{conn: conn} do
1576 note = insert(:note_activity)
1577 user = User.get_cached_by_ap_id(note.data["actor"])
1579 file = %Plug.Upload{
1580 content_type: "image/jpg",
1581 path: Path.absname("test/fixtures/image.jpg"),
1582 filename: "an_image.jpg"
1585 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1587 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1591 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1593 assert [%{"id" => id}] = json_response(conn, 200)
1594 assert id == to_string(image_post.id)
1598 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1600 assert [%{"id" => id}] = json_response(conn, 200)
1601 assert id == to_string(image_post.id)
1604 test "gets a user's statuses without reblogs", %{conn: conn} do
1605 user = insert(:user)
1606 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1607 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1611 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1613 assert [%{"id" => id}] = json_response(conn, 200)
1614 assert id == to_string(post.id)
1618 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1620 assert [%{"id" => id}] = json_response(conn, 200)
1621 assert id == to_string(post.id)
1624 test "filters user's statuses by a hashtag", %{conn: conn} do
1625 user = insert(:user)
1626 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1627 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1631 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1633 assert [%{"id" => id}] = json_response(conn, 200)
1634 assert id == to_string(post.id)
1638 describe "user relationships" do
1639 test "returns the relationships for the current user", %{conn: conn} do
1640 user = insert(:user)
1641 other_user = insert(:user)
1642 {:ok, user} = User.follow(user, other_user)
1646 |> assign(:user, user)
1647 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1649 assert [relationship] = json_response(conn, 200)
1651 assert to_string(other_user.id) == relationship["id"]
1655 describe "media upload" do
1657 user = insert(:user)
1661 |> assign(:user, user)
1663 image = %Plug.Upload{
1664 content_type: "image/jpg",
1665 path: Path.absname("test/fixtures/image.jpg"),
1666 filename: "an_image.jpg"
1669 [conn: conn, image: image]
1672 clear_config([:media_proxy])
1673 clear_config([Pleroma.Upload])
1675 test "returns uploaded image", %{conn: conn, image: image} do
1676 desc = "Description of the image"
1680 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1681 |> json_response(:ok)
1683 assert media["type"] == "image"
1684 assert media["description"] == desc
1687 object = Repo.get(Object, media["id"])
1688 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1692 describe "locked accounts" do
1693 test "/api/v1/follow_requests works" do
1694 user = insert(:user, %{info: %User.Info{locked: true}})
1695 other_user = insert(:user)
1697 {:ok, _activity} = ActivityPub.follow(other_user, user)
1699 user = User.get_cached_by_id(user.id)
1700 other_user = User.get_cached_by_id(other_user.id)
1702 assert User.following?(other_user, user) == false
1706 |> assign(:user, user)
1707 |> get("/api/v1/follow_requests")
1709 assert [relationship] = json_response(conn, 200)
1710 assert to_string(other_user.id) == relationship["id"]
1713 test "/api/v1/follow_requests/:id/authorize works" do
1714 user = insert(:user, %{info: %User.Info{locked: true}})
1715 other_user = insert(:user)
1717 {:ok, _activity} = ActivityPub.follow(other_user, user)
1719 user = User.get_cached_by_id(user.id)
1720 other_user = User.get_cached_by_id(other_user.id)
1722 assert User.following?(other_user, user) == false
1726 |> assign(:user, user)
1727 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1729 assert relationship = json_response(conn, 200)
1730 assert to_string(other_user.id) == relationship["id"]
1732 user = User.get_cached_by_id(user.id)
1733 other_user = User.get_cached_by_id(other_user.id)
1735 assert User.following?(other_user, user) == true
1738 test "verify_credentials", %{conn: conn} do
1739 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1743 |> assign(:user, user)
1744 |> get("/api/v1/accounts/verify_credentials")
1746 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1747 assert id == to_string(user.id)
1750 test "/api/v1/follow_requests/:id/reject works" do
1751 user = insert(:user, %{info: %User.Info{locked: true}})
1752 other_user = insert(:user)
1754 {:ok, _activity} = ActivityPub.follow(other_user, user)
1756 user = User.get_cached_by_id(user.id)
1760 |> assign(:user, user)
1761 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1763 assert relationship = json_response(conn, 200)
1764 assert to_string(other_user.id) == relationship["id"]
1766 user = User.get_cached_by_id(user.id)
1767 other_user = User.get_cached_by_id(other_user.id)
1769 assert User.following?(other_user, user) == false
1773 test "account fetching", %{conn: conn} do
1774 user = insert(:user)
1778 |> get("/api/v1/accounts/#{user.id}")
1780 assert %{"id" => id} = json_response(conn, 200)
1781 assert id == to_string(user.id)
1785 |> get("/api/v1/accounts/-1")
1787 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1790 test "account fetching also works nickname", %{conn: conn} do
1791 user = insert(:user)
1795 |> get("/api/v1/accounts/#{user.nickname}")
1797 assert %{"id" => id} = json_response(conn, 200)
1798 assert id == user.id
1801 test "mascot upload", %{conn: conn} do
1802 user = insert(:user)
1804 non_image_file = %Plug.Upload{
1805 content_type: "audio/mpeg",
1806 path: Path.absname("test/fixtures/sound.mp3"),
1807 filename: "sound.mp3"
1812 |> assign(:user, user)
1813 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1815 assert json_response(conn, 415)
1817 file = %Plug.Upload{
1818 content_type: "image/jpg",
1819 path: Path.absname("test/fixtures/image.jpg"),
1820 filename: "an_image.jpg"
1825 |> assign(:user, user)
1826 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1828 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1831 test "mascot retrieving", %{conn: conn} do
1832 user = insert(:user)
1833 # When user hasn't set a mascot, we should just get pleroma tan back
1836 |> assign(:user, user)
1837 |> get("/api/v1/pleroma/mascot")
1839 assert %{"url" => url} = json_response(conn, 200)
1840 assert url =~ "pleroma-fox-tan-smol"
1842 # When a user sets their mascot, we should get that back
1843 file = %Plug.Upload{
1844 content_type: "image/jpg",
1845 path: Path.absname("test/fixtures/image.jpg"),
1846 filename: "an_image.jpg"
1851 |> assign(:user, user)
1852 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1854 assert json_response(conn, 200)
1856 user = User.get_cached_by_id(user.id)
1860 |> assign(:user, user)
1861 |> get("/api/v1/pleroma/mascot")
1863 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1864 assert url =~ "an_image"
1867 test "hashtag timeline", %{conn: conn} do
1868 following = insert(:user)
1871 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1873 {:ok, [_activity]} =
1874 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1878 |> get("/api/v1/timelines/tag/2hu")
1880 assert [%{"id" => id}] = json_response(nconn, 200)
1882 assert id == to_string(activity.id)
1884 # works for different capitalization too
1887 |> get("/api/v1/timelines/tag/2HU")
1889 assert [%{"id" => id}] = json_response(nconn, 200)
1891 assert id == to_string(activity.id)
1895 test "multi-hashtag timeline", %{conn: conn} do
1896 user = insert(:user)
1898 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1899 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1900 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1904 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1906 [status_none, status_test1, status_test] = json_response(any_test, 200)
1908 assert to_string(activity_test.id) == status_test["id"]
1909 assert to_string(activity_test1.id) == status_test1["id"]
1910 assert to_string(activity_none.id) == status_none["id"]
1914 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1916 assert [status_test1] == json_response(restricted_test, 200)
1918 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1920 assert [status_none] == json_response(all_test, 200)
1923 test "getting followers", %{conn: conn} do
1924 user = insert(:user)
1925 other_user = insert(:user)
1926 {:ok, user} = User.follow(user, other_user)
1930 |> get("/api/v1/accounts/#{other_user.id}/followers")
1932 assert [%{"id" => id}] = json_response(conn, 200)
1933 assert id == to_string(user.id)
1936 test "getting followers, hide_followers", %{conn: conn} do
1937 user = insert(:user)
1938 other_user = insert(:user, %{info: %{hide_followers: true}})
1939 {:ok, _user} = User.follow(user, other_user)
1943 |> get("/api/v1/accounts/#{other_user.id}/followers")
1945 assert [] == json_response(conn, 200)
1948 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1949 user = insert(:user)
1950 other_user = insert(:user, %{info: %{hide_followers: true}})
1951 {:ok, _user} = User.follow(user, other_user)
1955 |> assign(:user, other_user)
1956 |> get("/api/v1/accounts/#{other_user.id}/followers")
1958 refute [] == json_response(conn, 200)
1961 test "getting followers, pagination", %{conn: conn} do
1962 user = insert(:user)
1963 follower1 = insert(:user)
1964 follower2 = insert(:user)
1965 follower3 = insert(:user)
1966 {:ok, _} = User.follow(follower1, user)
1967 {:ok, _} = User.follow(follower2, user)
1968 {:ok, _} = User.follow(follower3, user)
1972 |> assign(:user, user)
1976 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1978 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1979 assert id3 == follower3.id
1980 assert id2 == follower2.id
1984 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1986 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1987 assert id2 == follower2.id
1988 assert id1 == follower1.id
1992 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1994 assert [%{"id" => id2}] = json_response(res_conn, 200)
1995 assert id2 == follower2.id
1997 assert [link_header] = get_resp_header(res_conn, "link")
1998 assert link_header =~ ~r/min_id=#{follower2.id}/
1999 assert link_header =~ ~r/max_id=#{follower2.id}/
2002 test "getting following", %{conn: conn} do
2003 user = insert(:user)
2004 other_user = insert(:user)
2005 {:ok, user} = User.follow(user, other_user)
2009 |> get("/api/v1/accounts/#{user.id}/following")
2011 assert [%{"id" => id}] = json_response(conn, 200)
2012 assert id == to_string(other_user.id)
2015 test "getting following, hide_follows", %{conn: conn} do
2016 user = insert(:user, %{info: %{hide_follows: true}})
2017 other_user = insert(:user)
2018 {:ok, user} = User.follow(user, other_user)
2022 |> get("/api/v1/accounts/#{user.id}/following")
2024 assert [] == json_response(conn, 200)
2027 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2028 user = insert(:user, %{info: %{hide_follows: true}})
2029 other_user = insert(:user)
2030 {:ok, user} = User.follow(user, other_user)
2034 |> assign(:user, user)
2035 |> get("/api/v1/accounts/#{user.id}/following")
2037 refute [] == json_response(conn, 200)
2040 test "getting following, pagination", %{conn: conn} do
2041 user = insert(:user)
2042 following1 = insert(:user)
2043 following2 = insert(:user)
2044 following3 = insert(:user)
2045 {:ok, _} = User.follow(user, following1)
2046 {:ok, _} = User.follow(user, following2)
2047 {:ok, _} = User.follow(user, following3)
2051 |> assign(:user, user)
2055 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2057 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2058 assert id3 == following3.id
2059 assert id2 == following2.id
2063 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2065 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2066 assert id2 == following2.id
2067 assert id1 == following1.id
2071 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2073 assert [%{"id" => id2}] = json_response(res_conn, 200)
2074 assert id2 == following2.id
2076 assert [link_header] = get_resp_header(res_conn, "link")
2077 assert link_header =~ ~r/min_id=#{following2.id}/
2078 assert link_header =~ ~r/max_id=#{following2.id}/
2081 test "following / unfollowing a user", %{conn: conn} do
2082 user = insert(:user)
2083 other_user = insert(:user)
2087 |> assign(:user, user)
2088 |> post("/api/v1/accounts/#{other_user.id}/follow")
2090 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2092 user = User.get_cached_by_id(user.id)
2096 |> assign(:user, user)
2097 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2099 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2101 user = User.get_cached_by_id(user.id)
2105 |> assign(:user, user)
2106 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2108 assert %{"id" => id} = json_response(conn, 200)
2109 assert id == to_string(other_user.id)
2112 test "following without reblogs" do
2113 follower = insert(:user)
2114 followed = insert(:user)
2115 other_user = insert(:user)
2119 |> assign(:user, follower)
2120 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2122 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2124 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2125 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2129 |> assign(:user, User.get_cached_by_id(follower.id))
2130 |> get("/api/v1/timelines/home")
2132 assert [] == json_response(conn, 200)
2136 |> assign(:user, follower)
2137 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2139 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2143 |> assign(:user, User.get_cached_by_id(follower.id))
2144 |> get("/api/v1/timelines/home")
2146 expected_activity_id = reblog.id
2147 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2150 test "following / unfollowing errors" do
2151 user = insert(:user)
2155 |> assign(:user, user)
2158 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2159 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2162 user = User.get_cached_by_id(user.id)
2163 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2164 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2166 # self follow via uri
2167 user = User.get_cached_by_id(user.id)
2168 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2169 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2171 # follow non existing user
2172 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2173 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2175 # follow non existing user via uri
2176 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2177 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2179 # unfollow non existing user
2180 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2181 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2184 describe "mute/unmute" do
2185 test "with notifications", %{conn: conn} do
2186 user = insert(:user)
2187 other_user = insert(:user)
2191 |> assign(:user, user)
2192 |> post("/api/v1/accounts/#{other_user.id}/mute")
2194 response = json_response(conn, 200)
2196 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2197 user = User.get_cached_by_id(user.id)
2201 |> assign(:user, user)
2202 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2204 response = json_response(conn, 200)
2205 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2208 test "without notifications", %{conn: conn} do
2209 user = insert(:user)
2210 other_user = insert(:user)
2214 |> assign(:user, user)
2215 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2217 response = json_response(conn, 200)
2219 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2220 user = User.get_cached_by_id(user.id)
2224 |> assign(:user, user)
2225 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2227 response = json_response(conn, 200)
2228 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2232 test "subscribing / unsubscribing to a user", %{conn: conn} do
2233 user = insert(:user)
2234 subscription_target = insert(:user)
2238 |> assign(:user, user)
2239 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2241 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2245 |> assign(:user, user)
2246 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2248 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2251 test "getting a list of mutes", %{conn: conn} do
2252 user = insert(:user)
2253 other_user = insert(:user)
2255 {:ok, user} = User.mute(user, other_user)
2259 |> assign(:user, user)
2260 |> get("/api/v1/mutes")
2262 other_user_id = to_string(other_user.id)
2263 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2266 test "blocking / unblocking a user", %{conn: conn} do
2267 user = insert(:user)
2268 other_user = insert(:user)
2272 |> assign(:user, user)
2273 |> post("/api/v1/accounts/#{other_user.id}/block")
2275 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2277 user = User.get_cached_by_id(user.id)
2281 |> assign(:user, user)
2282 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2284 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2287 test "getting a list of blocks", %{conn: conn} do
2288 user = insert(:user)
2289 other_user = insert(:user)
2291 {:ok, user} = User.block(user, other_user)
2295 |> assign(:user, user)
2296 |> get("/api/v1/blocks")
2298 other_user_id = to_string(other_user.id)
2299 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2302 test "blocking / unblocking a domain", %{conn: conn} do
2303 user = insert(:user)
2304 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2308 |> assign(:user, user)
2309 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2311 assert %{} = json_response(conn, 200)
2312 user = User.get_cached_by_ap_id(user.ap_id)
2313 assert User.blocks?(user, other_user)
2317 |> assign(:user, user)
2318 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2320 assert %{} = json_response(conn, 200)
2321 user = User.get_cached_by_ap_id(user.ap_id)
2322 refute User.blocks?(user, other_user)
2325 test "getting a list of domain blocks", %{conn: conn} do
2326 user = insert(:user)
2328 {:ok, user} = User.block_domain(user, "bad.site")
2329 {:ok, user} = User.block_domain(user, "even.worse.site")
2333 |> assign(:user, user)
2334 |> get("/api/v1/domain_blocks")
2336 domain_blocks = json_response(conn, 200)
2338 assert "bad.site" in domain_blocks
2339 assert "even.worse.site" in domain_blocks
2342 test "unimplemented follow_requests, blocks, domain blocks" do
2343 user = insert(:user)
2345 ["blocks", "domain_blocks", "follow_requests"]
2346 |> Enum.each(fn endpoint ->
2349 |> assign(:user, user)
2350 |> get("/api/v1/#{endpoint}")
2352 assert [] = json_response(conn, 200)
2356 test "returns the favorites of a user", %{conn: conn} do
2357 user = insert(:user)
2358 other_user = insert(:user)
2360 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2361 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2363 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2367 |> assign(:user, user)
2368 |> get("/api/v1/favourites")
2370 assert [status] = json_response(first_conn, 200)
2371 assert status["id"] == to_string(activity.id)
2373 assert [{"link", _link_header}] =
2374 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2376 # Honours query params
2377 {:ok, second_activity} =
2378 CommonAPI.post(other_user, %{
2380 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2383 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2385 last_like = status["id"]
2389 |> assign(:user, user)
2390 |> get("/api/v1/favourites?since_id=#{last_like}")
2392 assert [second_status] = json_response(second_conn, 200)
2393 assert second_status["id"] == to_string(second_activity.id)
2397 |> assign(:user, user)
2398 |> get("/api/v1/favourites?limit=0")
2400 assert [] = json_response(third_conn, 200)
2403 describe "getting favorites timeline of specified user" do
2405 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2406 [current_user: current_user, user: user]
2409 test "returns list of statuses favorited by specified user", %{
2411 current_user: current_user,
2414 [activity | _] = insert_pair(:note_activity)
2415 CommonAPI.favorite(activity.id, user)
2419 |> assign(:user, current_user)
2420 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2421 |> json_response(:ok)
2425 assert length(response) == 1
2426 assert like["id"] == activity.id
2429 test "returns favorites for specified user_id when user is not logged in", %{
2433 activity = insert(:note_activity)
2434 CommonAPI.favorite(activity.id, user)
2438 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2439 |> json_response(:ok)
2441 assert length(response) == 1
2444 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2446 current_user: current_user,
2450 CommonAPI.post(current_user, %{
2451 "status" => "Hi @#{user.nickname}!",
2452 "visibility" => "direct"
2455 CommonAPI.favorite(direct.id, user)
2459 |> assign(:user, current_user)
2460 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2461 |> json_response(:ok)
2463 assert length(response) == 1
2465 anonymous_response =
2467 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2468 |> json_response(:ok)
2470 assert Enum.empty?(anonymous_response)
2473 test "does not return others' favorited DM when user is not one of recipients", %{
2475 current_user: current_user,
2478 user_two = insert(:user)
2481 CommonAPI.post(user_two, %{
2482 "status" => "Hi @#{user.nickname}!",
2483 "visibility" => "direct"
2486 CommonAPI.favorite(direct.id, user)
2490 |> assign(:user, current_user)
2491 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2492 |> json_response(:ok)
2494 assert Enum.empty?(response)
2497 test "paginates favorites using since_id and max_id", %{
2499 current_user: current_user,
2502 activities = insert_list(10, :note_activity)
2504 Enum.each(activities, fn activity ->
2505 CommonAPI.favorite(activity.id, user)
2508 third_activity = Enum.at(activities, 2)
2509 seventh_activity = Enum.at(activities, 6)
2513 |> assign(:user, current_user)
2514 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2515 since_id: third_activity.id,
2516 max_id: seventh_activity.id
2518 |> json_response(:ok)
2520 assert length(response) == 3
2521 refute third_activity in response
2522 refute seventh_activity in response
2525 test "limits favorites using limit parameter", %{
2527 current_user: current_user,
2531 |> insert_list(:note_activity)
2532 |> Enum.each(fn activity ->
2533 CommonAPI.favorite(activity.id, user)
2538 |> assign(:user, current_user)
2539 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2540 |> json_response(:ok)
2542 assert length(response) == 3
2545 test "returns empty response when user does not have any favorited statuses", %{
2547 current_user: current_user,
2552 |> assign(:user, current_user)
2553 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2554 |> json_response(:ok)
2556 assert Enum.empty?(response)
2559 test "returns 404 error when specified user is not exist", %{conn: conn} do
2560 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2562 assert json_response(conn, 404) == %{"error" => "Record not found"}
2565 test "returns 403 error when user has hidden own favorites", %{
2567 current_user: current_user
2569 user = insert(:user, %{info: %{hide_favorites: true}})
2570 activity = insert(:note_activity)
2571 CommonAPI.favorite(activity.id, user)
2575 |> assign(:user, current_user)
2576 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2578 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2581 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2582 user = insert(:user)
2583 activity = insert(:note_activity)
2584 CommonAPI.favorite(activity.id, user)
2588 |> assign(:user, current_user)
2589 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2591 assert user.info.hide_favorites
2592 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2596 test "get instance information", %{conn: conn} do
2597 conn = get(conn, "/api/v1/instance")
2598 assert result = json_response(conn, 200)
2600 email = Config.get([:instance, :email])
2601 # Note: not checking for "max_toot_chars" since it's optional
2607 "email" => from_config_email,
2609 "streaming_api" => _
2614 "registrations" => _,
2618 assert email == from_config_email
2621 test "get instance stats", %{conn: conn} do
2622 user = insert(:user, %{local: true})
2624 user2 = insert(:user, %{local: true})
2625 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2627 insert(:user, %{local: false, nickname: "u@peer1.com"})
2628 insert(:user, %{local: false, nickname: "u@peer2.com"})
2630 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2632 # Stats should count users with missing or nil `info.deactivated` value
2633 user = User.get_cached_by_id(user.id)
2634 info_change = Changeset.change(user.info, %{deactivated: nil})
2638 |> Changeset.change()
2639 |> Changeset.put_embed(:info, info_change)
2640 |> User.update_and_set_cache()
2642 Pleroma.Stats.force_update()
2644 conn = get(conn, "/api/v1/instance")
2646 assert result = json_response(conn, 200)
2648 stats = result["stats"]
2651 assert stats["user_count"] == 1
2652 assert stats["status_count"] == 1
2653 assert stats["domain_count"] == 2
2656 test "get peers", %{conn: conn} do
2657 insert(:user, %{local: false, nickname: "u@peer1.com"})
2658 insert(:user, %{local: false, nickname: "u@peer2.com"})
2660 Pleroma.Stats.force_update()
2662 conn = get(conn, "/api/v1/instance/peers")
2664 assert result = json_response(conn, 200)
2666 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2669 test "put settings", %{conn: conn} do
2670 user = insert(:user)
2674 |> assign(:user, user)
2675 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2677 assert _result = json_response(conn, 200)
2679 user = User.get_cached_by_ap_id(user.ap_id)
2680 assert user.info.settings == %{"programming" => "socks"}
2683 describe "pinned statuses" do
2685 user = insert(:user)
2686 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2688 [user: user, activity: activity]
2691 clear_config([:instance, :max_pinned_statuses]) do
2692 Config.put([:instance, :max_pinned_statuses], 1)
2695 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2696 {:ok, _} = CommonAPI.pin(activity.id, user)
2700 |> assign(:user, user)
2701 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2702 |> json_response(200)
2704 id_str = to_string(activity.id)
2706 assert [%{"id" => ^id_str, "pinned" => true}] = result
2709 test "pin status", %{conn: conn, user: user, activity: activity} do
2710 id_str = to_string(activity.id)
2712 assert %{"id" => ^id_str, "pinned" => true} =
2714 |> assign(:user, user)
2715 |> post("/api/v1/statuses/#{activity.id}/pin")
2716 |> json_response(200)
2718 assert [%{"id" => ^id_str, "pinned" => true}] =
2720 |> assign(:user, user)
2721 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2722 |> json_response(200)
2725 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2726 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2730 |> assign(:user, user)
2731 |> post("/api/v1/statuses/#{dm.id}/pin")
2733 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2736 test "unpin status", %{conn: conn, user: user, activity: activity} do
2737 {:ok, _} = CommonAPI.pin(activity.id, user)
2739 id_str = to_string(activity.id)
2740 user = refresh_record(user)
2742 assert %{"id" => ^id_str, "pinned" => false} =
2744 |> assign(:user, user)
2745 |> post("/api/v1/statuses/#{activity.id}/unpin")
2746 |> json_response(200)
2750 |> assign(:user, user)
2751 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2752 |> json_response(200)
2755 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2758 |> assign(:user, user)
2759 |> post("/api/v1/statuses/1/unpin")
2761 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2764 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2765 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2767 id_str_one = to_string(activity_one.id)
2769 assert %{"id" => ^id_str_one, "pinned" => true} =
2771 |> assign(:user, user)
2772 |> post("/api/v1/statuses/#{id_str_one}/pin")
2773 |> json_response(200)
2775 user = refresh_record(user)
2777 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2779 |> assign(:user, user)
2780 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2781 |> json_response(400)
2787 Config.put([:rich_media, :enabled], true)
2789 user = insert(:user)
2793 test "returns rich-media card", %{conn: conn, user: user} do
2794 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2797 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2798 "provider_name" => "example.com",
2799 "provider_url" => "https://example.com",
2800 "title" => "The Rock",
2802 "url" => "https://example.com/ogp",
2804 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2807 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2808 "title" => "The Rock",
2809 "type" => "video.movie",
2810 "url" => "https://example.com/ogp",
2812 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2819 |> get("/api/v1/statuses/#{activity.id}/card")
2820 |> json_response(200)
2822 assert response == card_data
2824 # works with private posts
2826 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2830 |> assign(:user, user)
2831 |> get("/api/v1/statuses/#{activity.id}/card")
2832 |> json_response(200)
2834 assert response_two == card_data
2837 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2839 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2843 |> get("/api/v1/statuses/#{activity.id}/card")
2844 |> json_response(:ok)
2846 assert response == %{
2848 "title" => "Pleroma",
2849 "description" => "",
2851 "provider_name" => "example.com",
2852 "provider_url" => "https://example.com",
2853 "url" => "https://example.com/ogp-missing-data",
2856 "title" => "Pleroma",
2857 "type" => "website",
2858 "url" => "https://example.com/ogp-missing-data"
2866 user = insert(:user)
2867 for_user = insert(:user)
2870 CommonAPI.post(user, %{
2871 "status" => "heweoo?"
2875 CommonAPI.post(user, %{
2876 "status" => "heweoo!"
2881 |> assign(:user, for_user)
2882 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2884 assert json_response(response1, 200)["bookmarked"] == true
2888 |> assign(:user, for_user)
2889 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2891 assert json_response(response2, 200)["bookmarked"] == true
2895 |> assign(:user, for_user)
2896 |> get("/api/v1/bookmarks")
2898 assert [json_response(response2, 200), json_response(response1, 200)] ==
2899 json_response(bookmarks, 200)
2903 |> assign(:user, for_user)
2904 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2906 assert json_response(response1, 200)["bookmarked"] == false
2910 |> assign(:user, for_user)
2911 |> get("/api/v1/bookmarks")
2913 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2916 describe "conversation muting" do
2918 post_user = insert(:user)
2919 user = insert(:user)
2921 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2923 [user: user, activity: activity]
2926 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2927 id_str = to_string(activity.id)
2929 assert %{"id" => ^id_str, "muted" => true} =
2931 |> assign(:user, user)
2932 |> post("/api/v1/statuses/#{activity.id}/mute")
2933 |> json_response(200)
2936 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2937 {:ok, _} = CommonAPI.add_mute(user, activity)
2941 |> assign(:user, user)
2942 |> post("/api/v1/statuses/#{activity.id}/mute")
2944 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2947 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2948 {:ok, _} = CommonAPI.add_mute(user, activity)
2950 id_str = to_string(activity.id)
2951 user = refresh_record(user)
2953 assert %{"id" => ^id_str, "muted" => false} =
2955 |> assign(:user, user)
2956 |> post("/api/v1/statuses/#{activity.id}/unmute")
2957 |> json_response(200)
2961 describe "reports" do
2963 reporter = insert(:user)
2964 target_user = insert(:user)
2966 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2968 [reporter: reporter, target_user: target_user, activity: activity]
2971 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2972 assert %{"action_taken" => false, "id" => _} =
2974 |> assign(:user, reporter)
2975 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2976 |> json_response(200)
2979 test "submit a report with statuses and comment", %{
2982 target_user: target_user,
2985 assert %{"action_taken" => false, "id" => _} =
2987 |> assign(:user, reporter)
2988 |> post("/api/v1/reports", %{
2989 "account_id" => target_user.id,
2990 "status_ids" => [activity.id],
2991 "comment" => "bad status!",
2992 "forward" => "false"
2994 |> json_response(200)
2997 test "account_id is required", %{
3002 assert %{"error" => "Valid `account_id` required"} =
3004 |> assign(:user, reporter)
3005 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3006 |> json_response(400)
3009 test "comment must be up to the size specified in the config", %{
3012 target_user: target_user
3014 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3015 comment = String.pad_trailing("a", max_size + 1, "a")
3017 error = %{"error" => "Comment must be up to #{max_size} characters"}
3021 |> assign(:user, reporter)
3022 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3023 |> json_response(400)
3026 test "returns error when account is not exist", %{
3033 |> assign(:user, reporter)
3034 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3036 assert json_response(conn, 400) == %{"error" => "Account not found"}
3040 describe "link headers" do
3041 test "preserves parameters in link headers", %{conn: conn} do
3042 user = insert(:user)
3043 other_user = insert(:user)
3046 CommonAPI.post(other_user, %{
3047 "status" => "hi @#{user.nickname}",
3048 "visibility" => "public"
3052 CommonAPI.post(other_user, %{
3053 "status" => "hi @#{user.nickname}",
3054 "visibility" => "public"
3057 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3058 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3062 |> assign(:user, user)
3063 |> get("/api/v1/notifications", %{media_only: true})
3065 assert [link_header] = get_resp_header(conn, "link")
3066 assert link_header =~ ~r/media_only=true/
3067 assert link_header =~ ~r/min_id=#{notification2.id}/
3068 assert link_header =~ ~r/max_id=#{notification1.id}/
3072 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3073 # Need to set an old-style integer ID to reproduce the problem
3074 # (these are no longer assigned to new accounts but were preserved
3075 # for existing accounts during the migration to flakeIDs)
3076 user_one = insert(:user, %{id: 1212})
3077 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3081 |> get("/api/v1/accounts/#{user_one.id}")
3085 |> get("/api/v1/accounts/#{user_two.nickname}")
3089 |> get("/api/v1/accounts/#{user_two.id}")
3091 acc_one = json_response(resp_one, 200)
3092 acc_two = json_response(resp_two, 200)
3093 acc_three = json_response(resp_three, 200)
3094 refute acc_one == acc_two
3095 assert acc_two == acc_three
3098 describe "custom emoji" do
3099 test "with tags", %{conn: conn} do
3102 |> get("/api/v1/custom_emojis")
3103 |> json_response(200)
3105 assert Map.has_key?(emoji, "shortcode")
3106 assert Map.has_key?(emoji, "static_url")
3107 assert Map.has_key?(emoji, "tags")
3108 assert is_list(emoji["tags"])
3109 assert Map.has_key?(emoji, "category")
3110 assert Map.has_key?(emoji, "url")
3111 assert Map.has_key?(emoji, "visible_in_picker")
3115 describe "index/2 redirections" do
3116 setup %{conn: conn} do
3120 signing_salt: "cooldude"
3125 |> Plug.Session.call(Plug.Session.init(session_opts))
3128 test_path = "/web/statuses/test"
3129 %{conn: conn, path: test_path}
3132 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3133 conn = get(conn, path)
3135 assert conn.status == 302
3136 assert redirected_to(conn) == "/web/login"
3139 test "redirects not logged-in users to the login page on private instances", %{
3143 Config.put([:instance, :public], false)
3145 conn = get(conn, path)
3147 assert conn.status == 302
3148 assert redirected_to(conn) == "/web/login"
3151 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3152 token = insert(:oauth_token)
3156 |> assign(:user, token.user)
3157 |> put_session(:oauth_token, token.token)
3160 assert conn.status == 200
3163 test "saves referer path to session", %{conn: conn, path: path} do
3164 conn = get(conn, path)
3165 return_to = Plug.Conn.get_session(conn, :return_to)
3167 assert return_to == path
3170 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3171 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3172 auth = insert(:oauth_authorization, app: app)
3176 |> put_session(:return_to, path)
3177 |> get("/web/login", %{code: auth.token})
3179 assert conn.status == 302
3180 assert redirected_to(conn) == path
3183 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3184 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3185 auth = insert(:oauth_authorization, app: app)
3187 conn = get(conn, "/web/login", %{code: auth.token})
3189 assert conn.status == 302
3190 assert redirected_to(conn) == "/web/getting-started"
3194 describe "scheduled activities" do
3195 test "creates a scheduled activity", %{conn: conn} do
3196 user = insert(:user)
3197 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3201 |> assign(:user, user)
3202 |> post("/api/v1/statuses", %{
3203 "status" => "scheduled",
3204 "scheduled_at" => scheduled_at
3207 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3208 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3209 assert [] == Repo.all(Activity)
3212 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3213 user = insert(:user)
3214 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3216 file = %Plug.Upload{
3217 content_type: "image/jpg",
3218 path: Path.absname("test/fixtures/image.jpg"),
3219 filename: "an_image.jpg"
3222 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3226 |> assign(:user, user)
3227 |> post("/api/v1/statuses", %{
3228 "media_ids" => [to_string(upload.id)],
3229 "status" => "scheduled",
3230 "scheduled_at" => scheduled_at
3233 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3234 assert %{"type" => "image"} = media_attachment
3237 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3239 user = insert(:user)
3242 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3246 |> assign(:user, user)
3247 |> post("/api/v1/statuses", %{
3248 "status" => "not scheduled",
3249 "scheduled_at" => scheduled_at
3252 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3253 assert [] == Repo.all(ScheduledActivity)
3256 test "returns error when daily user limit is exceeded", %{conn: conn} do
3257 user = insert(:user)
3260 NaiveDateTime.utc_now()
3261 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3262 |> NaiveDateTime.to_iso8601()
3264 attrs = %{params: %{}, scheduled_at: today}
3265 {:ok, _} = ScheduledActivity.create(user, attrs)
3266 {:ok, _} = ScheduledActivity.create(user, attrs)
3270 |> assign(:user, user)
3271 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3273 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3276 test "returns error when total user limit is exceeded", %{conn: conn} do
3277 user = insert(:user)
3280 NaiveDateTime.utc_now()
3281 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3282 |> NaiveDateTime.to_iso8601()
3285 NaiveDateTime.utc_now()
3286 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3287 |> NaiveDateTime.to_iso8601()
3289 attrs = %{params: %{}, scheduled_at: today}
3290 {:ok, _} = ScheduledActivity.create(user, attrs)
3291 {:ok, _} = ScheduledActivity.create(user, attrs)
3292 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3296 |> assign(:user, user)
3297 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3299 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3302 test "shows scheduled activities", %{conn: conn} do
3303 user = insert(:user)
3304 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3305 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3306 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3307 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3311 |> assign(:user, user)
3316 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3318 result = json_response(conn_res, 200)
3319 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3324 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3326 result = json_response(conn_res, 200)
3327 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3332 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3334 result = json_response(conn_res, 200)
3335 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3338 test "shows a scheduled activity", %{conn: conn} do
3339 user = insert(:user)
3340 scheduled_activity = insert(:scheduled_activity, user: user)
3344 |> assign(:user, user)
3345 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3347 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3348 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3352 |> assign(:user, user)
3353 |> get("/api/v1/scheduled_statuses/404")
3355 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3358 test "updates a scheduled activity", %{conn: conn} do
3359 user = insert(:user)
3360 scheduled_activity = insert(:scheduled_activity, user: user)
3363 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3367 |> assign(:user, user)
3368 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3369 scheduled_at: new_scheduled_at
3372 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3373 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3377 |> assign(:user, user)
3378 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3380 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3383 test "deletes a scheduled activity", %{conn: conn} do
3384 user = insert(:user)
3385 scheduled_activity = insert(:scheduled_activity, user: user)
3389 |> assign(:user, user)
3390 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3392 assert %{} = json_response(res_conn, 200)
3393 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3397 |> assign(:user, user)
3398 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3400 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3404 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3405 user1 = insert(:user)
3406 user2 = insert(:user)
3407 user3 = insert(:user)
3409 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3411 # Reply to status from another user
3414 |> assign(:user, user2)
3415 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3417 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3419 activity = Activity.get_by_id_with_object(id)
3421 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3422 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3424 # Reblog from the third user
3427 |> assign(:user, user3)
3428 |> post("/api/v1/statuses/#{activity.id}/reblog")
3430 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3431 json_response(conn2, 200)
3433 assert to_string(activity.id) == id
3435 # Getting third user status
3438 |> assign(:user, user3)
3439 |> get("api/v1/timelines/home")
3441 [reblogged_activity] = json_response(conn3, 200)
3443 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3445 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3446 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3449 describe "create account by app" do
3450 test "Account registration via Application", %{conn: conn} do
3453 |> post("/api/v1/apps", %{
3454 client_name: "client_name",
3455 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3456 scopes: "read, write, follow"
3460 "client_id" => client_id,
3461 "client_secret" => client_secret,
3463 "name" => "client_name",
3464 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3467 } = json_response(conn, 200)
3471 |> post("/oauth/token", %{
3472 grant_type: "client_credentials",
3473 client_id: client_id,
3474 client_secret: client_secret
3477 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3478 json_response(conn, 200)
3481 token_from_db = Repo.get_by(Token, token: token)
3482 assert token_from_db
3484 assert scope == "read write follow"
3488 |> put_req_header("authorization", "Bearer " <> token)
3489 |> post("/api/v1/accounts", %{
3491 email: "lain@example.org",
3492 password: "PlzDontHackLain",
3497 "access_token" => token,
3498 "created_at" => _created_at,
3500 "token_type" => "Bearer"
3501 } = json_response(conn, 200)
3503 token_from_db = Repo.get_by(Token, token: token)
3504 assert token_from_db
3505 token_from_db = Repo.preload(token_from_db, :user)
3506 assert token_from_db.user
3508 assert token_from_db.user.info.confirmation_pending
3511 test "rate limit", %{conn: conn} do
3512 app_token = insert(:oauth_token, user: nil)
3515 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3516 |> Map.put(:remote_ip, {15, 15, 15, 15})
3521 |> post("/api/v1/accounts", %{
3522 username: "#{i}lain",
3523 email: "#{i}lain@example.org",
3524 password: "PlzDontHackLain",
3529 "access_token" => token,
3530 "created_at" => _created_at,
3532 "token_type" => "Bearer"
3533 } = json_response(conn, 200)
3535 token_from_db = Repo.get_by(Token, token: token)
3536 assert token_from_db
3537 token_from_db = Repo.preload(token_from_db, :user)
3538 assert token_from_db.user
3540 assert token_from_db.user.info.confirmation_pending
3545 |> post("/api/v1/accounts", %{
3547 email: "6lain@example.org",
3548 password: "PlzDontHackLain",
3552 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3556 describe "GET /api/v1/polls/:id" do
3557 test "returns poll entity for object id", %{conn: conn} do
3558 user = insert(:user)
3561 CommonAPI.post(user, %{
3562 "status" => "Pleroma does",
3563 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3566 object = Object.normalize(activity)
3570 |> assign(:user, user)
3571 |> get("/api/v1/polls/#{object.id}")
3573 response = json_response(conn, 200)
3574 id = to_string(object.id)
3575 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3578 test "does not expose polls for private statuses", %{conn: conn} do
3579 user = insert(:user)
3580 other_user = insert(:user)
3583 CommonAPI.post(user, %{
3584 "status" => "Pleroma does",
3585 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3586 "visibility" => "private"
3589 object = Object.normalize(activity)
3593 |> assign(:user, other_user)
3594 |> get("/api/v1/polls/#{object.id}")
3596 assert json_response(conn, 404)
3600 describe "POST /api/v1/polls/:id/votes" do
3601 test "votes are added to the poll", %{conn: conn} do
3602 user = insert(:user)
3603 other_user = insert(:user)
3606 CommonAPI.post(user, %{
3607 "status" => "A very delicious sandwich",
3609 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3615 object = Object.normalize(activity)
3619 |> assign(:user, other_user)
3620 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3622 assert json_response(conn, 200)
3623 object = Object.get_by_id(object.id)
3625 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3630 test "author can't vote", %{conn: conn} do
3631 user = insert(:user)
3634 CommonAPI.post(user, %{
3635 "status" => "Am I cute?",
3636 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3639 object = Object.normalize(activity)
3642 |> assign(:user, user)
3643 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3644 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3646 object = Object.get_by_id(object.id)
3648 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3651 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3652 user = insert(:user)
3653 other_user = insert(:user)
3656 CommonAPI.post(user, %{
3657 "status" => "The glass is",
3658 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3661 object = Object.normalize(activity)
3664 |> assign(:user, other_user)
3665 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3666 |> json_response(422) == %{"error" => "Too many choices"}
3668 object = Object.get_by_id(object.id)
3670 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3675 test "does not allow choice index to be greater than options count", %{conn: conn} do
3676 user = insert(:user)
3677 other_user = insert(:user)
3680 CommonAPI.post(user, %{
3681 "status" => "Am I cute?",
3682 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3685 object = Object.normalize(activity)
3689 |> assign(:user, other_user)
3690 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3692 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3695 test "returns 404 error when object is not exist", %{conn: conn} do
3696 user = insert(:user)
3700 |> assign(:user, user)
3701 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3703 assert json_response(conn, 404) == %{"error" => "Record not found"}
3706 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3707 user = insert(:user)
3708 other_user = insert(:user)
3711 CommonAPI.post(user, %{
3712 "status" => "Am I cute?",
3713 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3714 "visibility" => "private"
3717 object = Object.normalize(activity)
3721 |> assign(:user, other_user)
3722 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3724 assert json_response(conn, 404) == %{"error" => "Record not found"}
3728 describe "GET /api/v1/statuses/:id/favourited_by" do
3730 user = insert(:user)
3731 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3735 |> assign(:user, user)
3737 [conn: conn, activity: activity]
3740 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3741 other_user = insert(:user)
3742 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3746 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3747 |> json_response(:ok)
3749 [%{"id" => id}] = response
3751 assert id == other_user.id
3754 test "returns empty array when status has not been favorited yet", %{
3760 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3761 |> json_response(:ok)
3763 assert Enum.empty?(response)
3766 test "does not return users who have favorited the status but are blocked", %{
3767 conn: %{assigns: %{user: user}} = conn,
3770 other_user = insert(:user)
3771 {:ok, user} = User.block(user, other_user)
3773 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3777 |> assign(:user, user)
3778 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3779 |> json_response(:ok)
3781 assert Enum.empty?(response)
3784 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3785 other_user = insert(:user)
3786 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3790 |> assign(:user, nil)
3791 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3792 |> json_response(:ok)
3794 [%{"id" => id}] = response
3795 assert id == other_user.id
3799 describe "GET /api/v1/statuses/:id/reblogged_by" do
3801 user = insert(:user)
3802 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3806 |> assign(:user, user)
3808 [conn: conn, activity: activity]
3811 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3812 other_user = insert(:user)
3813 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3817 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3818 |> json_response(:ok)
3820 [%{"id" => id}] = response
3822 assert id == other_user.id
3825 test "returns empty array when status has not been reblogged yet", %{
3831 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3832 |> json_response(:ok)
3834 assert Enum.empty?(response)
3837 test "does not return users who have reblogged the status but are blocked", %{
3838 conn: %{assigns: %{user: user}} = conn,
3841 other_user = insert(:user)
3842 {:ok, user} = User.block(user, other_user)
3844 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3848 |> assign(:user, user)
3849 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3850 |> json_response(:ok)
3852 assert Enum.empty?(response)
3855 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3856 other_user = insert(:user)
3857 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3861 |> assign(:user, nil)
3862 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3863 |> json_response(:ok)
3865 [%{"id" => id}] = response
3866 assert id == other_user.id
3870 describe "POST /auth/password, with valid parameters" do
3871 setup %{conn: conn} do
3872 user = insert(:user)
3873 conn = post(conn, "/auth/password?email=#{user.email}")
3874 %{conn: conn, user: user}
3877 test "it returns 204", %{conn: conn} do
3878 assert json_response(conn, :no_content)
3881 test "it creates a PasswordResetToken record for user", %{user: user} do
3882 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3886 test "it sends an email to user", %{user: user} do
3887 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3889 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3890 notify_email = Config.get([:instance, :notify_email])
3891 instance_name = Config.get([:instance, :name])
3894 from: {instance_name, notify_email},
3895 to: {user.name, user.email},
3896 html_body: email.html_body
3901 describe "POST /auth/password, with invalid parameters" do
3903 user = insert(:user)
3907 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3908 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3909 assert conn.status == 404
3910 assert conn.resp_body == ""
3913 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3914 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3915 conn = post(conn, "/auth/password?email=#{user.email}")
3916 assert conn.status == 400
3917 assert conn.resp_body == ""
3921 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3923 user = insert(:user)
3924 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3928 |> Changeset.change()
3929 |> Changeset.put_embed(:info, info_change)
3932 assert user.info.confirmation_pending
3937 clear_config([:instance, :account_activation_required]) do
3938 Config.put([:instance, :account_activation_required], true)
3941 test "resend account confirmation email", %{conn: conn, user: user} do
3943 |> assign(:user, user)
3944 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3945 |> json_response(:no_content)
3947 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3948 notify_email = Config.get([:instance, :notify_email])
3949 instance_name = Config.get([:instance, :name])
3952 from: {instance_name, notify_email},
3953 to: {user.name, user.email},
3954 html_body: email.html_body
3959 describe "GET /api/v1/suggestions" do
3961 user = insert(:user)
3962 other_user = insert(:user)
3963 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3964 url500 = "http://test500?#{host}&#{user.nickname}"
3965 url200 = "http://test200?#{host}&#{user.nickname}"
3968 %{method: :get, url: ^url500} ->
3969 %Tesla.Env{status: 500, body: "bad request"}
3971 %{method: :get, url: ^url200} ->
3975 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3977 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3981 [user: user, other_user: other_user]
3984 clear_config(:suggestions)
3986 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3987 Config.put([:suggestions, :enabled], false)
3991 |> assign(:user, user)
3992 |> get("/api/v1/suggestions")
3993 |> json_response(200)
3998 test "returns error", %{conn: conn, user: user} do
3999 Config.put([:suggestions, :enabled], true)
4000 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4004 |> assign(:user, user)
4005 |> get("/api/v1/suggestions")
4006 |> json_response(500)
4008 assert res == "Something went wrong"
4011 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4012 Config.put([:suggestions, :enabled], true)
4013 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4017 |> assign(:user, user)
4018 |> get("/api/v1/suggestions")
4019 |> json_response(200)
4024 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4025 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4029 "acct" => other_user.ap_id,
4030 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4031 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4032 "id" => other_user.id