969256fa42d44d455ac7252a7cf2e42712cb5c47
[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 end
517
518 test "getting followers, hide_followers", %{user: user, conn: conn} do
519 other_user = insert(:user, hide_followers: true)
520 {:ok, _user} = User.follow(user, other_user)
521
522 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
523
524 assert [] == json_response(conn, 200)
525 end
526
527 test "getting followers, hide_followers, same user requesting" do
528 user = insert(:user)
529 other_user = insert(:user, hide_followers: true)
530 {:ok, _user} = User.follow(user, other_user)
531
532 conn =
533 build_conn()
534 |> assign(:user, other_user)
535 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
536 |> get("/api/v1/accounts/#{other_user.id}/followers")
537
538 refute [] == json_response(conn, 200)
539 end
540
541 test "getting followers, pagination", %{user: user, conn: conn} do
542 follower1 = insert(:user)
543 follower2 = insert(:user)
544 follower3 = insert(:user)
545 {:ok, _} = User.follow(follower1, user)
546 {:ok, _} = User.follow(follower2, user)
547 {:ok, _} = User.follow(follower3, user)
548
549 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
550
551 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
552 assert id3 == follower3.id
553 assert id2 == follower2.id
554
555 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
556
557 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
558 assert id2 == follower2.id
559 assert id1 == follower1.id
560
561 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
562
563 assert [%{"id" => id2}] = json_response(res_conn, 200)
564 assert id2 == follower2.id
565
566 assert [link_header] = get_resp_header(res_conn, "link")
567 assert link_header =~ ~r/min_id=#{follower2.id}/
568 assert link_header =~ ~r/max_id=#{follower2.id}/
569 end
570 end
571
572 describe "following" do
573 setup do: oauth_access(["read:accounts"])
574
575 test "getting following", %{user: user, conn: conn} do
576 other_user = insert(:user)
577 {:ok, user} = User.follow(user, other_user)
578
579 conn = get(conn, "/api/v1/accounts/#{user.id}/following")
580
581 assert [%{"id" => id}] = json_response(conn, 200)
582 assert id == to_string(other_user.id)
583 end
584
585 test "getting following, hide_follows, other user requesting" do
586 user = insert(:user, hide_follows: true)
587 other_user = insert(:user)
588 {:ok, user} = User.follow(user, other_user)
589
590 conn =
591 build_conn()
592 |> assign(:user, other_user)
593 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
594 |> get("/api/v1/accounts/#{user.id}/following")
595
596 assert [] == json_response(conn, 200)
597 end
598
599 test "getting following, hide_follows, same user requesting" do
600 user = insert(:user, hide_follows: true)
601 other_user = insert(:user)
602 {:ok, user} = User.follow(user, other_user)
603
604 conn =
605 build_conn()
606 |> assign(:user, user)
607 |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"]))
608 |> get("/api/v1/accounts/#{user.id}/following")
609
610 refute [] == json_response(conn, 200)
611 end
612
613 test "getting following, pagination", %{user: user, conn: conn} do
614 following1 = insert(:user)
615 following2 = insert(:user)
616 following3 = insert(:user)
617 {:ok, _} = User.follow(user, following1)
618 {:ok, _} = User.follow(user, following2)
619 {:ok, _} = User.follow(user, following3)
620
621 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
622
623 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
624 assert id3 == following3.id
625 assert id2 == following2.id
626
627 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
628
629 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
630 assert id2 == following2.id
631 assert id1 == following1.id
632
633 res_conn =
634 get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
635
636 assert [%{"id" => id2}] = json_response(res_conn, 200)
637 assert id2 == following2.id
638
639 assert [link_header] = get_resp_header(res_conn, "link")
640 assert link_header =~ ~r/min_id=#{following2.id}/
641 assert link_header =~ ~r/max_id=#{following2.id}/
642 end
643 end
644
645 describe "follow/unfollow" do
646 setup do: oauth_access(["follow"])
647
648 test "following / unfollowing a user", %{conn: conn} do
649 other_user = insert(:user)
650
651 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/follow")
652
653 assert %{"id" => _id, "following" => true} = json_response(ret_conn, 200)
654
655 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/unfollow")
656
657 assert %{"id" => _id, "following" => false} = json_response(ret_conn, 200)
658
659 conn = post(conn, "/api/v1/follows", %{"uri" => other_user.nickname})
660
661 assert %{"id" => id} = json_response(conn, 200)
662 assert id == to_string(other_user.id)
663 end
664
665 test "cancelling follow request", %{conn: conn} do
666 %{id: other_user_id} = insert(:user, %{locked: true})
667
668 assert %{"id" => ^other_user_id, "following" => false, "requested" => true} =
669 conn |> post("/api/v1/accounts/#{other_user_id}/follow") |> json_response(:ok)
670
671 assert %{"id" => ^other_user_id, "following" => false, "requested" => false} =
672 conn |> post("/api/v1/accounts/#{other_user_id}/unfollow") |> json_response(:ok)
673 end
674
675 test "following without reblogs" do
676 %{conn: conn} = oauth_access(["follow", "read:statuses"])
677 followed = insert(:user)
678 other_user = insert(:user)
679
680 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false")
681
682 assert %{"showing_reblogs" => false} = json_response(ret_conn, 200)
683
684 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
685 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
686
687 ret_conn = get(conn, "/api/v1/timelines/home")
688
689 assert [] == json_response(ret_conn, 200)
690
691 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=true")
692
693 assert %{"showing_reblogs" => true} = json_response(ret_conn, 200)
694
695 conn = get(conn, "/api/v1/timelines/home")
696
697 expected_activity_id = reblog.id
698 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
699 end
700
701 test "following / unfollowing errors", %{user: user, conn: conn} do
702 # self follow
703 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
704 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
705
706 # self unfollow
707 user = User.get_cached_by_id(user.id)
708 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
709 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
710
711 # self follow via uri
712 user = User.get_cached_by_id(user.id)
713 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
714 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
715
716 # follow non existing user
717 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
718 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
719
720 # follow non existing user via uri
721 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
722 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
723
724 # unfollow non existing user
725 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
726 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
727 end
728 end
729
730 describe "mute/unmute" do
731 setup do: oauth_access(["write:mutes"])
732
733 test "with notifications", %{conn: conn} do
734 other_user = insert(:user)
735
736 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/mute")
737
738 response = json_response(ret_conn, 200)
739
740 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
741
742 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
743
744 response = json_response(conn, 200)
745 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
746 end
747
748 test "without notifications", %{conn: conn} do
749 other_user = insert(:user)
750
751 ret_conn =
752 post(conn, "/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
753
754 response = json_response(ret_conn, 200)
755
756 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
757
758 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
759
760 response = json_response(conn, 200)
761 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
762 end
763 end
764
765 describe "pinned statuses" do
766 setup do
767 user = insert(:user)
768 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
769 %{conn: conn} = oauth_access(["read:statuses"], user: user)
770
771 [conn: conn, user: user, activity: activity]
772 end
773
774 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
775 {:ok, _} = CommonAPI.pin(activity.id, user)
776
777 result =
778 conn
779 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
780 |> json_response(200)
781
782 id_str = to_string(activity.id)
783
784 assert [%{"id" => ^id_str, "pinned" => true}] = result
785 end
786 end
787
788 test "blocking / unblocking a user" do
789 %{conn: conn} = oauth_access(["follow"])
790 other_user = insert(:user)
791
792 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
793
794 assert %{"id" => _id, "blocking" => true} = json_response(ret_conn, 200)
795
796 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
797
798 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
799 end
800
801 describe "create account by app" do
802 setup do
803 valid_params = %{
804 username: "lain",
805 email: "lain@example.org",
806 password: "PlzDontHackLain",
807 agreement: true
808 }
809
810 [valid_params: valid_params]
811 end
812
813 setup do: clear_config([:instance, :account_activation_required])
814
815 test "Account registration via Application", %{conn: conn} do
816 conn =
817 conn
818 |> put_req_header("content-type", "application/json")
819 |> post("/api/v1/apps", %{
820 client_name: "client_name",
821 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
822 scopes: "read, write, follow"
823 })
824
825 %{
826 "client_id" => client_id,
827 "client_secret" => client_secret,
828 "id" => _,
829 "name" => "client_name",
830 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
831 "vapid_key" => _,
832 "website" => nil
833 } = json_response(conn, 200)
834
835 conn =
836 post(conn, "/oauth/token", %{
837 grant_type: "client_credentials",
838 client_id: client_id,
839 client_secret: client_secret
840 })
841
842 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
843 json_response(conn, 200)
844
845 assert token
846 token_from_db = Repo.get_by(Token, token: token)
847 assert token_from_db
848 assert refresh
849 assert scope == "read write follow"
850
851 conn =
852 build_conn()
853 |> put_req_header("content-type", "multipart/form-data")
854 |> put_req_header("authorization", "Bearer " <> token)
855 |> post("/api/v1/accounts", %{
856 username: "lain",
857 email: "lain@example.org",
858 password: "PlzDontHackLain",
859 bio: "Test Bio",
860 agreement: true
861 })
862
863 %{
864 "access_token" => token,
865 "created_at" => _created_at,
866 "scope" => _scope,
867 "token_type" => "Bearer"
868 } = json_response(conn, 200)
869
870 token_from_db = Repo.get_by(Token, token: token)
871 assert token_from_db
872 token_from_db = Repo.preload(token_from_db, :user)
873 assert token_from_db.user
874
875 assert token_from_db.user.confirmation_pending
876 end
877
878 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
879 _user = insert(:user, email: "lain@example.org")
880 app_token = insert(:oauth_token, user: nil)
881
882 res =
883 conn
884 |> put_req_header("authorization", "Bearer " <> app_token.token)
885 |> put_req_header("content-type", "application/json")
886 |> post("/api/v1/accounts", valid_params)
887
888 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
889 end
890
891 test "returns bad_request if missing required params", %{
892 conn: conn,
893 valid_params: valid_params
894 } do
895 app_token = insert(:oauth_token, user: nil)
896
897 conn =
898 conn
899 |> put_req_header("authorization", "Bearer " <> app_token.token)
900 |> put_req_header("content-type", "application/json")
901
902 res = post(conn, "/api/v1/accounts", valid_params)
903 assert json_response(res, 200)
904
905 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
906 |> Stream.zip(Map.delete(valid_params, :email))
907 |> Enum.each(fn {ip, {attr, _}} ->
908 res =
909 conn
910 |> Map.put(:remote_ip, ip)
911 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
912 |> json_response(400)
913
914 assert res == %{"error" => "Missing parameters"}
915 end)
916 end
917
918 setup do: clear_config([:instance, :account_activation_required])
919
920 test "returns bad_request if missing email params when :account_activation_required is enabled",
921 %{conn: conn, valid_params: valid_params} do
922 Pleroma.Config.put([:instance, :account_activation_required], true)
923
924 app_token = insert(:oauth_token, user: nil)
925
926 conn =
927 conn
928 |> put_req_header("authorization", "Bearer " <> app_token.token)
929 |> put_req_header("content-type", "application/json")
930
931 res =
932 conn
933 |> Map.put(:remote_ip, {127, 0, 0, 5})
934 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
935
936 assert json_response(res, 400) == %{"error" => "Missing parameters"}
937
938 res =
939 conn
940 |> Map.put(:remote_ip, {127, 0, 0, 6})
941 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
942
943 assert json_response(res, 400) == %{"error" => "{\"email\":[\"can't be blank\"]}"}
944 end
945
946 test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
947 app_token = insert(:oauth_token, user: nil)
948 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
949
950 res =
951 conn
952 |> put_req_header("content-type", "application/json")
953 |> Map.put(:remote_ip, {127, 0, 0, 7})
954 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
955
956 assert json_response(res, 200)
957 end
958
959 test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
960 app_token = insert(:oauth_token, user: nil)
961 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
962
963 res =
964 conn
965 |> put_req_header("content-type", "application/json")
966 |> Map.put(:remote_ip, {127, 0, 0, 8})
967 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
968
969 assert json_response(res, 200)
970 end
971
972 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
973 res =
974 conn
975 |> put_req_header("authorization", "Bearer " <> "invalid-token")
976 |> put_req_header("content-type", "multipart/form-data")
977 |> post("/api/v1/accounts", valid_params)
978
979 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
980 end
981 end
982
983 describe "create account by app / rate limit" do
984 setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
985
986 test "respects rate limit setting", %{conn: conn} do
987 app_token = insert(:oauth_token, user: nil)
988
989 conn =
990 conn
991 |> put_req_header("authorization", "Bearer " <> app_token.token)
992 |> Map.put(:remote_ip, {15, 15, 15, 15})
993 |> put_req_header("content-type", "multipart/form-data")
994
995 for i <- 1..2 do
996 conn =
997 conn
998 |> post("/api/v1/accounts", %{
999 username: "#{i}lain",
1000 email: "#{i}lain@example.org",
1001 password: "PlzDontHackLain",
1002 agreement: true
1003 })
1004
1005 %{
1006 "access_token" => token,
1007 "created_at" => _created_at,
1008 "scope" => _scope,
1009 "token_type" => "Bearer"
1010 } = json_response(conn, 200)
1011
1012 token_from_db = Repo.get_by(Token, token: token)
1013 assert token_from_db
1014 token_from_db = Repo.preload(token_from_db, :user)
1015 assert token_from_db.user
1016
1017 assert token_from_db.user.confirmation_pending
1018 end
1019
1020 conn =
1021 post(conn, "/api/v1/accounts", %{
1022 username: "6lain",
1023 email: "6lain@example.org",
1024 password: "PlzDontHackLain",
1025 agreement: true
1026 })
1027
1028 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
1029 end
1030 end
1031
1032 describe "GET /api/v1/accounts/:id/lists - account_lists" do
1033 test "returns lists to which the account belongs" do
1034 %{user: user, conn: conn} = oauth_access(["read:lists"])
1035 other_user = insert(:user)
1036 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
1037 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
1038
1039 res =
1040 conn
1041 |> get("/api/v1/accounts/#{other_user.id}/lists")
1042 |> json_response(200)
1043
1044 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
1045 end
1046 end
1047
1048 describe "verify_credentials" do
1049 test "verify_credentials" do
1050 %{user: user, conn: conn} = oauth_access(["read:accounts"])
1051 conn = get(conn, "/api/v1/accounts/verify_credentials")
1052
1053 response = json_response(conn, 200)
1054
1055 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
1056 assert response["pleroma"]["chat_token"]
1057 assert id == to_string(user.id)
1058 end
1059
1060 test "verify_credentials default scope unlisted" do
1061 user = insert(:user, default_scope: "unlisted")
1062 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1063
1064 conn = get(conn, "/api/v1/accounts/verify_credentials")
1065
1066 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
1067 assert id == to_string(user.id)
1068 end
1069
1070 test "locked accounts" do
1071 user = insert(:user, default_scope: "private")
1072 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1073
1074 conn = get(conn, "/api/v1/accounts/verify_credentials")
1075
1076 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1077 assert id == to_string(user.id)
1078 end
1079 end
1080
1081 describe "user relationships" do
1082 setup do: oauth_access(["read:follows"])
1083
1084 test "returns the relationships for the current user", %{user: user, conn: conn} do
1085 %{id: other_user_id} = other_user = insert(:user)
1086 {:ok, _user} = User.follow(user, other_user)
1087
1088 assert [%{"id" => ^other_user_id}] =
1089 conn
1090 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1091 |> json_response(200)
1092
1093 assert [%{"id" => ^other_user_id}] =
1094 conn
1095 |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
1096 |> json_response(200)
1097 end
1098
1099 test "returns an empty list on a bad request", %{conn: conn} do
1100 conn = get(conn, "/api/v1/accounts/relationships", %{})
1101
1102 assert [] = json_response(conn, 200)
1103 end
1104 end
1105
1106 test "getting a list of mutes" do
1107 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1108 other_user = insert(:user)
1109
1110 {:ok, _user_relationships} = User.mute(user, other_user)
1111
1112 conn = get(conn, "/api/v1/mutes")
1113
1114 other_user_id = to_string(other_user.id)
1115 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1116 end
1117
1118 test "getting a list of blocks" do
1119 %{user: user, conn: conn} = oauth_access(["read:blocks"])
1120 other_user = insert(:user)
1121
1122 {:ok, _user_relationship} = User.block(user, other_user)
1123
1124 conn =
1125 conn
1126 |> assign(:user, user)
1127 |> get("/api/v1/blocks")
1128
1129 other_user_id = to_string(other_user.id)
1130 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1131 end
1132 end