1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
10 alias Pleroma.Notification
13 alias Pleroma.Tests.ObanHelpers
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.OAuth.App
18 alias Pleroma.Web.Push
20 import ExUnit.CaptureLog
21 import Pleroma.Factory
22 import Swoosh.TestAssertions
26 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
30 clear_config([:instance, :public])
31 clear_config([:rich_media, :enabled])
33 test "apps/verify_credentials", %{conn: conn} do
34 token = insert(:oauth_token)
38 |> assign(:user, token.user)
39 |> assign(:token, token)
40 |> get("/api/v1/apps/verify_credentials")
42 app = Repo.preload(token, :app).app
45 "name" => app.client_name,
46 "website" => app.website,
47 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
50 assert expected == json_response(conn, 200)
53 test "creates an oauth app", %{conn: conn} do
55 app_attrs = build(:oauth_app)
59 |> assign(:user, user)
60 |> post("/api/v1/apps", %{
61 client_name: app_attrs.client_name,
62 redirect_uris: app_attrs.redirect_uris
68 "name" => app.client_name,
69 "website" => app.website,
70 "client_id" => app.client_id,
71 "client_secret" => app.client_secret,
72 "id" => app.id |> to_string(),
73 "redirect_uri" => app.redirect_uris,
74 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
77 assert expected == json_response(conn, 200)
80 describe "media upload" do
86 |> assign(:user, user)
89 content_type: "image/jpg",
90 path: Path.absname("test/fixtures/image.jpg"),
91 filename: "an_image.jpg"
94 [conn: conn, image: image]
97 clear_config([:media_proxy])
98 clear_config([Pleroma.Upload])
100 test "returns uploaded image", %{conn: conn, image: image} do
101 desc = "Description of the image"
105 |> post("/api/v1/media", %{"file" => image, "description" => desc})
106 |> json_response(:ok)
108 assert media["type"] == "image"
109 assert media["description"] == desc
112 object = Repo.get(Object, media["id"])
113 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
117 test "getting a list of mutes", %{conn: conn} do
119 other_user = insert(:user)
121 {:ok, user} = User.mute(user, other_user)
125 |> assign(:user, user)
126 |> get("/api/v1/mutes")
128 other_user_id = to_string(other_user.id)
129 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
132 test "getting a list of blocks", %{conn: conn} do
134 other_user = insert(:user)
136 {:ok, user} = User.block(user, other_user)
140 |> assign(:user, user)
141 |> get("/api/v1/blocks")
143 other_user_id = to_string(other_user.id)
144 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
147 test "unimplemented follow_requests, blocks, domain blocks" do
150 ["blocks", "domain_blocks", "follow_requests"]
151 |> Enum.each(fn endpoint ->
154 |> assign(:user, user)
155 |> get("/api/v1/#{endpoint}")
157 assert [] = json_response(conn, 200)
161 test "returns the favorites of a user", %{conn: conn} do
163 other_user = insert(:user)
165 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
166 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
168 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
172 |> assign(:user, user)
173 |> get("/api/v1/favourites")
175 assert [status] = json_response(first_conn, 200)
176 assert status["id"] == to_string(activity.id)
178 assert [{"link", _link_header}] =
179 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
181 # Honours query params
182 {:ok, second_activity} =
183 CommonAPI.post(other_user, %{
185 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
188 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
190 last_like = status["id"]
194 |> assign(:user, user)
195 |> get("/api/v1/favourites?since_id=#{last_like}")
197 assert [second_status] = json_response(second_conn, 200)
198 assert second_status["id"] == to_string(second_activity.id)
202 |> assign(:user, user)
203 |> get("/api/v1/favourites?limit=0")
205 assert [] = json_response(third_conn, 200)
208 test "get instance information", %{conn: conn} do
209 conn = get(conn, "/api/v1/instance")
210 assert result = json_response(conn, 200)
212 email = Config.get([:instance, :email])
213 # Note: not checking for "max_toot_chars" since it's optional
219 "email" => from_config_email,
226 "registrations" => _,
230 assert email == from_config_email
233 test "get instance stats", %{conn: conn} do
234 user = insert(:user, %{local: true})
236 user2 = insert(:user, %{local: true})
237 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
239 insert(:user, %{local: false, nickname: "u@peer1.com"})
240 insert(:user, %{local: false, nickname: "u@peer2.com"})
242 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
244 # Stats should count users with missing or nil `info.deactivated` value
248 |> User.get_cached_by_id()
249 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
251 Pleroma.Stats.force_update()
253 conn = get(conn, "/api/v1/instance")
255 assert result = json_response(conn, 200)
257 stats = result["stats"]
260 assert stats["user_count"] == 1
261 assert stats["status_count"] == 1
262 assert stats["domain_count"] == 2
265 test "get peers", %{conn: conn} do
266 insert(:user, %{local: false, nickname: "u@peer1.com"})
267 insert(:user, %{local: false, nickname: "u@peer2.com"})
269 Pleroma.Stats.force_update()
271 conn = get(conn, "/api/v1/instance/peers")
273 assert result = json_response(conn, 200)
275 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
278 test "put settings", %{conn: conn} do
283 |> assign(:user, user)
284 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
286 assert _result = json_response(conn, 200)
288 user = User.get_cached_by_ap_id(user.ap_id)
289 assert user.info.settings == %{"programming" => "socks"}
292 describe "link headers" do
293 test "preserves parameters in link headers", %{conn: conn} do
295 other_user = insert(:user)
298 CommonAPI.post(other_user, %{
299 "status" => "hi @#{user.nickname}",
300 "visibility" => "public"
304 CommonAPI.post(other_user, %{
305 "status" => "hi @#{user.nickname}",
306 "visibility" => "public"
309 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
310 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
314 |> assign(:user, user)
315 |> get("/api/v1/notifications", %{media_only: true})
317 assert [link_header] = get_resp_header(conn, "link")
318 assert link_header =~ ~r/media_only=true/
319 assert link_header =~ ~r/min_id=#{notification2.id}/
320 assert link_header =~ ~r/max_id=#{notification1.id}/
324 describe "custom emoji" do
325 test "with tags", %{conn: conn} do
328 |> get("/api/v1/custom_emojis")
329 |> json_response(200)
331 assert Map.has_key?(emoji, "shortcode")
332 assert Map.has_key?(emoji, "static_url")
333 assert Map.has_key?(emoji, "tags")
334 assert is_list(emoji["tags"])
335 assert Map.has_key?(emoji, "category")
336 assert Map.has_key?(emoji, "url")
337 assert Map.has_key?(emoji, "visible_in_picker")
341 describe "index/2 redirections" do
342 setup %{conn: conn} do
346 signing_salt: "cooldude"
351 |> Plug.Session.call(Plug.Session.init(session_opts))
354 test_path = "/web/statuses/test"
355 %{conn: conn, path: test_path}
358 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
359 conn = get(conn, path)
361 assert conn.status == 302
362 assert redirected_to(conn) == "/web/login"
365 test "redirects not logged-in users to the login page on private instances", %{
369 Config.put([:instance, :public], false)
371 conn = get(conn, path)
373 assert conn.status == 302
374 assert redirected_to(conn) == "/web/login"
377 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
378 token = insert(:oauth_token)
382 |> assign(:user, token.user)
383 |> put_session(:oauth_token, token.token)
386 assert conn.status == 200
389 test "saves referer path to session", %{conn: conn, path: path} do
390 conn = get(conn, path)
391 return_to = Plug.Conn.get_session(conn, :return_to)
393 assert return_to == path
396 test "redirects to the saved path after log in", %{conn: conn, path: path} do
397 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
398 auth = insert(:oauth_authorization, app: app)
402 |> put_session(:return_to, path)
403 |> get("/web/login", %{code: auth.token})
405 assert conn.status == 302
406 assert redirected_to(conn) == path
409 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
410 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
411 auth = insert(:oauth_authorization, app: app)
413 conn = get(conn, "/web/login", %{code: auth.token})
415 assert conn.status == 302
416 assert redirected_to(conn) == "/web/getting-started"
420 describe "POST /auth/password, with valid parameters" do
421 setup %{conn: conn} do
423 conn = post(conn, "/auth/password?email=#{user.email}")
424 %{conn: conn, user: user}
427 test "it returns 204", %{conn: conn} do
428 assert json_response(conn, :no_content)
431 test "it creates a PasswordResetToken record for user", %{user: user} do
432 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
436 test "it sends an email to user", %{user: user} do
437 ObanHelpers.perform_all()
438 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
440 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
441 notify_email = Config.get([:instance, :notify_email])
442 instance_name = Config.get([:instance, :name])
445 from: {instance_name, notify_email},
446 to: {user.name, user.email},
447 html_body: email.html_body
452 describe "POST /auth/password, with invalid parameters" do
458 test "it returns 404 when user is not found", %{conn: conn, user: user} do
459 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
460 assert conn.status == 404
461 assert conn.resp_body == ""
464 test "it returns 400 when user is not local", %{conn: conn, user: user} do
465 {:ok, user} = Repo.update(Changeset.change(user, local: false))
466 conn = post(conn, "/auth/password?email=#{user.email}")
467 assert conn.status == 400
468 assert conn.resp_body == ""
472 describe "GET /api/v1/suggestions" do
475 other_user = insert(:user)
476 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
477 url500 = "http://test500?#{host}&#{user.nickname}"
478 url200 = "http://test200?#{host}&#{user.nickname}"
481 %{method: :get, url: ^url500} ->
482 %Tesla.Env{status: 500, body: "bad request"}
484 %{method: :get, url: ^url200} ->
488 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
490 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
494 [user: user, other_user: other_user]
497 clear_config(:suggestions)
499 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
500 Config.put([:suggestions, :enabled], false)
504 |> assign(:user, user)
505 |> get("/api/v1/suggestions")
506 |> json_response(200)
511 test "returns error", %{conn: conn, user: user} do
512 Config.put([:suggestions, :enabled], true)
513 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
515 assert capture_log(fn ->
518 |> assign(:user, user)
519 |> get("/api/v1/suggestions")
520 |> json_response(500)
522 assert res == "Something went wrong"
523 end) =~ "Could not retrieve suggestions"
526 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
527 Config.put([:suggestions, :enabled], true)
528 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
532 |> assign(:user, user)
533 |> get("/api/v1/suggestions")
534 |> json_response(200)
539 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
540 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
544 "acct" => other_user.ap_id,
545 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
546 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
547 "id" => other_user.id
553 describe "PUT /api/v1/media/:id" do
555 actor = insert(:user)
558 content_type: "image/jpg",
559 path: Path.absname("test/fixtures/image.jpg"),
560 filename: "an_image.jpg"
563 {:ok, %Object{} = object} =
566 actor: User.ap_id(actor),
567 description: "test-m"
570 [actor: actor, object: object]
573 test "updates name of media", %{conn: conn, actor: actor, object: object} do
576 |> assign(:user, actor)
577 |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"})
578 |> json_response(:ok)
580 assert media["description"] == "test-media"
581 assert refresh_record(object).data["name"] == "test-media"
584 test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do
587 |> assign(:user, actor)
588 |> put("/api/v1/media/#{object.id}", %{})
589 |> json_response(400)
591 assert media == %{"error" => "bad_request"}
595 describe "DELETE /auth/sign_out" do
596 test "redirect to root page", %{conn: conn} do
601 |> assign(:user, user)
602 |> delete("/auth/sign_out")
604 assert conn.status == 302
605 assert redirected_to(conn) == "/"
609 describe "empty_array, stubs for mastodon api" do
610 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
615 |> assign(:user, user)
616 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
617 |> json_response(200)
622 test "GET /api/v1/endorsements", %{conn: conn} do
627 |> assign(:user, user)
628 |> get("/api/v1/endorsements")
629 |> json_response(200)
634 test "GET /api/v1/trends", %{conn: conn} do
639 |> assign(:user, user)
640 |> get("/api/v1/trends")
641 |> json_response(200)