79b3adc69de01889d74e8aa43f92420358ef5f38
[akkoma] / test / web / mastodon_api / controllers / account_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
6 use Pleroma.Web.ConnCase
7
8 alias Pleroma.Config
9 alias Pleroma.Repo
10 alias Pleroma.User
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.InternalFetchActor
13 alias Pleroma.Web.ApiSpec
14 alias Pleroma.Web.CommonAPI
15 alias Pleroma.Web.OAuth.Token
16
17 import OpenApiSpex.TestAssertions
18 import Pleroma.Factory
19
20 describe "account fetching" do
21 setup do: clear_config([:instance, :limit_to_local_content])
22
23 test "works by id" do
24 user = insert(:user)
25
26 conn =
27 build_conn()
28 |> get("/api/v1/accounts/#{user.id}")
29
30 assert %{"id" => id} = json_response(conn, 200)
31 assert id == to_string(user.id)
32
33 conn =
34 build_conn()
35 |> get("/api/v1/accounts/-1")
36
37 assert %{"error" => "Can't find user"} = json_response(conn, 404)
38 end
39
40 test "works by nickname" do
41 user = insert(:user)
42
43 conn =
44 build_conn()
45 |> get("/api/v1/accounts/#{user.nickname}")
46
47 assert %{"id" => id} = json_response(conn, 200)
48 assert id == user.id
49 end
50
51 test "works by nickname for remote users" do
52 Config.put([:instance, :limit_to_local_content], false)
53 user = insert(:user, nickname: "user@example.com", local: false)
54
55 conn =
56 build_conn()
57 |> get("/api/v1/accounts/#{user.nickname}")
58
59 assert %{"id" => id} = json_response(conn, 200)
60 assert id == user.id
61 end
62
63 test "respects limit_to_local_content == :all for remote user nicknames" do
64 Config.put([:instance, :limit_to_local_content], :all)
65
66 user = insert(:user, nickname: "user@example.com", local: false)
67
68 conn =
69 build_conn()
70 |> get("/api/v1/accounts/#{user.nickname}")
71
72 assert json_response(conn, 404)
73 end
74
75 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
76 Config.put([:instance, :limit_to_local_content], :unauthenticated)
77
78 user = insert(:user, nickname: "user@example.com", local: false)
79 reading_user = insert(:user)
80
81 conn =
82 build_conn()
83 |> get("/api/v1/accounts/#{user.nickname}")
84
85 assert json_response(conn, 404)
86
87 conn =
88 build_conn()
89 |> assign(:user, reading_user)
90 |> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
91 |> get("/api/v1/accounts/#{user.nickname}")
92
93 assert %{"id" => id} = json_response(conn, 200)
94 assert id == user.id
95 end
96
97 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
98 # Need to set an old-style integer ID to reproduce the problem
99 # (these are no longer assigned to new accounts but were preserved
100 # for existing accounts during the migration to flakeIDs)
101 user_one = insert(:user, %{id: 1212})
102 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
103
104 resp_one =
105 conn
106 |> get("/api/v1/accounts/#{user_one.id}")
107
108 resp_two =
109 conn
110 |> get("/api/v1/accounts/#{user_two.nickname}")
111
112 resp_three =
113 conn
114 |> get("/api/v1/accounts/#{user_two.id}")
115
116 acc_one = json_response(resp_one, 200)
117 acc_two = json_response(resp_two, 200)
118 acc_three = json_response(resp_three, 200)
119 refute acc_one == acc_two
120 assert acc_two == acc_three
121 end
122
123 test "returns 404 when user is invisible", %{conn: conn} do
124 user = insert(:user, %{invisible: true})
125
126 resp =
127 conn
128 |> get("/api/v1/accounts/#{user.nickname}")
129 |> json_response(404)
130
131 assert %{"error" => "Can't find user"} = resp
132 end
133
134 test "returns 404 for internal.fetch actor", %{conn: conn} do
135 %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor()
136
137 resp =
138 conn
139 |> get("/api/v1/accounts/internal.fetch")
140 |> json_response(404)
141
142 assert %{"error" => "Can't find user"} = resp
143 end
144 end
145
146 defp local_and_remote_users do
147 local = insert(:user)
148 remote = insert(:user, local: false)
149 {:ok, local: local, remote: remote}
150 end
151
152 describe "user fetching with restrict unauthenticated profiles for local and remote" do
153 setup do: local_and_remote_users()
154
155 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
156
157 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
158
159 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
160 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
161
162 assert json_response(res_conn, :not_found) == %{
163 "error" => "Can't find user"
164 }
165
166 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
167
168 assert json_response(res_conn, :not_found) == %{
169 "error" => "Can't find user"
170 }
171 end
172
173 test "if user is authenticated", %{local: local, remote: remote} do
174 %{conn: conn} = oauth_access(["read"])
175
176 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
177 assert %{"id" => _} = json_response(res_conn, 200)
178
179 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
180 assert %{"id" => _} = json_response(res_conn, 200)
181 end
182 end
183
184 describe "user fetching with restrict unauthenticated profiles for local" do
185 setup do: local_and_remote_users()
186
187 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
188
189 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
190 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
191
192 assert json_response(res_conn, :not_found) == %{
193 "error" => "Can't find user"
194 }
195
196 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
197 assert %{"id" => _} = json_response(res_conn, 200)
198 end
199
200 test "if user is authenticated", %{local: local, remote: remote} do
201 %{conn: conn} = oauth_access(["read"])
202
203 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
204 assert %{"id" => _} = json_response(res_conn, 200)
205
206 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
207 assert %{"id" => _} = json_response(res_conn, 200)
208 end
209 end
210
211 describe "user fetching with restrict unauthenticated profiles for remote" do
212 setup do: local_and_remote_users()
213
214 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
215
216 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
217 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
218 assert %{"id" => _} = json_response(res_conn, 200)
219
220 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
221
222 assert json_response(res_conn, :not_found) == %{
223 "error" => "Can't find user"
224 }
225 end
226
227 test "if user is authenticated", %{local: local, remote: remote} do
228 %{conn: conn} = oauth_access(["read"])
229
230 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
231 assert %{"id" => _} = json_response(res_conn, 200)
232
233 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
234 assert %{"id" => _} = json_response(res_conn, 200)
235 end
236 end
237
238 describe "user timelines" do
239 setup do: oauth_access(["read:statuses"])
240
241 test "respects blocks", %{user: user_one, conn: conn} do
242 user_two = insert(:user)
243 user_three = insert(:user)
244
245 User.block(user_one, user_two)
246
247 {:ok, activity} = CommonAPI.post(user_two, %{"status" => "User one sux0rz"})
248 {:ok, repeat, _} = CommonAPI.repeat(activity.id, user_three)
249
250 assert resp = get(conn, "/api/v1/accounts/#{user_two.id}/statuses") |> json_response(200)
251 assert [%{"id" => id}] = resp
252 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
253 assert id == activity.id
254
255 # Even a blocked user will deliver the full user timeline, there would be
256 # no point in looking at a blocked users timeline otherwise
257 assert resp = get(conn, "/api/v1/accounts/#{user_two.id}/statuses") |> json_response(200)
258 assert [%{"id" => id}] = resp
259 assert id == activity.id
260 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
261
262 # Third user's timeline includes the repeat when viewed by unauthenticated user
263 resp = get(build_conn(), "/api/v1/accounts/#{user_three.id}/statuses") |> json_response(200)
264 assert [%{"id" => id}] = resp
265 assert id == repeat.id
266 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
267
268 # When viewing a third user's timeline, the blocked users' statuses will NOT be shown
269 resp = get(conn, "/api/v1/accounts/#{user_three.id}/statuses")
270
271 assert [] = json_response(resp, 200)
272 end
273
274 test "gets users statuses", %{conn: conn} do
275 user_one = insert(:user)
276 user_two = insert(:user)
277 user_three = insert(:user)
278
279 {:ok, _user_three} = User.follow(user_three, user_one)
280
281 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
282
283 {:ok, direct_activity} =
284 CommonAPI.post(user_one, %{
285 "status" => "Hi, @#{user_two.nickname}.",
286 "visibility" => "direct"
287 })
288
289 {:ok, private_activity} =
290 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
291
292 resp = get(conn, "/api/v1/accounts/#{user_one.id}/statuses") |> json_response(200)
293 assert [%{"id" => id}] = resp
294 assert id == to_string(activity.id)
295 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
296
297 resp =
298 conn
299 |> assign(:user, user_two)
300 |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
301 |> get("/api/v1/accounts/#{user_one.id}/statuses")
302 |> json_response(200)
303
304 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
305 assert id_one == to_string(direct_activity.id)
306 assert id_two == to_string(activity.id)
307 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
308
309 resp =
310 conn
311 |> assign(:user, user_three)
312 |> assign(:token, insert(:oauth_token, user: user_three, scopes: ["read:statuses"]))
313 |> get("/api/v1/accounts/#{user_one.id}/statuses")
314 |> json_response(200)
315
316 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
317 assert id_one == to_string(private_activity.id)
318 assert id_two == to_string(activity.id)
319 assert_schema(resp, "StatusesResponse", ApiSpec.spec())
320 end
321
322 test "unimplemented pinned statuses feature", %{conn: conn} do
323 note = insert(:note_activity)
324 user = User.get_cached_by_ap_id(note.data["actor"])
325
326 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?pinned=true")
327
328 assert json_response(conn, 200) == []
329 end
330
331 test "gets an users media", %{conn: conn} do
332 note = insert(:note_activity)
333 user = User.get_cached_by_ap_id(note.data["actor"])
334
335 file = %Plug.Upload{
336 content_type: "image/jpg",
337 path: Path.absname("test/fixtures/image.jpg"),
338 filename: "an_image.jpg"
339 }
340
341 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
342
343 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
344
345 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")
346
347 assert [%{"id" => id}] = json_response(conn, 200)
348 assert id == to_string(image_post.id)
349 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
350
351 conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses?only_media=1")
352
353 assert [%{"id" => id}] = json_response(conn, 200)
354 assert id == to_string(image_post.id)
355 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
356 end
357
358 test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
359 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
360 {:ok, _, _} = CommonAPI.repeat(post.id, user)
361
362 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
363
364 assert [%{"id" => id}] = json_response(conn, 200)
365 assert id == to_string(post.id)
366 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
367
368 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=1")
369
370 assert [%{"id" => id}] = json_response(conn, 200)
371 assert id == to_string(post.id)
372 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
373 end
374
375 test "filters user's statuses by a hashtag", %{user: user, conn: conn} do
376 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
377 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
378
379 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?tagged=hashtag")
380
381 assert [%{"id" => id}] = json_response(conn, 200)
382 assert id == to_string(post.id)
383 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
384 end
385
386 test "the user views their own timelines and excludes direct messages", %{
387 user: user,
388 conn: conn
389 } do
390 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
391 {:ok, _direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
392
393 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_visibilities[]=direct")
394
395 assert [%{"id" => id}] = json_response(conn, 200)
396 assert id == to_string(public_activity.id)
397 assert_schema(json_response(conn, 200), "StatusesResponse", ApiSpec.spec())
398 end
399 end
400
401 defp local_and_remote_activities(%{local: local, remote: remote}) do
402 insert(:note_activity, user: local)
403 insert(:note_activity, user: remote, local: false)
404
405 :ok
406 end
407
408 describe "statuses with restrict unauthenticated profiles for local and remote" do
409 setup do: local_and_remote_users()
410 setup :local_and_remote_activities
411
412 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
413
414 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
415
416 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
417 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
418
419 assert json_response(res_conn, :not_found) == %{
420 "error" => "Can't find user"
421 }
422
423 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
424
425 assert json_response(res_conn, :not_found) == %{
426 "error" => "Can't find user"
427 }
428 end
429
430 test "if user is authenticated", %{local: local, remote: remote} do
431 %{conn: conn} = oauth_access(["read"])
432
433 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
434 assert length(json_response(res_conn, 200)) == 1
435 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
436
437 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
438 assert length(json_response(res_conn, 200)) == 1
439 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
440 end
441 end
442
443 describe "statuses with restrict unauthenticated profiles for local" do
444 setup do: local_and_remote_users()
445 setup :local_and_remote_activities
446
447 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
448
449 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
450 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
451
452 assert json_response(res_conn, :not_found) == %{
453 "error" => "Can't find user"
454 }
455
456 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
457 assert length(json_response(res_conn, 200)) == 1
458 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
459 end
460
461 test "if user is authenticated", %{local: local, remote: remote} do
462 %{conn: conn} = oauth_access(["read"])
463
464 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
465 assert length(json_response(res_conn, 200)) == 1
466 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
467
468 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
469 assert length(json_response(res_conn, 200)) == 1
470 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
471 end
472 end
473
474 describe "statuses with restrict unauthenticated profiles for remote" do
475 setup do: local_and_remote_users()
476 setup :local_and_remote_activities
477
478 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
479
480 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
481 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
482 assert length(json_response(res_conn, 200)) == 1
483 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
484
485 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
486
487 assert json_response(res_conn, :not_found) == %{
488 "error" => "Can't find user"
489 }
490 end
491
492 test "if user is authenticated", %{local: local, remote: remote} do
493 %{conn: conn} = oauth_access(["read"])
494
495 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
496 assert length(json_response(res_conn, 200)) == 1
497 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
498
499 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
500 assert length(json_response(res_conn, 200)) == 1
501 assert_schema(json_response(res_conn, 200), "StatusesResponse", ApiSpec.spec())
502 end
503 end
504
505 describe "followers" do
506 setup do: oauth_access(["read:accounts"])
507
508 test "getting followers", %{user: user, conn: conn} do
509 other_user = insert(:user)
510 {:ok, user} = User.follow(user, other_user)
511
512 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
513
514 assert [%{"id" => id}] = json_response(conn, 200)
515 assert id == to_string(user.id)
516 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
517 end
518
519 test "getting followers, hide_followers", %{user: user, conn: conn} do
520 other_user = insert(:user, hide_followers: true)
521 {:ok, _user} = User.follow(user, other_user)
522
523 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
524
525 assert [] == json_response(conn, 200)
526 end
527
528 test "getting followers, hide_followers, same user requesting" do
529 user = insert(:user)
530 other_user = insert(:user, hide_followers: true)
531 {:ok, _user} = User.follow(user, other_user)
532
533 conn =
534 build_conn()
535 |> assign(:user, other_user)
536 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
537 |> get("/api/v1/accounts/#{other_user.id}/followers")
538
539 refute [] == json_response(conn, 200)
540 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
541 end
542
543 test "getting followers, pagination", %{user: user, conn: conn} do
544 follower1 = insert(:user)
545 follower2 = insert(:user)
546 follower3 = insert(:user)
547 {:ok, _} = User.follow(follower1, user)
548 {:ok, _} = User.follow(follower2, user)
549 {:ok, _} = User.follow(follower3, user)
550
551 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
552
553 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
554 assert id3 == follower3.id
555 assert id2 == follower2.id
556 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
557
558 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
559
560 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
561 assert id2 == follower2.id
562 assert id1 == follower1.id
563
564 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
565
566 assert [%{"id" => id2}] = json_response(res_conn, 200)
567 assert id2 == follower2.id
568
569 assert [link_header] = get_resp_header(res_conn, "link")
570 assert link_header =~ ~r/min_id=#{follower2.id}/
571 assert link_header =~ ~r/max_id=#{follower2.id}/
572 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
573 end
574 end
575
576 describe "following" do
577 setup do: oauth_access(["read:accounts"])
578
579 test "getting following", %{user: user, conn: conn} do
580 other_user = insert(:user)
581 {:ok, user} = User.follow(user, other_user)
582
583 conn = get(conn, "/api/v1/accounts/#{user.id}/following")
584
585 assert [%{"id" => id}] = json_response(conn, 200)
586 assert id == to_string(other_user.id)
587 end
588
589 test "getting following, hide_follows, other user requesting" do
590 user = insert(:user, hide_follows: true)
591 other_user = insert(:user)
592 {:ok, user} = User.follow(user, other_user)
593
594 conn =
595 build_conn()
596 |> assign(:user, other_user)
597 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
598 |> get("/api/v1/accounts/#{user.id}/following")
599
600 assert [] == json_response(conn, 200)
601 end
602
603 test "getting following, hide_follows, same user requesting" do
604 user = insert(:user, hide_follows: true)
605 other_user = insert(:user)
606 {:ok, user} = User.follow(user, other_user)
607
608 conn =
609 build_conn()
610 |> assign(:user, user)
611 |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"]))
612 |> get("/api/v1/accounts/#{user.id}/following")
613
614 refute [] == json_response(conn, 200)
615 end
616
617 test "getting following, pagination", %{user: user, conn: conn} do
618 following1 = insert(:user)
619 following2 = insert(:user)
620 following3 = insert(:user)
621 {:ok, _} = User.follow(user, following1)
622 {:ok, _} = User.follow(user, following2)
623 {:ok, _} = User.follow(user, following3)
624
625 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
626
627 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
628 assert id3 == following3.id
629 assert id2 == following2.id
630
631 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
632
633 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
634 assert id2 == following2.id
635 assert id1 == following1.id
636
637 res_conn =
638 get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
639
640 assert [%{"id" => id2}] = json_response(res_conn, 200)
641 assert id2 == following2.id
642
643 assert [link_header] = get_resp_header(res_conn, "link")
644 assert link_header =~ ~r/min_id=#{following2.id}/
645 assert link_header =~ ~r/max_id=#{following2.id}/
646 end
647 end
648
649 describe "follow/unfollow" do
650 setup do: oauth_access(["follow"])
651
652 test "following / unfollowing a user", %{conn: conn} do
653 other_user = insert(:user)
654
655 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/follow")
656
657 assert %{"id" => _id, "following" => true} = json_response(ret_conn, 200)
658
659 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/unfollow")
660
661 assert %{"id" => _id, "following" => false} = json_response(ret_conn, 200)
662
663 conn = post(conn, "/api/v1/follows", %{"uri" => other_user.nickname})
664
665 assert %{"id" => id} = json_response(conn, 200)
666 assert id == to_string(other_user.id)
667 end
668
669 test "cancelling follow request", %{conn: conn} do
670 %{id: other_user_id} = insert(:user, %{locked: true})
671
672 assert %{"id" => ^other_user_id, "following" => false, "requested" => true} =
673 conn |> post("/api/v1/accounts/#{other_user_id}/follow") |> json_response(:ok)
674
675 assert %{"id" => ^other_user_id, "following" => false, "requested" => false} =
676 conn |> post("/api/v1/accounts/#{other_user_id}/unfollow") |> json_response(:ok)
677 end
678
679 test "following without reblogs" do
680 %{conn: conn} = oauth_access(["follow", "read:statuses"])
681 followed = insert(:user)
682 other_user = insert(:user)
683
684 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false")
685
686 assert %{"showing_reblogs" => false} = json_response(ret_conn, 200)
687
688 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
689 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
690
691 ret_conn = get(conn, "/api/v1/timelines/home")
692
693 assert [] == json_response(ret_conn, 200)
694
695 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=true")
696
697 assert %{"showing_reblogs" => true} = json_response(ret_conn, 200)
698
699 conn = get(conn, "/api/v1/timelines/home")
700
701 expected_activity_id = reblog.id
702 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
703 end
704
705 test "following / unfollowing errors", %{user: user, conn: conn} do
706 # self follow
707 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
708 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
709
710 # self unfollow
711 user = User.get_cached_by_id(user.id)
712 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
713 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
714
715 # self follow via uri
716 user = User.get_cached_by_id(user.id)
717 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
718 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
719
720 # follow non existing user
721 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
722 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
723
724 # follow non existing user via uri
725 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
726 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
727
728 # unfollow non existing user
729 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
730 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
731 end
732 end
733
734 describe "mute/unmute" do
735 setup do: oauth_access(["write:mutes"])
736
737 test "with notifications", %{conn: conn} do
738 other_user = insert(:user)
739
740 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/mute")
741
742 response = json_response(ret_conn, 200)
743
744 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
745
746 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
747
748 response = json_response(conn, 200)
749 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
750 end
751
752 test "without notifications", %{conn: conn} do
753 other_user = insert(:user)
754
755 ret_conn =
756 post(conn, "/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
757
758 response = json_response(ret_conn, 200)
759
760 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
761
762 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
763
764 response = json_response(conn, 200)
765 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
766 end
767 end
768
769 describe "pinned statuses" do
770 setup do
771 user = insert(:user)
772 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
773 %{conn: conn} = oauth_access(["read:statuses"], user: user)
774
775 [conn: conn, user: user, activity: activity]
776 end
777
778 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
779 {:ok, _} = CommonAPI.pin(activity.id, user)
780
781 result =
782 conn
783 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
784 |> json_response(200)
785
786 id_str = to_string(activity.id)
787
788 assert [%{"id" => ^id_str, "pinned" => true}] = result
789 end
790 end
791
792 test "blocking / unblocking a user" do
793 %{conn: conn} = oauth_access(["follow"])
794 other_user = insert(:user)
795
796 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
797
798 assert %{"id" => _id, "blocking" => true} = json_response(ret_conn, 200)
799
800 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
801
802 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
803 end
804
805 describe "create account by app" do
806 setup do
807 valid_params = %{
808 username: "lain",
809 email: "lain@example.org",
810 password: "PlzDontHackLain",
811 agreement: true
812 }
813
814 [valid_params: valid_params]
815 end
816
817 setup do: clear_config([:instance, :account_activation_required])
818
819 test "Account registration via Application", %{conn: conn} do
820 conn =
821 conn
822 |> put_req_header("content-type", "application/json")
823 |> post("/api/v1/apps", %{
824 client_name: "client_name",
825 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
826 scopes: "read, write, follow"
827 })
828
829 %{
830 "client_id" => client_id,
831 "client_secret" => client_secret,
832 "id" => _,
833 "name" => "client_name",
834 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
835 "vapid_key" => _,
836 "website" => nil
837 } = json_response(conn, 200)
838
839 conn =
840 post(conn, "/oauth/token", %{
841 grant_type: "client_credentials",
842 client_id: client_id,
843 client_secret: client_secret
844 })
845
846 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
847 json_response(conn, 200)
848
849 assert token
850 token_from_db = Repo.get_by(Token, token: token)
851 assert token_from_db
852 assert refresh
853 assert scope == "read write follow"
854
855 conn =
856 build_conn()
857 |> put_req_header("content-type", "multipart/form-data")
858 |> put_req_header("authorization", "Bearer " <> token)
859 |> post("/api/v1/accounts", %{
860 username: "lain",
861 email: "lain@example.org",
862 password: "PlzDontHackLain",
863 bio: "Test Bio",
864 agreement: true
865 })
866
867 %{
868 "access_token" => token,
869 "created_at" => _created_at,
870 "scope" => _scope,
871 "token_type" => "Bearer"
872 } = json_response(conn, 200)
873
874 token_from_db = Repo.get_by(Token, token: token)
875 assert token_from_db
876 token_from_db = Repo.preload(token_from_db, :user)
877 assert token_from_db.user
878
879 assert token_from_db.user.confirmation_pending
880 end
881
882 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
883 _user = insert(:user, email: "lain@example.org")
884 app_token = insert(:oauth_token, user: nil)
885
886 res =
887 conn
888 |> put_req_header("authorization", "Bearer " <> app_token.token)
889 |> put_req_header("content-type", "application/json")
890 |> post("/api/v1/accounts", valid_params)
891
892 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
893 end
894
895 test "returns bad_request if missing required params", %{
896 conn: conn,
897 valid_params: valid_params
898 } do
899 app_token = insert(:oauth_token, user: nil)
900
901 conn =
902 conn
903 |> put_req_header("authorization", "Bearer " <> app_token.token)
904 |> put_req_header("content-type", "application/json")
905
906 res = post(conn, "/api/v1/accounts", valid_params)
907 assert json_response(res, 200)
908
909 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
910 |> Stream.zip(Map.delete(valid_params, :email))
911 |> Enum.each(fn {ip, {attr, _}} ->
912 res =
913 conn
914 |> Map.put(:remote_ip, ip)
915 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
916 |> json_response(400)
917
918 assert res == %{"error" => "Missing parameters"}
919 end)
920 end
921
922 setup do: clear_config([:instance, :account_activation_required])
923
924 test "returns bad_request if missing email params when :account_activation_required is enabled",
925 %{conn: conn, valid_params: valid_params} do
926 Pleroma.Config.put([:instance, :account_activation_required], true)
927
928 app_token = insert(:oauth_token, user: nil)
929
930 conn =
931 conn
932 |> put_req_header("authorization", "Bearer " <> app_token.token)
933 |> put_req_header("content-type", "application/json")
934
935 res =
936 conn
937 |> Map.put(:remote_ip, {127, 0, 0, 5})
938 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
939
940 assert json_response(res, 400) == %{"error" => "Missing parameters"}
941
942 res =
943 conn
944 |> Map.put(:remote_ip, {127, 0, 0, 6})
945 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
946
947 assert json_response(res, 400) == %{"error" => "{\"email\":[\"can't be blank\"]}"}
948 end
949
950 test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
951 app_token = insert(:oauth_token, user: nil)
952 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
953
954 res =
955 conn
956 |> put_req_header("content-type", "application/json")
957 |> Map.put(:remote_ip, {127, 0, 0, 7})
958 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
959
960 assert json_response(res, 200)
961 end
962
963 test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
964 app_token = insert(:oauth_token, user: nil)
965 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
966
967 res =
968 conn
969 |> put_req_header("content-type", "application/json")
970 |> Map.put(:remote_ip, {127, 0, 0, 8})
971 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
972
973 assert json_response(res, 200)
974 end
975
976 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
977 res =
978 conn
979 |> put_req_header("authorization", "Bearer " <> "invalid-token")
980 |> put_req_header("content-type", "multipart/form-data")
981 |> post("/api/v1/accounts", valid_params)
982
983 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
984 end
985 end
986
987 describe "create account by app / rate limit" do
988 setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
989
990 test "respects rate limit setting", %{conn: conn} do
991 app_token = insert(:oauth_token, user: nil)
992
993 conn =
994 conn
995 |> put_req_header("authorization", "Bearer " <> app_token.token)
996 |> Map.put(:remote_ip, {15, 15, 15, 15})
997 |> put_req_header("content-type", "multipart/form-data")
998
999 for i <- 1..2 do
1000 conn =
1001 conn
1002 |> post("/api/v1/accounts", %{
1003 username: "#{i}lain",
1004 email: "#{i}lain@example.org",
1005 password: "PlzDontHackLain",
1006 agreement: true
1007 })
1008
1009 %{
1010 "access_token" => token,
1011 "created_at" => _created_at,
1012 "scope" => _scope,
1013 "token_type" => "Bearer"
1014 } = json_response(conn, 200)
1015
1016 token_from_db = Repo.get_by(Token, token: token)
1017 assert token_from_db
1018 token_from_db = Repo.preload(token_from_db, :user)
1019 assert token_from_db.user
1020
1021 assert token_from_db.user.confirmation_pending
1022 end
1023
1024 conn =
1025 post(conn, "/api/v1/accounts", %{
1026 username: "6lain",
1027 email: "6lain@example.org",
1028 password: "PlzDontHackLain",
1029 agreement: true
1030 })
1031
1032 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
1033 end
1034 end
1035
1036 describe "GET /api/v1/accounts/:id/lists - account_lists" do
1037 test "returns lists to which the account belongs" do
1038 %{user: user, conn: conn} = oauth_access(["read:lists"])
1039 other_user = insert(:user)
1040 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
1041 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
1042
1043 res =
1044 conn
1045 |> get("/api/v1/accounts/#{other_user.id}/lists")
1046 |> json_response(200)
1047
1048 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
1049 end
1050 end
1051
1052 describe "verify_credentials" do
1053 test "verify_credentials" do
1054 %{user: user, conn: conn} = oauth_access(["read:accounts"])
1055 conn = get(conn, "/api/v1/accounts/verify_credentials")
1056
1057 response = json_response(conn, 200)
1058
1059 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
1060 assert response["pleroma"]["chat_token"]
1061 assert id == to_string(user.id)
1062 end
1063
1064 test "verify_credentials default scope unlisted" do
1065 user = insert(:user, default_scope: "unlisted")
1066 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1067
1068 conn = get(conn, "/api/v1/accounts/verify_credentials")
1069
1070 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
1071 assert id == to_string(user.id)
1072 end
1073
1074 test "locked accounts" do
1075 user = insert(:user, default_scope: "private")
1076 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1077
1078 conn = get(conn, "/api/v1/accounts/verify_credentials")
1079
1080 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1081 assert id == to_string(user.id)
1082 end
1083 end
1084
1085 describe "user relationships" do
1086 setup do: oauth_access(["read:follows"])
1087
1088 test "returns the relationships for the current user", %{user: user, conn: conn} do
1089 %{id: other_user_id} = other_user = insert(:user)
1090 {:ok, _user} = User.follow(user, other_user)
1091
1092 assert [%{"id" => ^other_user_id}] =
1093 conn
1094 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1095 |> json_response(200)
1096
1097 assert [%{"id" => ^other_user_id}] =
1098 conn
1099 |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
1100 |> json_response(200)
1101 end
1102
1103 test "returns an empty list on a bad request", %{conn: conn} do
1104 conn = get(conn, "/api/v1/accounts/relationships", %{})
1105
1106 assert [] = json_response(conn, 200)
1107 end
1108 end
1109
1110 test "getting a list of mutes" do
1111 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1112 other_user = insert(:user)
1113
1114 {:ok, _user_relationships} = User.mute(user, other_user)
1115
1116 conn = get(conn, "/api/v1/mutes")
1117
1118 other_user_id = to_string(other_user.id)
1119 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1120 end
1121
1122 test "getting a list of blocks" do
1123 %{user: user, conn: conn} = oauth_access(["read:blocks"])
1124 other_user = insert(:user)
1125
1126 {:ok, _user_relationship} = User.block(user, other_user)
1127
1128 conn =
1129 conn
1130 |> assign(:user, user)
1131 |> get("/api/v1/blocks")
1132
1133 other_user_id = to_string(other_user.id)
1134 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1135 end
1136 end