Merge branch 'features/private-reblogs' into 'develop'
[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.Repo
12 alias Pleroma.Tests.ObanHelpers
13 alias Pleroma.User
14 alias Pleroma.Web.CommonAPI
15
16 import Pleroma.Factory
17 import Swoosh.TestAssertions
18 import Tesla.Mock
19
20 setup do
21 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
22 :ok
23 end
24
25 clear_config([:instance, :public])
26 clear_config([:rich_media, :enabled])
27
28 test "getting a list of mutes", %{conn: conn} do
29 user = insert(:user)
30 other_user = insert(:user)
31
32 {:ok, user} = User.mute(user, other_user)
33
34 conn =
35 conn
36 |> assign(:user, user)
37 |> get("/api/v1/mutes")
38
39 other_user_id = to_string(other_user.id)
40 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
41 end
42
43 test "getting a list of blocks", %{conn: conn} do
44 user = insert(:user)
45 other_user = insert(:user)
46
47 {:ok, user} = User.block(user, other_user)
48
49 conn =
50 conn
51 |> assign(:user, user)
52 |> get("/api/v1/blocks")
53
54 other_user_id = to_string(other_user.id)
55 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
56 end
57
58 test "unimplemented follow_requests, blocks, domain blocks" do
59 user = insert(:user)
60
61 ["blocks", "domain_blocks", "follow_requests"]
62 |> Enum.each(fn endpoint ->
63 conn =
64 build_conn()
65 |> assign(:user, user)
66 |> get("/api/v1/#{endpoint}")
67
68 assert [] = json_response(conn, 200)
69 end)
70 end
71
72 test "returns the favorites of a user", %{conn: conn} do
73 user = insert(:user)
74 other_user = insert(:user)
75
76 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
77 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
78
79 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
80
81 first_conn =
82 conn
83 |> assign(:user, user)
84 |> get("/api/v1/favourites")
85
86 assert [status] = json_response(first_conn, 200)
87 assert status["id"] == to_string(activity.id)
88
89 assert [{"link", _link_header}] =
90 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
91
92 # Honours query params
93 {:ok, second_activity} =
94 CommonAPI.post(other_user, %{
95 "status" =>
96 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
97 })
98
99 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
100
101 last_like = status["id"]
102
103 second_conn =
104 conn
105 |> assign(:user, user)
106 |> get("/api/v1/favourites?since_id=#{last_like}")
107
108 assert [second_status] = json_response(second_conn, 200)
109 assert second_status["id"] == to_string(second_activity.id)
110
111 third_conn =
112 conn
113 |> assign(:user, user)
114 |> get("/api/v1/favourites?limit=0")
115
116 assert [] = json_response(third_conn, 200)
117 end
118
119 test "get instance information", %{conn: conn} do
120 conn = get(conn, "/api/v1/instance")
121 assert result = json_response(conn, 200)
122
123 email = Config.get([:instance, :email])
124 # Note: not checking for "max_toot_chars" since it's optional
125 assert %{
126 "uri" => _,
127 "title" => _,
128 "description" => _,
129 "version" => _,
130 "email" => from_config_email,
131 "urls" => %{
132 "streaming_api" => _
133 },
134 "stats" => _,
135 "thumbnail" => _,
136 "languages" => _,
137 "registrations" => _,
138 "poll_limits" => _,
139 "upload_limit" => _,
140 "avatar_upload_limit" => _,
141 "background_upload_limit" => _,
142 "banner_upload_limit" => _
143 } = result
144
145 assert email == from_config_email
146 end
147
148 test "get instance stats", %{conn: conn} do
149 user = insert(:user, %{local: true})
150
151 user2 = insert(:user, %{local: true})
152 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
153
154 insert(:user, %{local: false, nickname: "u@peer1.com"})
155 insert(:user, %{local: false, nickname: "u@peer2.com"})
156
157 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
158
159 # Stats should count users with missing or nil `info.deactivated` value
160
161 {:ok, _user} =
162 user.id
163 |> User.get_cached_by_id()
164 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
165
166 Pleroma.Stats.force_update()
167
168 conn = get(conn, "/api/v1/instance")
169
170 assert result = json_response(conn, 200)
171
172 stats = result["stats"]
173
174 assert stats
175 assert stats["user_count"] == 1
176 assert stats["status_count"] == 1
177 assert stats["domain_count"] == 2
178 end
179
180 test "get peers", %{conn: conn} do
181 insert(:user, %{local: false, nickname: "u@peer1.com"})
182 insert(:user, %{local: false, nickname: "u@peer2.com"})
183
184 Pleroma.Stats.force_update()
185
186 conn = get(conn, "/api/v1/instance/peers")
187
188 assert result = json_response(conn, 200)
189
190 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
191 end
192
193 test "put settings", %{conn: conn} do
194 user = insert(:user)
195
196 conn =
197 conn
198 |> assign(:user, user)
199 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
200
201 assert _result = json_response(conn, 200)
202
203 user = User.get_cached_by_ap_id(user.ap_id)
204 assert user.info.settings == %{"programming" => "socks"}
205 end
206
207 describe "link headers" do
208 test "preserves parameters in link headers", %{conn: conn} do
209 user = insert(:user)
210 other_user = insert(:user)
211
212 {:ok, activity1} =
213 CommonAPI.post(other_user, %{
214 "status" => "hi @#{user.nickname}",
215 "visibility" => "public"
216 })
217
218 {:ok, activity2} =
219 CommonAPI.post(other_user, %{
220 "status" => "hi @#{user.nickname}",
221 "visibility" => "public"
222 })
223
224 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
225 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
226
227 conn =
228 conn
229 |> assign(:user, user)
230 |> get("/api/v1/notifications", %{media_only: true})
231
232 assert [link_header] = get_resp_header(conn, "link")
233 assert link_header =~ ~r/media_only=true/
234 assert link_header =~ ~r/min_id=#{notification2.id}/
235 assert link_header =~ ~r/max_id=#{notification1.id}/
236 end
237 end
238
239 describe "custom emoji" do
240 test "with tags", %{conn: conn} do
241 [emoji | _body] =
242 conn
243 |> get("/api/v1/custom_emojis")
244 |> json_response(200)
245
246 assert Map.has_key?(emoji, "shortcode")
247 assert Map.has_key?(emoji, "static_url")
248 assert Map.has_key?(emoji, "tags")
249 assert is_list(emoji["tags"])
250 assert Map.has_key?(emoji, "category")
251 assert Map.has_key?(emoji, "url")
252 assert Map.has_key?(emoji, "visible_in_picker")
253 end
254 end
255
256 describe "index/2 redirections" do
257 setup %{conn: conn} do
258 session_opts = [
259 store: :cookie,
260 key: "_test",
261 signing_salt: "cooldude"
262 ]
263
264 conn =
265 conn
266 |> Plug.Session.call(Plug.Session.init(session_opts))
267 |> fetch_session()
268
269 test_path = "/web/statuses/test"
270 %{conn: conn, path: test_path}
271 end
272
273 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
274 conn = get(conn, path)
275
276 assert conn.status == 302
277 assert redirected_to(conn) == "/web/login"
278 end
279
280 test "redirects not logged-in users to the login page on private instances", %{
281 conn: conn,
282 path: path
283 } do
284 Config.put([:instance, :public], false)
285
286 conn = get(conn, path)
287
288 assert conn.status == 302
289 assert redirected_to(conn) == "/web/login"
290 end
291
292 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
293 token = insert(:oauth_token)
294
295 conn =
296 conn
297 |> assign(:user, token.user)
298 |> put_session(:oauth_token, token.token)
299 |> get(path)
300
301 assert conn.status == 200
302 end
303
304 test "saves referer path to session", %{conn: conn, path: path} do
305 conn = get(conn, path)
306 return_to = Plug.Conn.get_session(conn, :return_to)
307
308 assert return_to == path
309 end
310
311 test "redirects to the saved path after log in", %{conn: conn, path: path} do
312 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
313 auth = insert(:oauth_authorization, app: app)
314
315 conn =
316 conn
317 |> put_session(:return_to, path)
318 |> get("/web/login", %{code: auth.token})
319
320 assert conn.status == 302
321 assert redirected_to(conn) == path
322 end
323
324 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
325 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
326 auth = insert(:oauth_authorization, app: app)
327
328 conn = get(conn, "/web/login", %{code: auth.token})
329
330 assert conn.status == 302
331 assert redirected_to(conn) == "/web/getting-started"
332 end
333 end
334
335 describe "POST /auth/password, with valid parameters" do
336 setup %{conn: conn} do
337 user = insert(:user)
338 conn = post(conn, "/auth/password?email=#{user.email}")
339 %{conn: conn, user: user}
340 end
341
342 test "it returns 204", %{conn: conn} do
343 assert json_response(conn, :no_content)
344 end
345
346 test "it creates a PasswordResetToken record for user", %{user: user} do
347 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
348 assert token_record
349 end
350
351 test "it sends an email to user", %{user: user} do
352 ObanHelpers.perform_all()
353 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
354
355 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
356 notify_email = Config.get([:instance, :notify_email])
357 instance_name = Config.get([:instance, :name])
358
359 assert_email_sent(
360 from: {instance_name, notify_email},
361 to: {user.name, user.email},
362 html_body: email.html_body
363 )
364 end
365 end
366
367 describe "POST /auth/password, with invalid parameters" do
368 setup do
369 user = insert(:user)
370 {:ok, user: user}
371 end
372
373 test "it returns 404 when user is not found", %{conn: conn, user: user} do
374 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
375 assert conn.status == 404
376 assert conn.resp_body == ""
377 end
378
379 test "it returns 400 when user is not local", %{conn: conn, user: user} do
380 {:ok, user} = Repo.update(Changeset.change(user, local: false))
381 conn = post(conn, "/auth/password?email=#{user.email}")
382 assert conn.status == 400
383 assert conn.resp_body == ""
384 end
385 end
386
387 describe "DELETE /auth/sign_out" do
388 test "redirect to root page", %{conn: conn} do
389 user = insert(:user)
390
391 conn =
392 conn
393 |> assign(:user, user)
394 |> delete("/auth/sign_out")
395
396 assert conn.status == 302
397 assert redirected_to(conn) == "/"
398 end
399 end
400
401 describe "empty_array, stubs for mastodon api" do
402 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
403 user = insert(:user)
404
405 res =
406 conn
407 |> assign(:user, user)
408 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
409 |> json_response(200)
410
411 assert res == []
412 end
413
414 test "GET /api/v1/endorsements", %{conn: conn} do
415 user = insert(:user)
416
417 res =
418 conn
419 |> assign(:user, user)
420 |> get("/api/v1/endorsements")
421 |> json_response(200)
422
423 assert res == []
424 end
425
426 test "GET /api/v1/trends", %{conn: conn} do
427 user = insert(:user)
428
429 res =
430 conn
431 |> assign(:user, user)
432 |> get("/api/v1/trends")
433 |> json_response(200)
434
435 assert res == []
436 end
437 end
438 end