Extract media actions from `MastodonAPIController` to `MediaController`
[akkoma] / test / web / mastodon_api / mastodon_api_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
7
8 alias Ecto.Changeset
9 alias Pleroma.Config
10 alias Pleroma.Notification
11 alias Pleroma.Object
12 alias Pleroma.Repo
13 alias Pleroma.Tests.ObanHelpers
14 alias Pleroma.User
15 alias Pleroma.Web.CommonAPI
16 alias Pleroma.Web.OAuth.App
17 alias Pleroma.Web.Push
18
19 import ExUnit.CaptureLog
20 import Pleroma.Factory
21 import Swoosh.TestAssertions
22 import Tesla.Mock
23
24 setup do
25 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
26 :ok
27 end
28
29 clear_config([:instance, :public])
30 clear_config([:rich_media, :enabled])
31
32 test "apps/verify_credentials", %{conn: conn} do
33 token = insert(:oauth_token)
34
35 conn =
36 conn
37 |> assign(:user, token.user)
38 |> assign(:token, token)
39 |> get("/api/v1/apps/verify_credentials")
40
41 app = Repo.preload(token, :app).app
42
43 expected = %{
44 "name" => app.client_name,
45 "website" => app.website,
46 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
47 }
48
49 assert expected == json_response(conn, 200)
50 end
51
52 test "creates an oauth app", %{conn: conn} do
53 user = insert(:user)
54 app_attrs = build(:oauth_app)
55
56 conn =
57 conn
58 |> assign(:user, user)
59 |> post("/api/v1/apps", %{
60 client_name: app_attrs.client_name,
61 redirect_uris: app_attrs.redirect_uris
62 })
63
64 [app] = Repo.all(App)
65
66 expected = %{
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)
74 }
75
76 assert expected == json_response(conn, 200)
77 end
78
79 test "getting a list of mutes", %{conn: conn} do
80 user = insert(:user)
81 other_user = insert(:user)
82
83 {:ok, user} = User.mute(user, other_user)
84
85 conn =
86 conn
87 |> assign(:user, user)
88 |> get("/api/v1/mutes")
89
90 other_user_id = to_string(other_user.id)
91 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
92 end
93
94 test "getting a list of blocks", %{conn: conn} do
95 user = insert(:user)
96 other_user = insert(:user)
97
98 {:ok, user} = User.block(user, other_user)
99
100 conn =
101 conn
102 |> assign(:user, user)
103 |> get("/api/v1/blocks")
104
105 other_user_id = to_string(other_user.id)
106 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
107 end
108
109 test "unimplemented follow_requests, blocks, domain blocks" do
110 user = insert(:user)
111
112 ["blocks", "domain_blocks", "follow_requests"]
113 |> Enum.each(fn endpoint ->
114 conn =
115 build_conn()
116 |> assign(:user, user)
117 |> get("/api/v1/#{endpoint}")
118
119 assert [] = json_response(conn, 200)
120 end)
121 end
122
123 test "returns the favorites of a user", %{conn: conn} do
124 user = insert(:user)
125 other_user = insert(:user)
126
127 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
128 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
129
130 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
131
132 first_conn =
133 conn
134 |> assign(:user, user)
135 |> get("/api/v1/favourites")
136
137 assert [status] = json_response(first_conn, 200)
138 assert status["id"] == to_string(activity.id)
139
140 assert [{"link", _link_header}] =
141 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
142
143 # Honours query params
144 {:ok, second_activity} =
145 CommonAPI.post(other_user, %{
146 "status" =>
147 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
148 })
149
150 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
151
152 last_like = status["id"]
153
154 second_conn =
155 conn
156 |> assign(:user, user)
157 |> get("/api/v1/favourites?since_id=#{last_like}")
158
159 assert [second_status] = json_response(second_conn, 200)
160 assert second_status["id"] == to_string(second_activity.id)
161
162 third_conn =
163 conn
164 |> assign(:user, user)
165 |> get("/api/v1/favourites?limit=0")
166
167 assert [] = json_response(third_conn, 200)
168 end
169
170 test "get instance information", %{conn: conn} do
171 conn = get(conn, "/api/v1/instance")
172 assert result = json_response(conn, 200)
173
174 email = Config.get([:instance, :email])
175 # Note: not checking for "max_toot_chars" since it's optional
176 assert %{
177 "uri" => _,
178 "title" => _,
179 "description" => _,
180 "version" => _,
181 "email" => from_config_email,
182 "urls" => %{
183 "streaming_api" => _
184 },
185 "stats" => _,
186 "thumbnail" => _,
187 "languages" => _,
188 "registrations" => _,
189 "poll_limits" => _
190 } = result
191
192 assert email == from_config_email
193 end
194
195 test "get instance stats", %{conn: conn} do
196 user = insert(:user, %{local: true})
197
198 user2 = insert(:user, %{local: true})
199 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
200
201 insert(:user, %{local: false, nickname: "u@peer1.com"})
202 insert(:user, %{local: false, nickname: "u@peer2.com"})
203
204 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
205
206 # Stats should count users with missing or nil `info.deactivated` value
207
208 {:ok, _user} =
209 user.id
210 |> User.get_cached_by_id()
211 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
212
213 Pleroma.Stats.force_update()
214
215 conn = get(conn, "/api/v1/instance")
216
217 assert result = json_response(conn, 200)
218
219 stats = result["stats"]
220
221 assert stats
222 assert stats["user_count"] == 1
223 assert stats["status_count"] == 1
224 assert stats["domain_count"] == 2
225 end
226
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"})
230
231 Pleroma.Stats.force_update()
232
233 conn = get(conn, "/api/v1/instance/peers")
234
235 assert result = json_response(conn, 200)
236
237 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
238 end
239
240 test "put settings", %{conn: conn} do
241 user = insert(:user)
242
243 conn =
244 conn
245 |> assign(:user, user)
246 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
247
248 assert _result = json_response(conn, 200)
249
250 user = User.get_cached_by_ap_id(user.ap_id)
251 assert user.info.settings == %{"programming" => "socks"}
252 end
253
254 describe "link headers" do
255 test "preserves parameters in link headers", %{conn: conn} do
256 user = insert(:user)
257 other_user = insert(:user)
258
259 {:ok, activity1} =
260 CommonAPI.post(other_user, %{
261 "status" => "hi @#{user.nickname}",
262 "visibility" => "public"
263 })
264
265 {:ok, activity2} =
266 CommonAPI.post(other_user, %{
267 "status" => "hi @#{user.nickname}",
268 "visibility" => "public"
269 })
270
271 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
272 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
273
274 conn =
275 conn
276 |> assign(:user, user)
277 |> get("/api/v1/notifications", %{media_only: true})
278
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}/
283 end
284 end
285
286 describe "custom emoji" do
287 test "with tags", %{conn: conn} do
288 [emoji | _body] =
289 conn
290 |> get("/api/v1/custom_emojis")
291 |> json_response(200)
292
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")
300 end
301 end
302
303 describe "index/2 redirections" do
304 setup %{conn: conn} do
305 session_opts = [
306 store: :cookie,
307 key: "_test",
308 signing_salt: "cooldude"
309 ]
310
311 conn =
312 conn
313 |> Plug.Session.call(Plug.Session.init(session_opts))
314 |> fetch_session()
315
316 test_path = "/web/statuses/test"
317 %{conn: conn, path: test_path}
318 end
319
320 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
321 conn = get(conn, path)
322
323 assert conn.status == 302
324 assert redirected_to(conn) == "/web/login"
325 end
326
327 test "redirects not logged-in users to the login page on private instances", %{
328 conn: conn,
329 path: path
330 } do
331 Config.put([:instance, :public], false)
332
333 conn = get(conn, path)
334
335 assert conn.status == 302
336 assert redirected_to(conn) == "/web/login"
337 end
338
339 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
340 token = insert(:oauth_token)
341
342 conn =
343 conn
344 |> assign(:user, token.user)
345 |> put_session(:oauth_token, token.token)
346 |> get(path)
347
348 assert conn.status == 200
349 end
350
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)
354
355 assert return_to == path
356 end
357
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)
361
362 conn =
363 conn
364 |> put_session(:return_to, path)
365 |> get("/web/login", %{code: auth.token})
366
367 assert conn.status == 302
368 assert redirected_to(conn) == path
369 end
370
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)
374
375 conn = get(conn, "/web/login", %{code: auth.token})
376
377 assert conn.status == 302
378 assert redirected_to(conn) == "/web/getting-started"
379 end
380 end
381
382 describe "POST /auth/password, with valid parameters" do
383 setup %{conn: conn} do
384 user = insert(:user)
385 conn = post(conn, "/auth/password?email=#{user.email}")
386 %{conn: conn, user: user}
387 end
388
389 test "it returns 204", %{conn: conn} do
390 assert json_response(conn, :no_content)
391 end
392
393 test "it creates a PasswordResetToken record for user", %{user: user} do
394 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
395 assert token_record
396 end
397
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)
401
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])
405
406 assert_email_sent(
407 from: {instance_name, notify_email},
408 to: {user.name, user.email},
409 html_body: email.html_body
410 )
411 end
412 end
413
414 describe "POST /auth/password, with invalid parameters" do
415 setup do
416 user = insert(:user)
417 {:ok, user: user}
418 end
419
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 == ""
424 end
425
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 == ""
431 end
432 end
433
434 describe "GET /api/v1/suggestions" do
435 setup do
436 user = insert(:user)
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}"
441
442 mock(fn
443 %{method: :get, url: ^url500} ->
444 %Tesla.Env{status: 500, body: "bad request"}
445
446 %{method: :get, url: ^url200} ->
447 %Tesla.Env{
448 status: 200,
449 body:
450 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
451 other_user.ap_id
452 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
453 }
454 end)
455
456 [user: user, other_user: other_user]
457 end
458
459 clear_config(:suggestions)
460
461 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
462 Config.put([:suggestions, :enabled], false)
463
464 res =
465 conn
466 |> assign(:user, user)
467 |> get("/api/v1/suggestions")
468 |> json_response(200)
469
470 assert res == []
471 end
472
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}}")
476
477 assert capture_log(fn ->
478 res =
479 conn
480 |> assign(:user, user)
481 |> get("/api/v1/suggestions")
482 |> json_response(500)
483
484 assert res == "Something went wrong"
485 end) =~ "Could not retrieve suggestions"
486 end
487
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}}")
491
492 res =
493 conn
494 |> assign(:user, user)
495 |> get("/api/v1/suggestions")
496 |> json_response(200)
497
498 assert res == [
499 %{
500 "acct" => "yj455",
501 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
502 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
503 "id" => 0
504 },
505 %{
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
510 }
511 ]
512 end
513 end
514
515 describe "DELETE /auth/sign_out" do
516 test "redirect to root page", %{conn: conn} do
517 user = insert(:user)
518
519 conn =
520 conn
521 |> assign(:user, user)
522 |> delete("/auth/sign_out")
523
524 assert conn.status == 302
525 assert redirected_to(conn) == "/"
526 end
527 end
528
529 describe "empty_array, stubs for mastodon api" do
530 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
531 user = insert(:user)
532
533 res =
534 conn
535 |> assign(:user, user)
536 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
537 |> json_response(200)
538
539 assert res == []
540 end
541
542 test "GET /api/v1/endorsements", %{conn: conn} do
543 user = insert(:user)
544
545 res =
546 conn
547 |> assign(:user, user)
548 |> get("/api/v1/endorsements")
549 |> json_response(200)
550
551 assert res == []
552 end
553
554 test "GET /api/v1/trends", %{conn: conn} do
555 user = insert(:user)
556
557 res =
558 conn
559 |> assign(:user, user)
560 |> get("/api/v1/trends")
561 |> json_response(200)
562
563 assert res == []
564 end
565 end
566 end