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
11 alias Pleroma.Notification
14 alias Pleroma.ScheduledActivity
15 alias Pleroma.Tests.ObanHelpers
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 alias Pleroma.Web.TwitterAPI.TwitterAPI
25 import Pleroma.Factory
26 import ExUnit.CaptureLog
28 import Swoosh.TestAssertions
30 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
33 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
37 clear_config([:instance, :public])
38 clear_config([:rich_media, :enabled])
40 test "the home timeline", %{conn: conn} do
42 following = insert(:user)
44 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
51 assert Enum.empty?(json_response(conn, 200))
53 {:ok, user} = User.follow(user, following)
57 |> assign(:user, user)
58 |> get("/api/v1/timelines/home")
60 assert [%{"content" => "test"}] = json_response(conn, 200)
63 test "the public timeline", %{conn: conn} do
64 following = insert(:user)
67 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
70 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
74 |> get("/api/v1/timelines/public", %{"local" => "False"})
76 assert length(json_response(conn, 200)) == 2
80 |> get("/api/v1/timelines/public", %{"local" => "True"})
82 assert [%{"content" => "test"}] = json_response(conn, 200)
86 |> get("/api/v1/timelines/public", %{"local" => "1"})
88 assert [%{"content" => "test"}] = json_response(conn, 200)
92 test "the public timeline when public is set to false", %{conn: conn} do
93 Config.put([:instance, :public], false)
96 |> get("/api/v1/timelines/public", %{"local" => "False"})
97 |> json_response(403) == %{"error" => "This resource requires authentication."}
100 describe "posting statuses" do
106 |> assign(:user, user)
111 test "posting a status", %{conn: conn} do
112 idempotency_key = "Pikachu rocks!"
116 |> put_req_header("idempotency-key", idempotency_key)
117 |> post("/api/v1/statuses", %{
119 "spoiler_text" => "2hu",
120 "sensitive" => "false"
123 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
125 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
127 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
128 json_response(conn_one, 200)
130 assert Activity.get_by_id(id)
134 |> put_req_header("idempotency-key", idempotency_key)
135 |> post("/api/v1/statuses", %{
137 "spoiler_text" => "2hu",
138 "sensitive" => "false"
141 assert %{"id" => second_id} = json_response(conn_two, 200)
142 assert id == second_id
146 |> post("/api/v1/statuses", %{
148 "spoiler_text" => "2hu",
149 "sensitive" => "false"
152 assert %{"id" => third_id} = json_response(conn_three, 200)
153 refute id == third_id
156 test "replying to a status", %{conn: conn} do
158 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
162 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
164 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
166 activity = Activity.get_by_id(id)
168 assert activity.data["context"] == replied_to.data["context"]
169 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
172 test "replying to a direct message with visibility other than direct", %{conn: conn} do
174 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
176 Enum.each(["public", "private", "unlisted"], fn visibility ->
179 |> post("/api/v1/statuses", %{
180 "status" => "@#{user.nickname} hey",
181 "in_reply_to_id" => replied_to.id,
182 "visibility" => visibility
185 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
189 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
192 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
194 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
195 assert Activity.get_by_id(id)
198 test "posting a sensitive status", %{conn: conn} do
201 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
203 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
204 assert Activity.get_by_id(id)
207 test "posting a fake status", %{conn: conn} do
210 |> post("/api/v1/statuses", %{
212 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
215 real_status = json_response(real_conn, 200)
218 assert Object.get_by_ap_id(real_status["uri"])
222 |> Map.put("id", nil)
223 |> Map.put("url", nil)
224 |> Map.put("uri", nil)
225 |> Map.put("created_at", nil)
226 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
230 |> post("/api/v1/statuses", %{
232 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
236 fake_status = json_response(fake_conn, 200)
239 refute Object.get_by_ap_id(fake_status["uri"])
243 |> Map.put("id", nil)
244 |> Map.put("url", nil)
245 |> Map.put("uri", nil)
246 |> Map.put("created_at", nil)
247 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
249 assert real_status == fake_status
252 test "posting a status with OGP link preview", %{conn: conn} do
253 Config.put([:rich_media, :enabled], true)
257 |> post("/api/v1/statuses", %{
258 "status" => "https://example.com/ogp"
261 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
262 assert Activity.get_by_id(id)
265 test "posting a direct status", %{conn: conn} do
266 user2 = insert(:user)
267 content = "direct cofe @#{user2.nickname}"
271 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
273 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
274 assert activity = Activity.get_by_id(id)
275 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
276 assert activity.data["to"] == [user2.ap_id]
277 assert activity.data["cc"] == []
281 describe "posting polls" do
282 test "posting a poll", %{conn: conn} do
284 time = NaiveDateTime.utc_now()
288 |> assign(:user, user)
289 |> post("/api/v1/statuses", %{
290 "status" => "Who is the #bestgrill?",
291 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
294 response = json_response(conn, 200)
296 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
297 title in ["Rei", "Asuka", "Misato"]
300 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
301 refute response["poll"]["expred"]
304 test "option limit is enforced", %{conn: conn} do
306 limit = Config.get([:instance, :poll_limits, :max_options])
310 |> assign(:user, user)
311 |> post("/api/v1/statuses", %{
313 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
316 %{"error" => error} = json_response(conn, 422)
317 assert error == "Poll can't contain more than #{limit} options"
320 test "option character limit is enforced", %{conn: conn} do
322 limit = Config.get([:instance, :poll_limits, :max_option_chars])
326 |> assign(:user, user)
327 |> post("/api/v1/statuses", %{
330 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
335 %{"error" => error} = json_response(conn, 422)
336 assert error == "Poll options cannot be longer than #{limit} characters each"
339 test "minimal date limit is enforced", %{conn: conn} do
341 limit = Config.get([:instance, :poll_limits, :min_expiration])
345 |> assign(:user, user)
346 |> post("/api/v1/statuses", %{
347 "status" => "imagine arbitrary limits",
349 "options" => ["this post was made by pleroma gang"],
350 "expires_in" => limit - 1
354 %{"error" => error} = json_response(conn, 422)
355 assert error == "Expiration date is too soon"
358 test "maximum date limit is enforced", %{conn: conn} do
360 limit = Config.get([:instance, :poll_limits, :max_expiration])
364 |> assign(:user, user)
365 |> post("/api/v1/statuses", %{
366 "status" => "imagine arbitrary limits",
368 "options" => ["this post was made by pleroma gang"],
369 "expires_in" => limit + 1
373 %{"error" => error} = json_response(conn, 422)
374 assert error == "Expiration date is too far in the future"
378 test "direct timeline", %{conn: conn} do
379 user_one = insert(:user)
380 user_two = insert(:user)
382 {:ok, user_two} = User.follow(user_two, user_one)
385 CommonAPI.post(user_one, %{
386 "status" => "Hi @#{user_two.nickname}!",
387 "visibility" => "direct"
390 {:ok, _follower_only} =
391 CommonAPI.post(user_one, %{
392 "status" => "Hi @#{user_two.nickname}!",
393 "visibility" => "private"
396 # Only direct should be visible here
399 |> assign(:user, user_two)
400 |> get("api/v1/timelines/direct")
402 [status] = json_response(res_conn, 200)
404 assert %{"visibility" => "direct"} = status
405 assert status["url"] != direct.data["id"]
407 # User should be able to see his own direct message
410 |> assign(:user, user_one)
411 |> get("api/v1/timelines/direct")
413 [status] = json_response(res_conn, 200)
415 assert %{"visibility" => "direct"} = status
417 # Both should be visible here
420 |> assign(:user, user_two)
421 |> get("api/v1/timelines/home")
423 [_s1, _s2] = json_response(res_conn, 200)
426 Enum.each(1..20, fn _ ->
428 CommonAPI.post(user_one, %{
429 "status" => "Hi @#{user_two.nickname}!",
430 "visibility" => "direct"
436 |> assign(:user, user_two)
437 |> get("api/v1/timelines/direct")
439 statuses = json_response(res_conn, 200)
440 assert length(statuses) == 20
444 |> assign(:user, user_two)
445 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
447 [status] = json_response(res_conn, 200)
449 assert status["url"] != direct.data["id"]
452 test "Conversations", %{conn: conn} do
453 user_one = insert(:user)
454 user_two = insert(:user)
455 user_three = insert(:user)
457 {:ok, user_two} = User.follow(user_two, user_one)
460 CommonAPI.post(user_one, %{
461 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
462 "visibility" => "direct"
465 {:ok, _follower_only} =
466 CommonAPI.post(user_one, %{
467 "status" => "Hi @#{user_two.nickname}!",
468 "visibility" => "private"
473 |> assign(:user, user_one)
474 |> get("/api/v1/conversations")
476 assert response = json_response(res_conn, 200)
481 "accounts" => res_accounts,
482 "last_status" => res_last_status,
487 account_ids = Enum.map(res_accounts, & &1["id"])
488 assert length(res_accounts) == 2
489 assert user_two.id in account_ids
490 assert user_three.id in account_ids
491 assert is_binary(res_id)
492 assert unread == true
493 assert res_last_status["id"] == direct.id
495 # Apparently undocumented API endpoint
498 |> assign(:user, user_one)
499 |> post("/api/v1/conversations/#{res_id}/read")
501 assert response = json_response(res_conn, 200)
502 assert length(response["accounts"]) == 2
503 assert response["last_status"]["id"] == direct.id
504 assert response["unread"] == false
506 # (vanilla) Mastodon frontend behaviour
509 |> assign(:user, user_one)
510 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
512 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
515 test "doesn't include DMs from blocked users", %{conn: conn} do
516 blocker = insert(:user)
517 blocked = insert(:user)
519 {:ok, blocker} = User.block(blocker, blocked)
521 {:ok, _blocked_direct} =
522 CommonAPI.post(blocked, %{
523 "status" => "Hi @#{blocker.nickname}!",
524 "visibility" => "direct"
528 CommonAPI.post(user, %{
529 "status" => "Hi @#{blocker.nickname}!",
530 "visibility" => "direct"
535 |> assign(:user, user)
536 |> get("api/v1/timelines/direct")
538 [status] = json_response(res_conn, 200)
539 assert status["id"] == direct.id
542 test "verify_credentials", %{conn: conn} do
547 |> assign(:user, user)
548 |> get("/api/v1/accounts/verify_credentials")
550 response = json_response(conn, 200)
552 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
553 assert response["pleroma"]["chat_token"]
554 assert id == to_string(user.id)
557 test "verify_credentials default scope unlisted", %{conn: conn} do
558 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
562 |> assign(:user, user)
563 |> get("/api/v1/accounts/verify_credentials")
565 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
566 assert id == to_string(user.id)
569 test "apps/verify_credentials", %{conn: conn} do
570 token = insert(:oauth_token)
574 |> assign(:user, token.user)
575 |> assign(:token, token)
576 |> get("/api/v1/apps/verify_credentials")
578 app = Repo.preload(token, :app).app
581 "name" => app.client_name,
582 "website" => app.website,
583 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
586 assert expected == json_response(conn, 200)
589 test "user avatar can be set", %{conn: conn} do
591 avatar_image = File.read!("test/fixtures/avatar_data_uri")
595 |> assign(:user, user)
596 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
598 user = refresh_record(user)
612 assert %{"url" => _} = json_response(conn, 200)
615 test "user avatar can be reset", %{conn: conn} do
620 |> assign(:user, user)
621 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
623 user = User.get_cached_by_id(user.id)
625 assert user.avatar == nil
627 assert %{"url" => nil} = json_response(conn, 200)
630 test "can set profile banner", %{conn: conn} do
635 |> assign(:user, user)
636 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
638 user = refresh_record(user)
639 assert user.info.banner["type"] == "Image"
641 assert %{"url" => _} = json_response(conn, 200)
644 test "can reset profile banner", %{conn: conn} do
649 |> assign(:user, user)
650 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
652 user = refresh_record(user)
653 assert user.info.banner == %{}
655 assert %{"url" => nil} = json_response(conn, 200)
658 test "background image can be set", %{conn: conn} do
663 |> assign(:user, user)
664 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
666 user = refresh_record(user)
667 assert user.info.background["type"] == "Image"
668 assert %{"url" => _} = json_response(conn, 200)
671 test "background image can be reset", %{conn: conn} do
676 |> assign(:user, user)
677 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
679 user = refresh_record(user)
680 assert user.info.background == %{}
681 assert %{"url" => nil} = json_response(conn, 200)
684 test "creates an oauth app", %{conn: conn} do
686 app_attrs = build(:oauth_app)
690 |> assign(:user, user)
691 |> post("/api/v1/apps", %{
692 client_name: app_attrs.client_name,
693 redirect_uris: app_attrs.redirect_uris
696 [app] = Repo.all(App)
699 "name" => app.client_name,
700 "website" => app.website,
701 "client_id" => app.client_id,
702 "client_secret" => app.client_secret,
703 "id" => app.id |> to_string(),
704 "redirect_uri" => app.redirect_uris,
705 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
708 assert expected == json_response(conn, 200)
711 test "get a status", %{conn: conn} do
712 activity = insert(:note_activity)
716 |> get("/api/v1/statuses/#{activity.id}")
718 assert %{"id" => id} = json_response(conn, 200)
719 assert id == to_string(activity.id)
722 describe "deleting a status" do
723 test "when you created it", %{conn: conn} do
724 activity = insert(:note_activity)
725 author = User.get_cached_by_ap_id(activity.data["actor"])
729 |> assign(:user, author)
730 |> delete("/api/v1/statuses/#{activity.id}")
732 assert %{} = json_response(conn, 200)
734 refute Activity.get_by_id(activity.id)
737 test "when you didn't create it", %{conn: conn} do
738 activity = insert(:note_activity)
743 |> assign(:user, user)
744 |> delete("/api/v1/statuses/#{activity.id}")
746 assert %{"error" => _} = json_response(conn, 403)
748 assert Activity.get_by_id(activity.id) == activity
751 test "when you're an admin or moderator", %{conn: conn} do
752 activity1 = insert(:note_activity)
753 activity2 = insert(:note_activity)
754 admin = insert(:user, info: %{is_admin: true})
755 moderator = insert(:user, info: %{is_moderator: true})
759 |> assign(:user, admin)
760 |> delete("/api/v1/statuses/#{activity1.id}")
762 assert %{} = json_response(res_conn, 200)
766 |> assign(:user, moderator)
767 |> delete("/api/v1/statuses/#{activity2.id}")
769 assert %{} = json_response(res_conn, 200)
771 refute Activity.get_by_id(activity1.id)
772 refute Activity.get_by_id(activity2.id)
776 describe "filters" do
777 test "creating a filter", %{conn: conn} do
780 filter = %Pleroma.Filter{
787 |> assign(:user, user)
788 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
790 assert response = json_response(conn, 200)
791 assert response["phrase"] == filter.phrase
792 assert response["context"] == filter.context
793 assert response["irreversible"] == false
794 assert response["id"] != nil
795 assert response["id"] != ""
798 test "fetching a list of filters", %{conn: conn} do
801 query_one = %Pleroma.Filter{
808 query_two = %Pleroma.Filter{
815 {:ok, filter_one} = Pleroma.Filter.create(query_one)
816 {:ok, filter_two} = Pleroma.Filter.create(query_two)
820 |> assign(:user, user)
821 |> get("/api/v1/filters")
822 |> json_response(200)
828 filters: [filter_two, filter_one]
832 test "get a filter", %{conn: conn} do
835 query = %Pleroma.Filter{
842 {:ok, filter} = Pleroma.Filter.create(query)
846 |> assign(:user, user)
847 |> get("/api/v1/filters/#{filter.filter_id}")
849 assert _response = json_response(conn, 200)
852 test "update a filter", %{conn: conn} do
855 query = %Pleroma.Filter{
862 {:ok, _filter} = Pleroma.Filter.create(query)
864 new = %Pleroma.Filter{
871 |> assign(:user, user)
872 |> put("/api/v1/filters/#{query.filter_id}", %{
877 assert response = json_response(conn, 200)
878 assert response["phrase"] == new.phrase
879 assert response["context"] == new.context
882 test "delete a filter", %{conn: conn} do
885 query = %Pleroma.Filter{
892 {:ok, filter} = Pleroma.Filter.create(query)
896 |> assign(:user, user)
897 |> delete("/api/v1/filters/#{filter.filter_id}")
899 assert response = json_response(conn, 200)
900 assert response == %{}
905 test "creating a list", %{conn: conn} do
910 |> assign(:user, user)
911 |> post("/api/v1/lists", %{"title" => "cuties"})
913 assert %{"title" => title} = json_response(conn, 200)
914 assert title == "cuties"
917 test "adding users to a list", %{conn: conn} do
919 other_user = insert(:user)
920 {:ok, list} = Pleroma.List.create("name", user)
924 |> assign(:user, user)
925 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
927 assert %{} == json_response(conn, 200)
928 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
929 assert following == [other_user.follower_address]
932 test "removing users from a list", %{conn: conn} do
934 other_user = insert(:user)
935 third_user = insert(:user)
936 {:ok, list} = Pleroma.List.create("name", user)
937 {:ok, list} = Pleroma.List.follow(list, other_user)
938 {:ok, list} = Pleroma.List.follow(list, third_user)
942 |> assign(:user, user)
943 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
945 assert %{} == json_response(conn, 200)
946 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
947 assert following == [third_user.follower_address]
950 test "listing users in a list", %{conn: conn} do
952 other_user = insert(:user)
953 {:ok, list} = Pleroma.List.create("name", user)
954 {:ok, list} = Pleroma.List.follow(list, other_user)
958 |> assign(:user, user)
959 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
961 assert [%{"id" => id}] = json_response(conn, 200)
962 assert id == to_string(other_user.id)
965 test "retrieving a list", %{conn: conn} do
967 {:ok, list} = Pleroma.List.create("name", user)
971 |> assign(:user, user)
972 |> get("/api/v1/lists/#{list.id}")
974 assert %{"id" => id} = json_response(conn, 200)
975 assert id == to_string(list.id)
978 test "renaming a list", %{conn: conn} do
980 {:ok, list} = Pleroma.List.create("name", user)
984 |> assign(:user, user)
985 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
987 assert %{"title" => name} = json_response(conn, 200)
988 assert name == "newname"
991 test "deleting a list", %{conn: conn} do
993 {:ok, list} = Pleroma.List.create("name", user)
997 |> assign(:user, user)
998 |> delete("/api/v1/lists/#{list.id}")
1000 assert %{} = json_response(conn, 200)
1001 assert is_nil(Repo.get(Pleroma.List, list.id))
1004 test "list timeline", %{conn: conn} do
1005 user = insert(:user)
1006 other_user = insert(:user)
1007 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
1008 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1009 {:ok, list} = Pleroma.List.create("name", user)
1010 {:ok, list} = Pleroma.List.follow(list, other_user)
1014 |> assign(:user, user)
1015 |> get("/api/v1/timelines/list/#{list.id}")
1017 assert [%{"id" => id}] = json_response(conn, 200)
1019 assert id == to_string(activity_two.id)
1022 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1023 user = insert(:user)
1024 other_user = insert(:user)
1025 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
1027 {:ok, _activity_two} =
1028 CommonAPI.post(other_user, %{
1029 "status" => "Marisa is cute.",
1030 "visibility" => "private"
1033 {:ok, list} = Pleroma.List.create("name", user)
1034 {:ok, list} = Pleroma.List.follow(list, other_user)
1038 |> assign(:user, user)
1039 |> get("/api/v1/timelines/list/#{list.id}")
1041 assert [%{"id" => id}] = json_response(conn, 200)
1043 assert id == to_string(activity_one.id)
1047 describe "notifications" do
1048 test "list of notifications", %{conn: conn} do
1049 user = insert(:user)
1050 other_user = insert(:user)
1052 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1054 {:ok, [_notification]} = Notification.create_notifications(activity)
1058 |> assign(:user, user)
1059 |> get("/api/v1/notifications")
1062 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1064 }\">@<span>#{user.nickname}</span></a></span>"
1066 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1067 assert response == expected_response
1070 test "getting a single notification", %{conn: conn} do
1071 user = insert(:user)
1072 other_user = insert(:user)
1074 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1076 {:ok, [notification]} = Notification.create_notifications(activity)
1080 |> assign(:user, user)
1081 |> get("/api/v1/notifications/#{notification.id}")
1084 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1086 }\">@<span>#{user.nickname}</span></a></span>"
1088 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1089 assert response == expected_response
1092 test "dismissing a single notification", %{conn: conn} do
1093 user = insert(:user)
1094 other_user = insert(:user)
1096 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1098 {:ok, [notification]} = Notification.create_notifications(activity)
1102 |> assign(:user, user)
1103 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1105 assert %{} = json_response(conn, 200)
1108 test "clearing all notifications", %{conn: conn} do
1109 user = insert(:user)
1110 other_user = insert(:user)
1112 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1114 {:ok, [_notification]} = Notification.create_notifications(activity)
1118 |> assign(:user, user)
1119 |> post("/api/v1/notifications/clear")
1121 assert %{} = json_response(conn, 200)
1125 |> assign(:user, user)
1126 |> get("/api/v1/notifications")
1128 assert all = json_response(conn, 200)
1132 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1133 user = insert(:user)
1134 other_user = insert(:user)
1136 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1137 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1138 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1139 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1141 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1142 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1143 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1144 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1148 |> assign(:user, user)
1153 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1155 result = json_response(conn_res, 200)
1156 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1161 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1163 result = json_response(conn_res, 200)
1164 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1169 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1171 result = json_response(conn_res, 200)
1172 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1175 test "filters notifications using exclude_types", %{conn: conn} do
1176 user = insert(:user)
1177 other_user = insert(:user)
1179 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1180 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1181 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1182 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1183 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1185 mention_notification_id =
1186 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1188 favorite_notification_id =
1189 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1191 reblog_notification_id =
1192 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1194 follow_notification_id =
1195 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1199 |> assign(:user, user)
1202 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1204 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1207 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1209 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1212 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1214 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1217 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1219 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1222 test "destroy multiple", %{conn: conn} do
1223 user = insert(:user)
1224 other_user = insert(:user)
1226 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1227 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1228 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1229 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1231 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1232 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1233 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1234 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1238 |> assign(:user, user)
1242 |> get("/api/v1/notifications")
1244 result = json_response(conn_res, 200)
1245 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1249 |> assign(:user, other_user)
1253 |> get("/api/v1/notifications")
1255 result = json_response(conn_res, 200)
1256 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1260 |> delete("/api/v1/notifications/destroy_multiple", %{
1261 "ids" => [notification1_id, notification2_id]
1264 assert json_response(conn_destroy, 200) == %{}
1268 |> get("/api/v1/notifications")
1270 result = json_response(conn_res, 200)
1271 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1274 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1275 user = insert(:user)
1276 user2 = insert(:user)
1278 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1279 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1281 conn = assign(conn, :user, user)
1283 conn = get(conn, "/api/v1/notifications")
1285 assert length(json_response(conn, 200)) == 1
1287 {:ok, user} = User.mute(user, user2)
1289 conn = assign(build_conn(), :user, user)
1290 conn = get(conn, "/api/v1/notifications")
1292 assert json_response(conn, 200) == []
1295 test "see notifications after muting user without notifications", %{conn: conn} do
1296 user = insert(:user)
1297 user2 = insert(:user)
1299 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1300 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1302 conn = assign(conn, :user, user)
1304 conn = get(conn, "/api/v1/notifications")
1306 assert length(json_response(conn, 200)) == 1
1308 {:ok, user} = User.mute(user, user2, false)
1310 conn = assign(build_conn(), :user, user)
1311 conn = get(conn, "/api/v1/notifications")
1313 assert length(json_response(conn, 200)) == 1
1316 test "see notifications after muting user with notifications and with_muted parameter", %{
1319 user = insert(:user)
1320 user2 = insert(:user)
1322 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1323 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1325 conn = assign(conn, :user, user)
1327 conn = get(conn, "/api/v1/notifications")
1329 assert length(json_response(conn, 200)) == 1
1331 {:ok, user} = User.mute(user, user2)
1333 conn = assign(build_conn(), :user, user)
1334 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1336 assert length(json_response(conn, 200)) == 1
1340 describe "reblogging" do
1341 test "reblogs and returns the reblogged status", %{conn: conn} do
1342 activity = insert(:note_activity)
1343 user = insert(:user)
1347 |> assign(:user, user)
1348 |> post("/api/v1/statuses/#{activity.id}/reblog")
1351 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1353 } = json_response(conn, 200)
1355 assert to_string(activity.id) == id
1358 test "reblogged status for another user", %{conn: conn} do
1359 activity = insert(:note_activity)
1360 user1 = insert(:user)
1361 user2 = insert(:user)
1362 user3 = insert(:user)
1363 CommonAPI.favorite(activity.id, user2)
1364 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1365 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1366 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1370 |> assign(:user, user3)
1371 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1374 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1375 "reblogged" => false,
1376 "favourited" => false,
1377 "bookmarked" => false
1378 } = json_response(conn_res, 200)
1382 |> assign(:user, user2)
1383 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1386 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1387 "reblogged" => true,
1388 "favourited" => true,
1389 "bookmarked" => true
1390 } = json_response(conn_res, 200)
1392 assert to_string(activity.id) == id
1395 test "returns 400 error when activity is not exist", %{conn: conn} do
1396 user = insert(:user)
1400 |> assign(:user, user)
1401 |> post("/api/v1/statuses/foo/reblog")
1403 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1407 describe "unreblogging" do
1408 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1409 activity = insert(:note_activity)
1410 user = insert(:user)
1412 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1416 |> assign(:user, user)
1417 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1419 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1421 assert to_string(activity.id) == id
1424 test "returns 400 error when activity is not exist", %{conn: conn} do
1425 user = insert(:user)
1429 |> assign(:user, user)
1430 |> post("/api/v1/statuses/foo/unreblog")
1432 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1436 describe "favoriting" do
1437 test "favs a status and returns it", %{conn: conn} do
1438 activity = insert(:note_activity)
1439 user = insert(:user)
1443 |> assign(:user, user)
1444 |> post("/api/v1/statuses/#{activity.id}/favourite")
1446 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1447 json_response(conn, 200)
1449 assert to_string(activity.id) == id
1452 test "returns 400 error for a wrong id", %{conn: conn} do
1453 user = insert(:user)
1457 |> assign(:user, user)
1458 |> post("/api/v1/statuses/1/favourite")
1460 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1464 describe "unfavoriting" do
1465 test "unfavorites a status and returns it", %{conn: conn} do
1466 activity = insert(:note_activity)
1467 user = insert(:user)
1469 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1473 |> assign(:user, user)
1474 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1476 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1477 json_response(conn, 200)
1479 assert to_string(activity.id) == id
1482 test "returns 400 error for a wrong id", %{conn: conn} do
1483 user = insert(:user)
1487 |> assign(:user, user)
1488 |> post("/api/v1/statuses/1/unfavourite")
1490 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1494 describe "user timelines" do
1495 test "gets a users statuses", %{conn: conn} do
1496 user_one = insert(:user)
1497 user_two = insert(:user)
1498 user_three = insert(:user)
1500 {:ok, user_three} = User.follow(user_three, user_one)
1502 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1504 {:ok, direct_activity} =
1505 CommonAPI.post(user_one, %{
1506 "status" => "Hi, @#{user_two.nickname}.",
1507 "visibility" => "direct"
1510 {:ok, private_activity} =
1511 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1515 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1517 assert [%{"id" => id}] = json_response(resp, 200)
1518 assert id == to_string(activity.id)
1522 |> assign(:user, user_two)
1523 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1525 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1526 assert id_one == to_string(direct_activity.id)
1527 assert id_two == to_string(activity.id)
1531 |> assign(:user, user_three)
1532 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1534 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1535 assert id_one == to_string(private_activity.id)
1536 assert id_two == to_string(activity.id)
1539 test "unimplemented pinned statuses feature", %{conn: conn} do
1540 note = insert(:note_activity)
1541 user = User.get_cached_by_ap_id(note.data["actor"])
1545 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1547 assert json_response(conn, 200) == []
1550 test "gets an users media", %{conn: conn} do
1551 note = insert(:note_activity)
1552 user = User.get_cached_by_ap_id(note.data["actor"])
1554 file = %Plug.Upload{
1555 content_type: "image/jpg",
1556 path: Path.absname("test/fixtures/image.jpg"),
1557 filename: "an_image.jpg"
1561 TwitterAPI.upload(file, user, "json")
1565 CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1569 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1571 assert [%{"id" => id}] = json_response(conn, 200)
1572 assert id == to_string(image_post.id)
1576 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1578 assert [%{"id" => id}] = json_response(conn, 200)
1579 assert id == to_string(image_post.id)
1582 test "gets a user's statuses without reblogs", %{conn: conn} do
1583 user = insert(:user)
1584 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1585 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1589 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1591 assert [%{"id" => id}] = json_response(conn, 200)
1592 assert id == to_string(post.id)
1596 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1598 assert [%{"id" => id}] = json_response(conn, 200)
1599 assert id == to_string(post.id)
1602 test "filters user's statuses by a hashtag", %{conn: conn} do
1603 user = insert(:user)
1604 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1605 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1609 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1611 assert [%{"id" => id}] = json_response(conn, 200)
1612 assert id == to_string(post.id)
1616 describe "user relationships" do
1617 test "returns the relationships for the current user", %{conn: conn} do
1618 user = insert(:user)
1619 other_user = insert(:user)
1620 {:ok, user} = User.follow(user, other_user)
1624 |> assign(:user, user)
1625 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1627 assert [relationship] = json_response(conn, 200)
1629 assert to_string(other_user.id) == relationship["id"]
1633 describe "media upload" do
1635 user = insert(:user)
1639 |> assign(:user, user)
1641 image = %Plug.Upload{
1642 content_type: "image/jpg",
1643 path: Path.absname("test/fixtures/image.jpg"),
1644 filename: "an_image.jpg"
1647 [conn: conn, image: image]
1650 clear_config([:media_proxy])
1651 clear_config([Pleroma.Upload])
1653 test "returns uploaded image", %{conn: conn, image: image} do
1654 desc = "Description of the image"
1658 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1659 |> json_response(:ok)
1661 assert media["type"] == "image"
1662 assert media["description"] == desc
1665 object = Repo.get(Object, media["id"])
1666 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1670 describe "locked accounts" do
1671 test "/api/v1/follow_requests works" do
1672 user = insert(:user, %{info: %User.Info{locked: true}})
1673 other_user = insert(:user)
1675 {:ok, _activity} = ActivityPub.follow(other_user, user)
1677 user = User.get_cached_by_id(user.id)
1678 other_user = User.get_cached_by_id(other_user.id)
1680 assert User.following?(other_user, user) == false
1684 |> assign(:user, user)
1685 |> get("/api/v1/follow_requests")
1687 assert [relationship] = json_response(conn, 200)
1688 assert to_string(other_user.id) == relationship["id"]
1691 test "/api/v1/follow_requests/:id/authorize works" do
1692 user = insert(:user, %{info: %User.Info{locked: true}})
1693 other_user = insert(:user)
1695 {:ok, _activity} = ActivityPub.follow(other_user, user)
1697 user = User.get_cached_by_id(user.id)
1698 other_user = User.get_cached_by_id(other_user.id)
1700 assert User.following?(other_user, user) == false
1704 |> assign(:user, user)
1705 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1707 assert relationship = json_response(conn, 200)
1708 assert to_string(other_user.id) == relationship["id"]
1710 user = User.get_cached_by_id(user.id)
1711 other_user = User.get_cached_by_id(other_user.id)
1713 assert User.following?(other_user, user) == true
1716 test "verify_credentials", %{conn: conn} do
1717 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1721 |> assign(:user, user)
1722 |> get("/api/v1/accounts/verify_credentials")
1724 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1725 assert id == to_string(user.id)
1728 test "/api/v1/follow_requests/:id/reject works" do
1729 user = insert(:user, %{info: %User.Info{locked: true}})
1730 other_user = insert(:user)
1732 {:ok, _activity} = ActivityPub.follow(other_user, user)
1734 user = User.get_cached_by_id(user.id)
1738 |> assign(:user, user)
1739 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1741 assert relationship = json_response(conn, 200)
1742 assert to_string(other_user.id) == relationship["id"]
1744 user = User.get_cached_by_id(user.id)
1745 other_user = User.get_cached_by_id(other_user.id)
1747 assert User.following?(other_user, user) == false
1751 test "account fetching", %{conn: conn} do
1752 user = insert(:user)
1756 |> get("/api/v1/accounts/#{user.id}")
1758 assert %{"id" => id} = json_response(conn, 200)
1759 assert id == to_string(user.id)
1763 |> get("/api/v1/accounts/-1")
1765 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1768 test "account fetching also works nickname", %{conn: conn} do
1769 user = insert(:user)
1773 |> get("/api/v1/accounts/#{user.nickname}")
1775 assert %{"id" => id} = json_response(conn, 200)
1776 assert id == user.id
1779 test "mascot upload", %{conn: conn} do
1780 user = insert(:user)
1782 non_image_file = %Plug.Upload{
1783 content_type: "audio/mpeg",
1784 path: Path.absname("test/fixtures/sound.mp3"),
1785 filename: "sound.mp3"
1790 |> assign(:user, user)
1791 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1793 assert json_response(conn, 415)
1795 file = %Plug.Upload{
1796 content_type: "image/jpg",
1797 path: Path.absname("test/fixtures/image.jpg"),
1798 filename: "an_image.jpg"
1803 |> assign(:user, user)
1804 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1806 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1809 test "mascot retrieving", %{conn: conn} do
1810 user = insert(:user)
1811 # When user hasn't set a mascot, we should just get pleroma tan back
1814 |> assign(:user, user)
1815 |> get("/api/v1/pleroma/mascot")
1817 assert %{"url" => url} = json_response(conn, 200)
1818 assert url =~ "pleroma-fox-tan-smol"
1820 # When a user sets their mascot, we should get that back
1821 file = %Plug.Upload{
1822 content_type: "image/jpg",
1823 path: Path.absname("test/fixtures/image.jpg"),
1824 filename: "an_image.jpg"
1829 |> assign(:user, user)
1830 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1832 assert json_response(conn, 200)
1834 user = User.get_cached_by_id(user.id)
1838 |> assign(:user, user)
1839 |> get("/api/v1/pleroma/mascot")
1841 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1842 assert url =~ "an_image"
1845 test "hashtag timeline", %{conn: conn} do
1846 following = insert(:user)
1849 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1851 {:ok, [_activity]} =
1852 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1856 |> get("/api/v1/timelines/tag/2hu")
1858 assert [%{"id" => id}] = json_response(nconn, 200)
1860 assert id == to_string(activity.id)
1862 # works for different capitalization too
1865 |> get("/api/v1/timelines/tag/2HU")
1867 assert [%{"id" => id}] = json_response(nconn, 200)
1869 assert id == to_string(activity.id)
1873 test "multi-hashtag timeline", %{conn: conn} do
1874 user = insert(:user)
1876 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1877 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1878 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1882 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1884 [status_none, status_test1, status_test] = json_response(any_test, 200)
1886 assert to_string(activity_test.id) == status_test["id"]
1887 assert to_string(activity_test1.id) == status_test1["id"]
1888 assert to_string(activity_none.id) == status_none["id"]
1892 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1894 assert [status_test1] == json_response(restricted_test, 200)
1896 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1898 assert [status_none] == json_response(all_test, 200)
1901 test "getting followers", %{conn: conn} do
1902 user = insert(:user)
1903 other_user = insert(:user)
1904 {:ok, user} = User.follow(user, other_user)
1908 |> get("/api/v1/accounts/#{other_user.id}/followers")
1910 assert [%{"id" => id}] = json_response(conn, 200)
1911 assert id == to_string(user.id)
1914 test "getting followers, hide_followers", %{conn: conn} do
1915 user = insert(:user)
1916 other_user = insert(:user, %{info: %{hide_followers: true}})
1917 {:ok, _user} = User.follow(user, other_user)
1921 |> get("/api/v1/accounts/#{other_user.id}/followers")
1923 assert [] == json_response(conn, 200)
1926 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1927 user = insert(:user)
1928 other_user = insert(:user, %{info: %{hide_followers: true}})
1929 {:ok, _user} = User.follow(user, other_user)
1933 |> assign(:user, other_user)
1934 |> get("/api/v1/accounts/#{other_user.id}/followers")
1936 refute [] == json_response(conn, 200)
1939 test "getting followers, pagination", %{conn: conn} do
1940 user = insert(:user)
1941 follower1 = insert(:user)
1942 follower2 = insert(:user)
1943 follower3 = insert(:user)
1944 {:ok, _} = User.follow(follower1, user)
1945 {:ok, _} = User.follow(follower2, user)
1946 {:ok, _} = User.follow(follower3, user)
1950 |> assign(:user, user)
1954 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1956 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1957 assert id3 == follower3.id
1958 assert id2 == follower2.id
1962 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1964 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1965 assert id2 == follower2.id
1966 assert id1 == follower1.id
1970 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1972 assert [%{"id" => id2}] = json_response(res_conn, 200)
1973 assert id2 == follower2.id
1975 assert [link_header] = get_resp_header(res_conn, "link")
1976 assert link_header =~ ~r/min_id=#{follower2.id}/
1977 assert link_header =~ ~r/max_id=#{follower2.id}/
1980 test "getting following", %{conn: conn} do
1981 user = insert(:user)
1982 other_user = insert(:user)
1983 {:ok, user} = User.follow(user, other_user)
1987 |> get("/api/v1/accounts/#{user.id}/following")
1989 assert [%{"id" => id}] = json_response(conn, 200)
1990 assert id == to_string(other_user.id)
1993 test "getting following, hide_follows", %{conn: conn} do
1994 user = insert(:user, %{info: %{hide_follows: true}})
1995 other_user = insert(:user)
1996 {:ok, user} = User.follow(user, other_user)
2000 |> get("/api/v1/accounts/#{user.id}/following")
2002 assert [] == json_response(conn, 200)
2005 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2006 user = insert(:user, %{info: %{hide_follows: true}})
2007 other_user = insert(:user)
2008 {:ok, user} = User.follow(user, other_user)
2012 |> assign(:user, user)
2013 |> get("/api/v1/accounts/#{user.id}/following")
2015 refute [] == json_response(conn, 200)
2018 test "getting following, pagination", %{conn: conn} do
2019 user = insert(:user)
2020 following1 = insert(:user)
2021 following2 = insert(:user)
2022 following3 = insert(:user)
2023 {:ok, _} = User.follow(user, following1)
2024 {:ok, _} = User.follow(user, following2)
2025 {:ok, _} = User.follow(user, following3)
2029 |> assign(:user, user)
2033 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2035 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2036 assert id3 == following3.id
2037 assert id2 == following2.id
2041 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2043 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2044 assert id2 == following2.id
2045 assert id1 == following1.id
2049 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2051 assert [%{"id" => id2}] = json_response(res_conn, 200)
2052 assert id2 == following2.id
2054 assert [link_header] = get_resp_header(res_conn, "link")
2055 assert link_header =~ ~r/min_id=#{following2.id}/
2056 assert link_header =~ ~r/max_id=#{following2.id}/
2059 test "following / unfollowing a user", %{conn: conn} do
2060 user = insert(:user)
2061 other_user = insert(:user)
2065 |> assign(:user, user)
2066 |> post("/api/v1/accounts/#{other_user.id}/follow")
2068 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2070 user = User.get_cached_by_id(user.id)
2074 |> assign(:user, user)
2075 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2077 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2079 user = User.get_cached_by_id(user.id)
2083 |> assign(:user, user)
2084 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2086 assert %{"id" => id} = json_response(conn, 200)
2087 assert id == to_string(other_user.id)
2090 test "following without reblogs" do
2091 follower = insert(:user)
2092 followed = insert(:user)
2093 other_user = insert(:user)
2097 |> assign(:user, follower)
2098 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2100 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2102 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2103 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2107 |> assign(:user, User.get_cached_by_id(follower.id))
2108 |> get("/api/v1/timelines/home")
2110 assert [] == json_response(conn, 200)
2114 |> assign(:user, follower)
2115 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2117 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2121 |> assign(:user, User.get_cached_by_id(follower.id))
2122 |> get("/api/v1/timelines/home")
2124 expected_activity_id = reblog.id
2125 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2128 test "following / unfollowing errors" do
2129 user = insert(:user)
2133 |> assign(:user, user)
2136 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2137 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2140 user = User.get_cached_by_id(user.id)
2141 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2142 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2144 # self follow via uri
2145 user = User.get_cached_by_id(user.id)
2146 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2147 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2149 # follow non existing user
2150 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2151 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2153 # follow non existing user via uri
2154 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2155 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2157 # unfollow non existing user
2158 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2159 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2162 describe "mute/unmute" do
2163 test "with notifications", %{conn: conn} do
2164 user = insert(:user)
2165 other_user = insert(:user)
2169 |> assign(:user, user)
2170 |> post("/api/v1/accounts/#{other_user.id}/mute")
2172 response = json_response(conn, 200)
2174 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2175 user = User.get_cached_by_id(user.id)
2179 |> assign(:user, user)
2180 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2182 response = json_response(conn, 200)
2183 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2186 test "without notifications", %{conn: conn} do
2187 user = insert(:user)
2188 other_user = insert(:user)
2192 |> assign(:user, user)
2193 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2195 response = json_response(conn, 200)
2197 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2198 user = User.get_cached_by_id(user.id)
2202 |> assign(:user, user)
2203 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2205 response = json_response(conn, 200)
2206 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2210 test "subscribing / unsubscribing to a user", %{conn: conn} do
2211 user = insert(:user)
2212 subscription_target = insert(:user)
2216 |> assign(:user, user)
2217 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2219 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2223 |> assign(:user, user)
2224 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2226 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2229 test "getting a list of mutes", %{conn: conn} do
2230 user = insert(:user)
2231 other_user = insert(:user)
2233 {:ok, user} = User.mute(user, other_user)
2237 |> assign(:user, user)
2238 |> get("/api/v1/mutes")
2240 other_user_id = to_string(other_user.id)
2241 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2244 test "blocking / unblocking a user", %{conn: conn} do
2245 user = insert(:user)
2246 other_user = insert(:user)
2250 |> assign(:user, user)
2251 |> post("/api/v1/accounts/#{other_user.id}/block")
2253 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2255 user = User.get_cached_by_id(user.id)
2259 |> assign(:user, user)
2260 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2262 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2265 test "getting a list of blocks", %{conn: conn} do
2266 user = insert(:user)
2267 other_user = insert(:user)
2269 {:ok, user} = User.block(user, other_user)
2273 |> assign(:user, user)
2274 |> get("/api/v1/blocks")
2276 other_user_id = to_string(other_user.id)
2277 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2280 test "blocking / unblocking a domain", %{conn: conn} do
2281 user = insert(:user)
2282 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2286 |> assign(:user, user)
2287 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2289 assert %{} = json_response(conn, 200)
2290 user = User.get_cached_by_ap_id(user.ap_id)
2291 assert User.blocks?(user, other_user)
2295 |> assign(:user, user)
2296 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2298 assert %{} = json_response(conn, 200)
2299 user = User.get_cached_by_ap_id(user.ap_id)
2300 refute User.blocks?(user, other_user)
2303 test "getting a list of domain blocks", %{conn: conn} do
2304 user = insert(:user)
2306 {:ok, user} = User.block_domain(user, "bad.site")
2307 {:ok, user} = User.block_domain(user, "even.worse.site")
2311 |> assign(:user, user)
2312 |> get("/api/v1/domain_blocks")
2314 domain_blocks = json_response(conn, 200)
2316 assert "bad.site" in domain_blocks
2317 assert "even.worse.site" in domain_blocks
2320 test "unimplemented follow_requests, blocks, domain blocks" do
2321 user = insert(:user)
2323 ["blocks", "domain_blocks", "follow_requests"]
2324 |> Enum.each(fn endpoint ->
2327 |> assign(:user, user)
2328 |> get("/api/v1/#{endpoint}")
2330 assert [] = json_response(conn, 200)
2334 test "returns the favorites of a user", %{conn: conn} do
2335 user = insert(:user)
2336 other_user = insert(:user)
2338 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2339 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2341 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2345 |> assign(:user, user)
2346 |> get("/api/v1/favourites")
2348 assert [status] = json_response(first_conn, 200)
2349 assert status["id"] == to_string(activity.id)
2351 assert [{"link", _link_header}] =
2352 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2354 # Honours query params
2355 {:ok, second_activity} =
2356 CommonAPI.post(other_user, %{
2358 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2361 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2363 last_like = status["id"]
2367 |> assign(:user, user)
2368 |> get("/api/v1/favourites?since_id=#{last_like}")
2370 assert [second_status] = json_response(second_conn, 200)
2371 assert second_status["id"] == to_string(second_activity.id)
2375 |> assign(:user, user)
2376 |> get("/api/v1/favourites?limit=0")
2378 assert [] = json_response(third_conn, 200)
2381 describe "getting favorites timeline of specified user" do
2383 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2384 [current_user: current_user, user: user]
2387 test "returns list of statuses favorited by specified user", %{
2389 current_user: current_user,
2392 [activity | _] = insert_pair(:note_activity)
2393 CommonAPI.favorite(activity.id, user)
2397 |> assign(:user, current_user)
2398 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2399 |> json_response(:ok)
2403 assert length(response) == 1
2404 assert like["id"] == activity.id
2407 test "returns favorites for specified user_id when user is not logged in", %{
2411 activity = insert(:note_activity)
2412 CommonAPI.favorite(activity.id, user)
2416 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2417 |> json_response(:ok)
2419 assert length(response) == 1
2422 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2424 current_user: current_user,
2428 CommonAPI.post(current_user, %{
2429 "status" => "Hi @#{user.nickname}!",
2430 "visibility" => "direct"
2433 CommonAPI.favorite(direct.id, user)
2437 |> assign(:user, current_user)
2438 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2439 |> json_response(:ok)
2441 assert length(response) == 1
2443 anonymous_response =
2445 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2446 |> json_response(:ok)
2448 assert Enum.empty?(anonymous_response)
2451 test "does not return others' favorited DM when user is not one of recipients", %{
2453 current_user: current_user,
2456 user_two = insert(:user)
2459 CommonAPI.post(user_two, %{
2460 "status" => "Hi @#{user.nickname}!",
2461 "visibility" => "direct"
2464 CommonAPI.favorite(direct.id, user)
2468 |> assign(:user, current_user)
2469 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2470 |> json_response(:ok)
2472 assert Enum.empty?(response)
2475 test "paginates favorites using since_id and max_id", %{
2477 current_user: current_user,
2480 activities = insert_list(10, :note_activity)
2482 Enum.each(activities, fn activity ->
2483 CommonAPI.favorite(activity.id, user)
2486 third_activity = Enum.at(activities, 2)
2487 seventh_activity = Enum.at(activities, 6)
2491 |> assign(:user, current_user)
2492 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2493 since_id: third_activity.id,
2494 max_id: seventh_activity.id
2496 |> json_response(:ok)
2498 assert length(response) == 3
2499 refute third_activity in response
2500 refute seventh_activity in response
2503 test "limits favorites using limit parameter", %{
2505 current_user: current_user,
2509 |> insert_list(:note_activity)
2510 |> Enum.each(fn activity ->
2511 CommonAPI.favorite(activity.id, user)
2516 |> assign(:user, current_user)
2517 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2518 |> json_response(:ok)
2520 assert length(response) == 3
2523 test "returns empty response when user does not have any favorited statuses", %{
2525 current_user: current_user,
2530 |> assign(:user, current_user)
2531 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2532 |> json_response(:ok)
2534 assert Enum.empty?(response)
2537 test "returns 404 error when specified user is not exist", %{conn: conn} do
2538 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2540 assert json_response(conn, 404) == %{"error" => "Record not found"}
2543 test "returns 403 error when user has hidden own favorites", %{
2545 current_user: current_user
2547 user = insert(:user, %{info: %{hide_favorites: true}})
2548 activity = insert(:note_activity)
2549 CommonAPI.favorite(activity.id, user)
2553 |> assign(:user, current_user)
2554 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2556 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2559 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2560 user = insert(:user)
2561 activity = insert(:note_activity)
2562 CommonAPI.favorite(activity.id, user)
2566 |> assign(:user, current_user)
2567 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2569 assert user.info.hide_favorites
2570 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2574 test "get instance information", %{conn: conn} do
2575 conn = get(conn, "/api/v1/instance")
2576 assert result = json_response(conn, 200)
2578 email = Config.get([:instance, :email])
2579 # Note: not checking for "max_toot_chars" since it's optional
2585 "email" => from_config_email,
2587 "streaming_api" => _
2592 "registrations" => _,
2596 assert email == from_config_email
2599 test "get instance stats", %{conn: conn} do
2600 user = insert(:user, %{local: true})
2602 user2 = insert(:user, %{local: true})
2603 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2605 insert(:user, %{local: false, nickname: "u@peer1.com"})
2606 insert(:user, %{local: false, nickname: "u@peer2.com"})
2608 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2610 # Stats should count users with missing or nil `info.deactivated` value
2611 user = User.get_cached_by_id(user.id)
2612 info_change = Changeset.change(user.info, %{deactivated: nil})
2616 |> Changeset.change()
2617 |> Changeset.put_embed(:info, info_change)
2618 |> User.update_and_set_cache()
2620 Pleroma.Stats.force_update()
2622 conn = get(conn, "/api/v1/instance")
2624 assert result = json_response(conn, 200)
2626 stats = result["stats"]
2629 assert stats["user_count"] == 1
2630 assert stats["status_count"] == 1
2631 assert stats["domain_count"] == 2
2634 test "get peers", %{conn: conn} do
2635 insert(:user, %{local: false, nickname: "u@peer1.com"})
2636 insert(:user, %{local: false, nickname: "u@peer2.com"})
2638 Pleroma.Stats.force_update()
2640 conn = get(conn, "/api/v1/instance/peers")
2642 assert result = json_response(conn, 200)
2644 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2647 test "put settings", %{conn: conn} do
2648 user = insert(:user)
2652 |> assign(:user, user)
2653 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2655 assert _result = json_response(conn, 200)
2657 user = User.get_cached_by_ap_id(user.ap_id)
2658 assert user.info.settings == %{"programming" => "socks"}
2661 describe "pinned statuses" do
2663 user = insert(:user)
2664 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2666 [user: user, activity: activity]
2669 clear_config([:instance, :max_pinned_statuses]) do
2670 Config.put([:instance, :max_pinned_statuses], 1)
2673 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2674 {:ok, _} = CommonAPI.pin(activity.id, user)
2678 |> assign(:user, user)
2679 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2680 |> json_response(200)
2682 id_str = to_string(activity.id)
2684 assert [%{"id" => ^id_str, "pinned" => true}] = result
2687 test "pin status", %{conn: conn, user: user, activity: activity} do
2688 id_str = to_string(activity.id)
2690 assert %{"id" => ^id_str, "pinned" => true} =
2692 |> assign(:user, user)
2693 |> post("/api/v1/statuses/#{activity.id}/pin")
2694 |> json_response(200)
2696 assert [%{"id" => ^id_str, "pinned" => true}] =
2698 |> assign(:user, user)
2699 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2700 |> json_response(200)
2703 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2704 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2708 |> assign(:user, user)
2709 |> post("/api/v1/statuses/#{dm.id}/pin")
2711 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2714 test "unpin status", %{conn: conn, user: user, activity: activity} do
2715 {:ok, _} = CommonAPI.pin(activity.id, user)
2717 id_str = to_string(activity.id)
2718 user = refresh_record(user)
2720 assert %{"id" => ^id_str, "pinned" => false} =
2722 |> assign(:user, user)
2723 |> post("/api/v1/statuses/#{activity.id}/unpin")
2724 |> json_response(200)
2728 |> assign(:user, user)
2729 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2730 |> json_response(200)
2733 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2736 |> assign(:user, user)
2737 |> post("/api/v1/statuses/1/unpin")
2739 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2742 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2743 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2745 id_str_one = to_string(activity_one.id)
2747 assert %{"id" => ^id_str_one, "pinned" => true} =
2749 |> assign(:user, user)
2750 |> post("/api/v1/statuses/#{id_str_one}/pin")
2751 |> json_response(200)
2753 user = refresh_record(user)
2755 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2757 |> assign(:user, user)
2758 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2759 |> json_response(400)
2765 Config.put([:rich_media, :enabled], true)
2767 user = insert(:user)
2771 test "returns rich-media card", %{conn: conn, user: user} do
2772 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2775 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2776 "provider_name" => "example.com",
2777 "provider_url" => "https://example.com",
2778 "title" => "The Rock",
2780 "url" => "https://example.com/ogp",
2782 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2785 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2786 "title" => "The Rock",
2787 "type" => "video.movie",
2788 "url" => "https://example.com/ogp",
2790 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2797 |> get("/api/v1/statuses/#{activity.id}/card")
2798 |> json_response(200)
2800 assert response == card_data
2802 # works with private posts
2804 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2808 |> assign(:user, user)
2809 |> get("/api/v1/statuses/#{activity.id}/card")
2810 |> json_response(200)
2812 assert response_two == card_data
2815 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2817 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2821 |> get("/api/v1/statuses/#{activity.id}/card")
2822 |> json_response(:ok)
2824 assert response == %{
2826 "title" => "Pleroma",
2827 "description" => "",
2829 "provider_name" => "example.com",
2830 "provider_url" => "https://example.com",
2831 "url" => "https://example.com/ogp-missing-data",
2834 "title" => "Pleroma",
2835 "type" => "website",
2836 "url" => "https://example.com/ogp-missing-data"
2844 user = insert(:user)
2845 for_user = insert(:user)
2848 CommonAPI.post(user, %{
2849 "status" => "heweoo?"
2853 CommonAPI.post(user, %{
2854 "status" => "heweoo!"
2859 |> assign(:user, for_user)
2860 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2862 assert json_response(response1, 200)["bookmarked"] == true
2866 |> assign(:user, for_user)
2867 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2869 assert json_response(response2, 200)["bookmarked"] == true
2873 |> assign(:user, for_user)
2874 |> get("/api/v1/bookmarks")
2876 assert [json_response(response2, 200), json_response(response1, 200)] ==
2877 json_response(bookmarks, 200)
2881 |> assign(:user, for_user)
2882 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2884 assert json_response(response1, 200)["bookmarked"] == false
2888 |> assign(:user, for_user)
2889 |> get("/api/v1/bookmarks")
2891 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2894 describe "conversation muting" do
2896 post_user = insert(:user)
2897 user = insert(:user)
2899 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2901 [user: user, activity: activity]
2904 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2905 id_str = to_string(activity.id)
2907 assert %{"id" => ^id_str, "muted" => true} =
2909 |> assign(:user, user)
2910 |> post("/api/v1/statuses/#{activity.id}/mute")
2911 |> json_response(200)
2914 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2915 {:ok, _} = CommonAPI.add_mute(user, activity)
2919 |> assign(:user, user)
2920 |> post("/api/v1/statuses/#{activity.id}/mute")
2922 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2925 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2926 {:ok, _} = CommonAPI.add_mute(user, activity)
2928 id_str = to_string(activity.id)
2929 user = refresh_record(user)
2931 assert %{"id" => ^id_str, "muted" => false} =
2933 |> assign(:user, user)
2934 |> post("/api/v1/statuses/#{activity.id}/unmute")
2935 |> json_response(200)
2939 describe "reports" do
2941 reporter = insert(:user)
2942 target_user = insert(:user)
2944 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2946 [reporter: reporter, target_user: target_user, activity: activity]
2949 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2950 assert %{"action_taken" => false, "id" => _} =
2952 |> assign(:user, reporter)
2953 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2954 |> json_response(200)
2957 test "submit a report with statuses and comment", %{
2960 target_user: target_user,
2963 assert %{"action_taken" => false, "id" => _} =
2965 |> assign(:user, reporter)
2966 |> post("/api/v1/reports", %{
2967 "account_id" => target_user.id,
2968 "status_ids" => [activity.id],
2969 "comment" => "bad status!",
2970 "forward" => "false"
2972 |> json_response(200)
2975 test "account_id is required", %{
2980 assert %{"error" => "Valid `account_id` required"} =
2982 |> assign(:user, reporter)
2983 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2984 |> json_response(400)
2987 test "comment must be up to the size specified in the config", %{
2990 target_user: target_user
2992 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2993 comment = String.pad_trailing("a", max_size + 1, "a")
2995 error = %{"error" => "Comment must be up to #{max_size} characters"}
2999 |> assign(:user, reporter)
3000 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3001 |> json_response(400)
3004 test "returns error when account is not exist", %{
3011 |> assign(:user, reporter)
3012 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3014 assert json_response(conn, 400) == %{"error" => "Account not found"}
3018 describe "link headers" do
3019 test "preserves parameters in link headers", %{conn: conn} do
3020 user = insert(:user)
3021 other_user = insert(:user)
3024 CommonAPI.post(other_user, %{
3025 "status" => "hi @#{user.nickname}",
3026 "visibility" => "public"
3030 CommonAPI.post(other_user, %{
3031 "status" => "hi @#{user.nickname}",
3032 "visibility" => "public"
3035 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3036 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3040 |> assign(:user, user)
3041 |> get("/api/v1/notifications", %{media_only: true})
3043 assert [link_header] = get_resp_header(conn, "link")
3044 assert link_header =~ ~r/media_only=true/
3045 assert link_header =~ ~r/min_id=#{notification2.id}/
3046 assert link_header =~ ~r/max_id=#{notification1.id}/
3050 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3051 # Need to set an old-style integer ID to reproduce the problem
3052 # (these are no longer assigned to new accounts but were preserved
3053 # for existing accounts during the migration to flakeIDs)
3054 user_one = insert(:user, %{id: 1212})
3055 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3059 |> get("/api/v1/accounts/#{user_one.id}")
3063 |> get("/api/v1/accounts/#{user_two.nickname}")
3067 |> get("/api/v1/accounts/#{user_two.id}")
3069 acc_one = json_response(resp_one, 200)
3070 acc_two = json_response(resp_two, 200)
3071 acc_three = json_response(resp_three, 200)
3072 refute acc_one == acc_two
3073 assert acc_two == acc_three
3076 describe "custom emoji" do
3077 test "with tags", %{conn: conn} do
3080 |> get("/api/v1/custom_emojis")
3081 |> json_response(200)
3083 assert Map.has_key?(emoji, "shortcode")
3084 assert Map.has_key?(emoji, "static_url")
3085 assert Map.has_key?(emoji, "tags")
3086 assert is_list(emoji["tags"])
3087 assert Map.has_key?(emoji, "category")
3088 assert Map.has_key?(emoji, "url")
3089 assert Map.has_key?(emoji, "visible_in_picker")
3093 describe "index/2 redirections" do
3094 setup %{conn: conn} do
3098 signing_salt: "cooldude"
3103 |> Plug.Session.call(Plug.Session.init(session_opts))
3106 test_path = "/web/statuses/test"
3107 %{conn: conn, path: test_path}
3110 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3111 conn = get(conn, path)
3113 assert conn.status == 302
3114 assert redirected_to(conn) == "/web/login"
3117 test "redirects not logged-in users to the login page on private instances", %{
3121 Config.put([:instance, :public], false)
3123 conn = get(conn, path)
3125 assert conn.status == 302
3126 assert redirected_to(conn) == "/web/login"
3129 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3130 token = insert(:oauth_token)
3134 |> assign(:user, token.user)
3135 |> put_session(:oauth_token, token.token)
3138 assert conn.status == 200
3141 test "saves referer path to session", %{conn: conn, path: path} do
3142 conn = get(conn, path)
3143 return_to = Plug.Conn.get_session(conn, :return_to)
3145 assert return_to == path
3148 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3149 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3150 auth = insert(:oauth_authorization, app: app)
3154 |> put_session(:return_to, path)
3155 |> get("/web/login", %{code: auth.token})
3157 assert conn.status == 302
3158 assert redirected_to(conn) == path
3161 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3162 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3163 auth = insert(:oauth_authorization, app: app)
3165 conn = get(conn, "/web/login", %{code: auth.token})
3167 assert conn.status == 302
3168 assert redirected_to(conn) == "/web/getting-started"
3172 describe "scheduled activities" do
3173 test "creates a scheduled activity", %{conn: conn} do
3174 user = insert(:user)
3175 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3179 |> assign(:user, user)
3180 |> post("/api/v1/statuses", %{
3181 "status" => "scheduled",
3182 "scheduled_at" => scheduled_at
3185 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3186 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3187 assert [] == Repo.all(Activity)
3190 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3191 user = insert(:user)
3192 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3194 file = %Plug.Upload{
3195 content_type: "image/jpg",
3196 path: Path.absname("test/fixtures/image.jpg"),
3197 filename: "an_image.jpg"
3200 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3204 |> assign(:user, user)
3205 |> post("/api/v1/statuses", %{
3206 "media_ids" => [to_string(upload.id)],
3207 "status" => "scheduled",
3208 "scheduled_at" => scheduled_at
3211 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3212 assert %{"type" => "image"} = media_attachment
3215 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3217 user = insert(:user)
3220 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3224 |> assign(:user, user)
3225 |> post("/api/v1/statuses", %{
3226 "status" => "not scheduled",
3227 "scheduled_at" => scheduled_at
3230 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3231 assert [] == Repo.all(ScheduledActivity)
3234 test "returns error when daily user limit is exceeded", %{conn: conn} do
3235 user = insert(:user)
3238 NaiveDateTime.utc_now()
3239 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3240 |> NaiveDateTime.to_iso8601()
3242 attrs = %{params: %{}, scheduled_at: today}
3243 {:ok, _} = ScheduledActivity.create(user, attrs)
3244 {:ok, _} = ScheduledActivity.create(user, attrs)
3248 |> assign(:user, user)
3249 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3251 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3254 test "returns error when total user limit is exceeded", %{conn: conn} do
3255 user = insert(:user)
3258 NaiveDateTime.utc_now()
3259 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3260 |> NaiveDateTime.to_iso8601()
3263 NaiveDateTime.utc_now()
3264 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3265 |> NaiveDateTime.to_iso8601()
3267 attrs = %{params: %{}, scheduled_at: today}
3268 {:ok, _} = ScheduledActivity.create(user, attrs)
3269 {:ok, _} = ScheduledActivity.create(user, attrs)
3270 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3274 |> assign(:user, user)
3275 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3277 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3280 test "shows scheduled activities", %{conn: conn} do
3281 user = insert(:user)
3282 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3283 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3284 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3285 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3289 |> assign(:user, user)
3294 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3296 result = json_response(conn_res, 200)
3297 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3302 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3304 result = json_response(conn_res, 200)
3305 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3310 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3312 result = json_response(conn_res, 200)
3313 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3316 test "shows a scheduled activity", %{conn: conn} do
3317 user = insert(:user)
3318 scheduled_activity = insert(:scheduled_activity, user: user)
3322 |> assign(:user, user)
3323 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3325 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3326 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3330 |> assign(:user, user)
3331 |> get("/api/v1/scheduled_statuses/404")
3333 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3336 test "updates a scheduled activity", %{conn: conn} do
3337 user = insert(:user)
3338 scheduled_activity = insert(:scheduled_activity, user: user)
3341 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3345 |> assign(:user, user)
3346 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3347 scheduled_at: new_scheduled_at
3350 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3351 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3355 |> assign(:user, user)
3356 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3358 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3361 test "deletes a scheduled activity", %{conn: conn} do
3362 user = insert(:user)
3363 scheduled_activity = insert(:scheduled_activity, user: user)
3367 |> assign(:user, user)
3368 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3370 assert %{} = json_response(res_conn, 200)
3371 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3375 |> assign(:user, user)
3376 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3378 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3382 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3383 user1 = insert(:user)
3384 user2 = insert(:user)
3385 user3 = insert(:user)
3387 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3389 # Reply to status from another user
3392 |> assign(:user, user2)
3393 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3395 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3397 activity = Activity.get_by_id_with_object(id)
3399 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3400 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3402 # Reblog from the third user
3405 |> assign(:user, user3)
3406 |> post("/api/v1/statuses/#{activity.id}/reblog")
3408 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3409 json_response(conn2, 200)
3411 assert to_string(activity.id) == id
3413 # Getting third user status
3416 |> assign(:user, user3)
3417 |> get("api/v1/timelines/home")
3419 [reblogged_activity] = json_response(conn3, 200)
3421 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3423 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3424 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3427 describe "create account by app" do
3428 test "Account registration via Application", %{conn: conn} do
3431 |> post("/api/v1/apps", %{
3432 client_name: "client_name",
3433 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3434 scopes: "read, write, follow"
3438 "client_id" => client_id,
3439 "client_secret" => client_secret,
3441 "name" => "client_name",
3442 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3445 } = json_response(conn, 200)
3449 |> post("/oauth/token", %{
3450 grant_type: "client_credentials",
3451 client_id: client_id,
3452 client_secret: client_secret
3455 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3456 json_response(conn, 200)
3459 token_from_db = Repo.get_by(Token, token: token)
3460 assert token_from_db
3462 assert scope == "read write follow"
3466 |> put_req_header("authorization", "Bearer " <> token)
3467 |> post("/api/v1/accounts", %{
3469 email: "lain@example.org",
3470 password: "PlzDontHackLain",
3475 "access_token" => token,
3476 "created_at" => _created_at,
3478 "token_type" => "Bearer"
3479 } = json_response(conn, 200)
3481 token_from_db = Repo.get_by(Token, token: token)
3482 assert token_from_db
3483 token_from_db = Repo.preload(token_from_db, :user)
3484 assert token_from_db.user
3486 assert token_from_db.user.info.confirmation_pending
3489 test "rate limit", %{conn: conn} do
3490 app_token = insert(:oauth_token, user: nil)
3493 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3494 |> Map.put(:remote_ip, {15, 15, 15, 15})
3499 |> post("/api/v1/accounts", %{
3500 username: "#{i}lain",
3501 email: "#{i}lain@example.org",
3502 password: "PlzDontHackLain",
3507 "access_token" => token,
3508 "created_at" => _created_at,
3510 "token_type" => "Bearer"
3511 } = json_response(conn, 200)
3513 token_from_db = Repo.get_by(Token, token: token)
3514 assert token_from_db
3515 token_from_db = Repo.preload(token_from_db, :user)
3516 assert token_from_db.user
3518 assert token_from_db.user.info.confirmation_pending
3523 |> post("/api/v1/accounts", %{
3525 email: "6lain@example.org",
3526 password: "PlzDontHackLain",
3530 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3534 describe "GET /api/v1/polls/:id" do
3535 test "returns poll entity for object id", %{conn: conn} do
3536 user = insert(:user)
3539 CommonAPI.post(user, %{
3540 "status" => "Pleroma does",
3541 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3544 object = Object.normalize(activity)
3548 |> assign(:user, user)
3549 |> get("/api/v1/polls/#{object.id}")
3551 response = json_response(conn, 200)
3552 id = to_string(object.id)
3553 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3556 test "does not expose polls for private statuses", %{conn: conn} do
3557 user = insert(:user)
3558 other_user = insert(:user)
3561 CommonAPI.post(user, %{
3562 "status" => "Pleroma does",
3563 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3564 "visibility" => "private"
3567 object = Object.normalize(activity)
3571 |> assign(:user, other_user)
3572 |> get("/api/v1/polls/#{object.id}")
3574 assert json_response(conn, 404)
3578 describe "POST /api/v1/polls/:id/votes" do
3579 test "votes are added to the poll", %{conn: conn} do
3580 user = insert(:user)
3581 other_user = insert(:user)
3584 CommonAPI.post(user, %{
3585 "status" => "A very delicious sandwich",
3587 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3593 object = Object.normalize(activity)
3597 |> assign(:user, other_user)
3598 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3600 assert json_response(conn, 200)
3601 object = Object.get_by_id(object.id)
3603 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3608 test "author can't vote", %{conn: conn} do
3609 user = insert(:user)
3612 CommonAPI.post(user, %{
3613 "status" => "Am I cute?",
3614 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3617 object = Object.normalize(activity)
3620 |> assign(:user, user)
3621 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3622 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3624 object = Object.get_by_id(object.id)
3626 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3629 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3630 user = insert(:user)
3631 other_user = insert(:user)
3634 CommonAPI.post(user, %{
3635 "status" => "The glass is",
3636 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3639 object = Object.normalize(activity)
3642 |> assign(:user, other_user)
3643 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3644 |> json_response(422) == %{"error" => "Too many choices"}
3646 object = Object.get_by_id(object.id)
3648 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3653 test "does not allow choice index to be greater than options count", %{conn: conn} do
3654 user = insert(:user)
3655 other_user = insert(:user)
3658 CommonAPI.post(user, %{
3659 "status" => "Am I cute?",
3660 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3663 object = Object.normalize(activity)
3667 |> assign(:user, other_user)
3668 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3670 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3673 test "returns 404 error when object is not exist", %{conn: conn} do
3674 user = insert(:user)
3678 |> assign(:user, user)
3679 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3681 assert json_response(conn, 404) == %{"error" => "Record not found"}
3684 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3685 user = insert(:user)
3686 other_user = insert(:user)
3689 CommonAPI.post(user, %{
3690 "status" => "Am I cute?",
3691 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3692 "visibility" => "private"
3695 object = Object.normalize(activity)
3699 |> assign(:user, other_user)
3700 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3702 assert json_response(conn, 404) == %{"error" => "Record not found"}
3706 describe "GET /api/v1/statuses/:id/favourited_by" do
3708 user = insert(:user)
3709 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3713 |> assign(:user, user)
3715 [conn: conn, activity: activity]
3718 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3719 other_user = insert(:user)
3720 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3724 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3725 |> json_response(:ok)
3727 [%{"id" => id}] = response
3729 assert id == other_user.id
3732 test "returns empty array when status has not been favorited yet", %{
3738 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3739 |> json_response(:ok)
3741 assert Enum.empty?(response)
3744 test "does not return users who have favorited the status but are blocked", %{
3745 conn: %{assigns: %{user: user}} = conn,
3748 other_user = insert(:user)
3749 {:ok, user} = User.block(user, other_user)
3751 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3755 |> assign(:user, user)
3756 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3757 |> json_response(:ok)
3759 assert Enum.empty?(response)
3762 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3763 other_user = insert(:user)
3764 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3768 |> assign(:user, nil)
3769 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3770 |> json_response(:ok)
3772 [%{"id" => id}] = response
3773 assert id == other_user.id
3777 describe "GET /api/v1/statuses/:id/reblogged_by" do
3779 user = insert(:user)
3780 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3784 |> assign(:user, user)
3786 [conn: conn, activity: activity]
3789 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3790 other_user = insert(:user)
3791 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3795 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3796 |> json_response(:ok)
3798 [%{"id" => id}] = response
3800 assert id == other_user.id
3803 test "returns empty array when status has not been reblogged yet", %{
3809 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3810 |> json_response(:ok)
3812 assert Enum.empty?(response)
3815 test "does not return users who have reblogged the status but are blocked", %{
3816 conn: %{assigns: %{user: user}} = conn,
3819 other_user = insert(:user)
3820 {:ok, user} = User.block(user, other_user)
3822 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3826 |> assign(:user, user)
3827 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3828 |> json_response(:ok)
3830 assert Enum.empty?(response)
3833 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3834 other_user = insert(:user)
3835 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3839 |> assign(:user, nil)
3840 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3841 |> json_response(:ok)
3843 [%{"id" => id}] = response
3844 assert id == other_user.id
3848 describe "POST /auth/password, with valid parameters" do
3849 setup %{conn: conn} do
3850 user = insert(:user)
3851 conn = post(conn, "/auth/password?email=#{user.email}")
3852 %{conn: conn, user: user}
3855 test "it returns 204", %{conn: conn} do
3856 assert json_response(conn, :no_content)
3859 test "it creates a PasswordResetToken record for user", %{user: user} do
3860 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3864 test "it sends an email to user", %{user: user} do
3865 ObanHelpers.perform_all()
3866 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3868 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3869 notify_email = Config.get([:instance, :notify_email])
3870 instance_name = Config.get([:instance, :name])
3873 from: {instance_name, notify_email},
3874 to: {user.name, user.email},
3875 html_body: email.html_body
3880 describe "POST /auth/password, with invalid parameters" do
3882 user = insert(:user)
3886 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3887 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3888 assert conn.status == 404
3889 assert conn.resp_body == ""
3892 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3893 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3894 conn = post(conn, "/auth/password?email=#{user.email}")
3895 assert conn.status == 400
3896 assert conn.resp_body == ""
3900 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3902 user = insert(:user)
3903 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3907 |> Changeset.change()
3908 |> Changeset.put_embed(:info, info_change)
3911 assert user.info.confirmation_pending
3916 clear_config([:instance, :account_activation_required]) do
3917 Config.put([:instance, :account_activation_required], true)
3920 test "resend account confirmation email", %{conn: conn, user: user} do
3922 |> assign(:user, user)
3923 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3924 |> json_response(:no_content)
3926 ObanHelpers.perform_all()
3928 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3929 notify_email = Config.get([:instance, :notify_email])
3930 instance_name = Config.get([:instance, :name])
3933 from: {instance_name, notify_email},
3934 to: {user.name, user.email},
3935 html_body: email.html_body
3940 describe "GET /api/v1/suggestions" do
3942 user = insert(:user)
3943 other_user = insert(:user)
3944 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3945 url500 = "http://test500?#{host}&#{user.nickname}"
3946 url200 = "http://test200?#{host}&#{user.nickname}"
3949 %{method: :get, url: ^url500} ->
3950 %Tesla.Env{status: 500, body: "bad request"}
3952 %{method: :get, url: ^url200} ->
3956 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3958 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3962 [user: user, other_user: other_user]
3965 clear_config(:suggestions)
3967 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
3968 Config.put([:suggestions, :enabled], false)
3972 |> assign(:user, user)
3973 |> get("/api/v1/suggestions")
3974 |> json_response(200)
3979 test "returns error", %{conn: conn, user: user} do
3980 Config.put([:suggestions, :enabled], true)
3981 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
3985 |> assign(:user, user)
3986 |> get("/api/v1/suggestions")
3987 |> json_response(500)
3989 assert res == "Something went wrong"
3992 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
3993 Config.put([:suggestions, :enabled], true)
3994 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
3998 |> assign(:user, user)
3999 |> get("/api/v1/suggestions")
4000 |> json_response(200)
4005 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4006 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4010 "acct" => other_user.ap_id,
4011 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4012 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4013 "id" => other_user.id