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.CommonAPI
16 alias Pleroma.Web.OAuth.App
17 alias Pleroma.Web.Push
19 import ExUnit.CaptureLog
20 import Pleroma.Factory
21 import Swoosh.TestAssertions
25 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
29 clear_config([:instance, :public])
30 clear_config([:rich_media, :enabled])
32 test "apps/verify_credentials", %{conn: conn} do
33 token = insert(:oauth_token)
37 |> assign(:user, token.user)
38 |> assign(:token, token)
39 |> get("/api/v1/apps/verify_credentials")
41 app = Repo.preload(token, :app).app
44 "name" => app.client_name,
45 "website" => app.website,
46 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
49 assert expected == json_response(conn, 200)
52 test "creates an oauth app", %{conn: conn} do
54 app_attrs = build(:oauth_app)
58 |> assign(:user, user)
59 |> post("/api/v1/apps", %{
60 client_name: app_attrs.client_name,
61 redirect_uris: app_attrs.redirect_uris
67 "name" => app.client_name,
68 "website" => app.website,
69 "client_id" => app.client_id,
70 "client_secret" => app.client_secret,
71 "id" => app.id |> to_string(),
72 "redirect_uri" => app.redirect_uris,
73 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
76 assert expected == json_response(conn, 200)
79 test "getting a list of mutes", %{conn: conn} do
81 other_user = insert(:user)
83 {:ok, user} = User.mute(user, other_user)
87 |> assign(:user, user)
88 |> get("/api/v1/mutes")
90 other_user_id = to_string(other_user.id)
91 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
94 test "getting a list of blocks", %{conn: conn} do
96 other_user = insert(:user)
98 {:ok, user} = User.block(user, other_user)
102 |> assign(:user, user)
103 |> get("/api/v1/blocks")
105 other_user_id = to_string(other_user.id)
106 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
109 test "unimplemented follow_requests, blocks, domain blocks" do
112 ["blocks", "domain_blocks", "follow_requests"]
113 |> Enum.each(fn endpoint ->
116 |> assign(:user, user)
117 |> get("/api/v1/#{endpoint}")
119 assert [] = json_response(conn, 200)
123 test "returns the favorites of a user", %{conn: conn} do
125 other_user = insert(:user)
127 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
128 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
130 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
134 |> assign(:user, user)
135 |> get("/api/v1/favourites")
137 assert [status] = json_response(first_conn, 200)
138 assert status["id"] == to_string(activity.id)
140 assert [{"link", _link_header}] =
141 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
143 # Honours query params
144 {:ok, second_activity} =
145 CommonAPI.post(other_user, %{
147 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
150 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
152 last_like = status["id"]
156 |> assign(:user, user)
157 |> get("/api/v1/favourites?since_id=#{last_like}")
159 assert [second_status] = json_response(second_conn, 200)
160 assert second_status["id"] == to_string(second_activity.id)
164 |> assign(:user, user)
165 |> get("/api/v1/favourites?limit=0")
167 assert [] = json_response(third_conn, 200)
170 test "get instance information", %{conn: conn} do
171 conn = get(conn, "/api/v1/instance")
172 assert result = json_response(conn, 200)
174 email = Config.get([:instance, :email])
175 # Note: not checking for "max_toot_chars" since it's optional
181 "email" => from_config_email,
188 "registrations" => _,
192 assert email == from_config_email
195 test "get instance stats", %{conn: conn} do
196 user = insert(:user, %{local: true})
198 user2 = insert(:user, %{local: true})
199 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
201 insert(:user, %{local: false, nickname: "u@peer1.com"})
202 insert(:user, %{local: false, nickname: "u@peer2.com"})
204 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
206 # Stats should count users with missing or nil `info.deactivated` value
210 |> User.get_cached_by_id()
211 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
213 Pleroma.Stats.force_update()
215 conn = get(conn, "/api/v1/instance")
217 assert result = json_response(conn, 200)
219 stats = result["stats"]
222 assert stats["user_count"] == 1
223 assert stats["status_count"] == 1
224 assert stats["domain_count"] == 2
227 test "get peers", %{conn: conn} do
228 insert(:user, %{local: false, nickname: "u@peer1.com"})
229 insert(:user, %{local: false, nickname: "u@peer2.com"})
231 Pleroma.Stats.force_update()
233 conn = get(conn, "/api/v1/instance/peers")
235 assert result = json_response(conn, 200)
237 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
240 test "put settings", %{conn: conn} do
245 |> assign(:user, user)
246 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
248 assert _result = json_response(conn, 200)
250 user = User.get_cached_by_ap_id(user.ap_id)
251 assert user.info.settings == %{"programming" => "socks"}
254 describe "link headers" do
255 test "preserves parameters in link headers", %{conn: conn} do
257 other_user = insert(:user)
260 CommonAPI.post(other_user, %{
261 "status" => "hi @#{user.nickname}",
262 "visibility" => "public"
266 CommonAPI.post(other_user, %{
267 "status" => "hi @#{user.nickname}",
268 "visibility" => "public"
271 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
272 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
276 |> assign(:user, user)
277 |> get("/api/v1/notifications", %{media_only: true})
279 assert [link_header] = get_resp_header(conn, "link")
280 assert link_header =~ ~r/media_only=true/
281 assert link_header =~ ~r/min_id=#{notification2.id}/
282 assert link_header =~ ~r/max_id=#{notification1.id}/
286 describe "custom emoji" do
287 test "with tags", %{conn: conn} do
290 |> get("/api/v1/custom_emojis")
291 |> json_response(200)
293 assert Map.has_key?(emoji, "shortcode")
294 assert Map.has_key?(emoji, "static_url")
295 assert Map.has_key?(emoji, "tags")
296 assert is_list(emoji["tags"])
297 assert Map.has_key?(emoji, "category")
298 assert Map.has_key?(emoji, "url")
299 assert Map.has_key?(emoji, "visible_in_picker")
303 describe "index/2 redirections" do
304 setup %{conn: conn} do
308 signing_salt: "cooldude"
313 |> Plug.Session.call(Plug.Session.init(session_opts))
316 test_path = "/web/statuses/test"
317 %{conn: conn, path: test_path}
320 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
321 conn = get(conn, path)
323 assert conn.status == 302
324 assert redirected_to(conn) == "/web/login"
327 test "redirects not logged-in users to the login page on private instances", %{
331 Config.put([:instance, :public], false)
333 conn = get(conn, path)
335 assert conn.status == 302
336 assert redirected_to(conn) == "/web/login"
339 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
340 token = insert(:oauth_token)
344 |> assign(:user, token.user)
345 |> put_session(:oauth_token, token.token)
348 assert conn.status == 200
351 test "saves referer path to session", %{conn: conn, path: path} do
352 conn = get(conn, path)
353 return_to = Plug.Conn.get_session(conn, :return_to)
355 assert return_to == path
358 test "redirects to the saved path after log in", %{conn: conn, path: path} do
359 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
360 auth = insert(:oauth_authorization, app: app)
364 |> put_session(:return_to, path)
365 |> get("/web/login", %{code: auth.token})
367 assert conn.status == 302
368 assert redirected_to(conn) == path
371 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
372 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
373 auth = insert(:oauth_authorization, app: app)
375 conn = get(conn, "/web/login", %{code: auth.token})
377 assert conn.status == 302
378 assert redirected_to(conn) == "/web/getting-started"
382 describe "POST /auth/password, with valid parameters" do
383 setup %{conn: conn} do
385 conn = post(conn, "/auth/password?email=#{user.email}")
386 %{conn: conn, user: user}
389 test "it returns 204", %{conn: conn} do
390 assert json_response(conn, :no_content)
393 test "it creates a PasswordResetToken record for user", %{user: user} do
394 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
398 test "it sends an email to user", %{user: user} do
399 ObanHelpers.perform_all()
400 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
402 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
403 notify_email = Config.get([:instance, :notify_email])
404 instance_name = Config.get([:instance, :name])
407 from: {instance_name, notify_email},
408 to: {user.name, user.email},
409 html_body: email.html_body
414 describe "POST /auth/password, with invalid parameters" do
420 test "it returns 404 when user is not found", %{conn: conn, user: user} do
421 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
422 assert conn.status == 404
423 assert conn.resp_body == ""
426 test "it returns 400 when user is not local", %{conn: conn, user: user} do
427 {:ok, user} = Repo.update(Changeset.change(user, local: false))
428 conn = post(conn, "/auth/password?email=#{user.email}")
429 assert conn.status == 400
430 assert conn.resp_body == ""
434 describe "GET /api/v1/suggestions" do
437 other_user = insert(:user)
438 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
439 url500 = "http://test500?#{host}&#{user.nickname}"
440 url200 = "http://test200?#{host}&#{user.nickname}"
443 %{method: :get, url: ^url500} ->
444 %Tesla.Env{status: 500, body: "bad request"}
446 %{method: :get, url: ^url200} ->
450 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
452 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
456 [user: user, other_user: other_user]
459 clear_config(:suggestions)
461 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
462 Config.put([:suggestions, :enabled], false)
466 |> assign(:user, user)
467 |> get("/api/v1/suggestions")
468 |> json_response(200)
473 test "returns error", %{conn: conn, user: user} do
474 Config.put([:suggestions, :enabled], true)
475 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
477 assert capture_log(fn ->
480 |> assign(:user, user)
481 |> get("/api/v1/suggestions")
482 |> json_response(500)
484 assert res == "Something went wrong"
485 end) =~ "Could not retrieve suggestions"
488 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
489 Config.put([:suggestions, :enabled], true)
490 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
494 |> assign(:user, user)
495 |> get("/api/v1/suggestions")
496 |> json_response(200)
501 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
502 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
506 "acct" => other_user.ap_id,
507 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
508 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
509 "id" => other_user.id
515 describe "DELETE /auth/sign_out" do
516 test "redirect to root page", %{conn: conn} do
521 |> assign(:user, user)
522 |> delete("/auth/sign_out")
524 assert conn.status == 302
525 assert redirected_to(conn) == "/"
529 describe "empty_array, stubs for mastodon api" do
530 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
535 |> assign(:user, user)
536 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
537 |> json_response(200)
542 test "GET /api/v1/endorsements", %{conn: conn} do
547 |> assign(:user, user)
548 |> get("/api/v1/endorsements")
549 |> json_response(200)
554 test "GET /api/v1/trends", %{conn: conn} do
559 |> assign(:user, user)
560 |> get("/api/v1/trends")
561 |> json_response(200)