d885b5e08b0cc07a8bde1bddff1d71d93f1c4d98
[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 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
588 end
589
590 test "getting following, hide_follows, other user requesting" do
591 user = insert(:user, hide_follows: true)
592 other_user = insert(:user)
593 {:ok, user} = User.follow(user, other_user)
594
595 conn =
596 build_conn()
597 |> assign(:user, other_user)
598 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
599 |> get("/api/v1/accounts/#{user.id}/following")
600
601 assert [] == json_response(conn, 200)
602 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
603 end
604
605 test "getting following, hide_follows, same user requesting" do
606 user = insert(:user, hide_follows: true)
607 other_user = insert(:user)
608 {:ok, user} = User.follow(user, other_user)
609
610 conn =
611 build_conn()
612 |> assign(:user, user)
613 |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"]))
614 |> get("/api/v1/accounts/#{user.id}/following")
615
616 refute [] == json_response(conn, 200)
617 end
618
619 test "getting following, pagination", %{user: user, conn: conn} do
620 following1 = insert(:user)
621 following2 = insert(:user)
622 following3 = insert(:user)
623 {:ok, _} = User.follow(user, following1)
624 {:ok, _} = User.follow(user, following2)
625 {:ok, _} = User.follow(user, following3)
626
627 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
628
629 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
630 assert id3 == following3.id
631 assert id2 == following2.id
632 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
633
634 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
635
636 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
637 assert id2 == following2.id
638 assert id1 == following1.id
639 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
640
641 res_conn =
642 get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
643
644 assert [%{"id" => id2}] = json_response(res_conn, 200)
645 assert id2 == following2.id
646
647 assert [link_header] = get_resp_header(res_conn, "link")
648 assert link_header =~ ~r/min_id=#{following2.id}/
649 assert link_header =~ ~r/max_id=#{following2.id}/
650 assert_schema(json_response(res_conn, 200), "AccountsResponse", ApiSpec.spec())
651 end
652 end
653
654 describe "follow/unfollow" do
655 setup do: oauth_access(["follow"])
656
657 test "following / unfollowing a user", %{conn: conn} do
658 other_user = insert(:user)
659
660 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/follow")
661
662 assert %{"id" => _id, "following" => true} = json_response(ret_conn, 200)
663 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
664
665 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/unfollow")
666
667 assert %{"id" => _id, "following" => false} = json_response(ret_conn, 200)
668 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
669
670 conn =
671 conn
672 |> put_req_header("content-type", "application/json")
673 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
674
675 assert %{"id" => id} = json_response(conn, 200)
676 assert id == to_string(other_user.id)
677 assert_schema(json_response(conn, 200), "Account", ApiSpec.spec())
678 end
679
680 test "cancelling follow request", %{conn: conn} do
681 %{id: other_user_id} = insert(:user, %{locked: true})
682
683 resp = conn |> post("/api/v1/accounts/#{other_user_id}/follow") |> json_response(:ok)
684
685 assert %{"id" => ^other_user_id, "following" => false, "requested" => true} = resp
686 assert_schema(resp, "AccountRelationship", ApiSpec.spec())
687
688 resp = conn |> post("/api/v1/accounts/#{other_user_id}/unfollow") |> json_response(:ok)
689
690 assert %{"id" => ^other_user_id, "following" => false, "requested" => false} = resp
691 assert_schema(resp, "AccountRelationship", ApiSpec.spec())
692 end
693
694 test "following without reblogs" do
695 %{conn: conn} = oauth_access(["follow", "read:statuses"])
696 followed = insert(:user)
697 other_user = insert(:user)
698
699 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false")
700
701 assert %{"showing_reblogs" => false} = json_response(ret_conn, 200)
702 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
703
704 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
705 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
706
707 ret_conn = get(conn, "/api/v1/timelines/home")
708
709 assert [] == json_response(ret_conn, 200)
710
711 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=true")
712
713 assert %{"showing_reblogs" => true} = json_response(ret_conn, 200)
714 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
715
716 conn = get(conn, "/api/v1/timelines/home")
717
718 expected_activity_id = reblog.id
719 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
720 end
721
722 test "following / unfollowing errors", %{user: user, conn: conn} do
723 # self follow
724 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
725 assert %{"error" => "Can not follow yourself"} = json_response(conn_res, 400)
726
727 # self unfollow
728 user = User.get_cached_by_id(user.id)
729 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
730 assert %{"error" => "Can not unfollow yourself"} = json_response(conn_res, 400)
731
732 # self follow via uri
733 user = User.get_cached_by_id(user.id)
734
735 conn_res =
736 conn
737 |> put_req_header("content-type", "multipart/form-data")
738 |> post("/api/v1/follows", %{"uri" => user.nickname})
739
740 assert %{"error" => "Can not follow yourself"} = json_response(conn_res, 400)
741
742 # follow non existing user
743 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
744 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
745
746 # follow non existing user via uri
747 conn_res =
748 conn
749 |> put_req_header("content-type", "multipart/form-data")
750 |> post("/api/v1/follows", %{"uri" => "doesntexist"})
751
752 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
753
754 # unfollow non existing user
755 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
756 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
757 end
758 end
759
760 describe "mute/unmute" do
761 setup do: oauth_access(["write:mutes"])
762
763 test "with notifications", %{conn: conn} do
764 other_user = insert(:user)
765
766 ret_conn =
767 conn
768 |> put_req_header("content-type", "application/json")
769 |> post("/api/v1/accounts/#{other_user.id}/mute")
770
771 response = json_response(ret_conn, 200)
772
773 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
774 assert_schema(response, "AccountRelationship", ApiSpec.spec())
775
776 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
777
778 response = json_response(conn, 200)
779 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
780 assert_schema(response, "AccountRelationship", ApiSpec.spec())
781 end
782
783 test "without notifications", %{conn: conn} do
784 other_user = insert(:user)
785
786 ret_conn =
787 conn
788 |> put_req_header("content-type", "multipart/form-data")
789 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
790
791 response = json_response(ret_conn, 200)
792
793 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
794 assert_schema(response, "AccountRelationship", ApiSpec.spec())
795
796 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
797
798 response = json_response(conn, 200)
799 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
800 assert_schema(response, "AccountRelationship", ApiSpec.spec())
801 end
802 end
803
804 describe "pinned statuses" do
805 setup do
806 user = insert(:user)
807 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
808 %{conn: conn} = oauth_access(["read:statuses"], user: user)
809
810 [conn: conn, user: user, activity: activity]
811 end
812
813 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
814 {:ok, _} = CommonAPI.pin(activity.id, user)
815
816 result =
817 conn
818 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
819 |> json_response(200)
820
821 id_str = to_string(activity.id)
822
823 assert [%{"id" => ^id_str, "pinned" => true}] = result
824 end
825 end
826
827 test "blocking / unblocking a user" do
828 %{conn: conn} = oauth_access(["follow"])
829 other_user = insert(:user)
830
831 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
832
833 assert %{"id" => _id, "blocking" => true} = json_response(ret_conn, 200)
834 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
835
836 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
837
838 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
839 assert_schema(json_response(ret_conn, 200), "AccountRelationship", ApiSpec.spec())
840 end
841
842 describe "create account by app" do
843 setup do
844 valid_params = %{
845 username: "lain",
846 email: "lain@example.org",
847 password: "PlzDontHackLain",
848 agreement: true
849 }
850
851 [valid_params: valid_params]
852 end
853
854 setup do: clear_config([:instance, :account_activation_required])
855
856 test "Account registration via Application", %{conn: conn} do
857 conn =
858 conn
859 |> put_req_header("content-type", "application/json")
860 |> post("/api/v1/apps", %{
861 client_name: "client_name",
862 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
863 scopes: "read, write, follow"
864 })
865
866 %{
867 "client_id" => client_id,
868 "client_secret" => client_secret,
869 "id" => _,
870 "name" => "client_name",
871 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
872 "vapid_key" => _,
873 "website" => nil
874 } = json_response(conn, 200)
875
876 conn =
877 post(conn, "/oauth/token", %{
878 grant_type: "client_credentials",
879 client_id: client_id,
880 client_secret: client_secret
881 })
882
883 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
884 json_response(conn, 200)
885
886 assert token
887 token_from_db = Repo.get_by(Token, token: token)
888 assert token_from_db
889 assert refresh
890 assert scope == "read write follow"
891
892 conn =
893 build_conn()
894 |> put_req_header("content-type", "multipart/form-data")
895 |> put_req_header("authorization", "Bearer " <> token)
896 |> post("/api/v1/accounts", %{
897 username: "lain",
898 email: "lain@example.org",
899 password: "PlzDontHackLain",
900 bio: "Test Bio",
901 agreement: true
902 })
903
904 %{
905 "access_token" => token,
906 "created_at" => _created_at,
907 "scope" => _scope,
908 "token_type" => "Bearer"
909 } = json_response(conn, 200)
910
911 token_from_db = Repo.get_by(Token, token: token)
912 assert token_from_db
913 token_from_db = Repo.preload(token_from_db, :user)
914 assert token_from_db.user
915
916 assert token_from_db.user.confirmation_pending
917 end
918
919 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
920 _user = insert(:user, email: "lain@example.org")
921 app_token = insert(:oauth_token, user: nil)
922
923 res =
924 conn
925 |> put_req_header("authorization", "Bearer " <> app_token.token)
926 |> put_req_header("content-type", "application/json")
927 |> post("/api/v1/accounts", valid_params)
928
929 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
930 end
931
932 test "returns bad_request if missing required params", %{
933 conn: conn,
934 valid_params: valid_params
935 } do
936 app_token = insert(:oauth_token, user: nil)
937
938 conn =
939 conn
940 |> put_req_header("authorization", "Bearer " <> app_token.token)
941 |> put_req_header("content-type", "application/json")
942
943 res = post(conn, "/api/v1/accounts", valid_params)
944 assert json_response(res, 200)
945
946 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
947 |> Stream.zip(Map.delete(valid_params, :email))
948 |> Enum.each(fn {ip, {attr, _}} ->
949 res =
950 conn
951 |> Map.put(:remote_ip, ip)
952 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
953 |> json_response(400)
954
955 assert res == %{
956 "error" => "Missing field: #{attr}.",
957 "errors" => [
958 %{
959 "message" => "Missing field: #{attr}",
960 "source" => %{"pointer" => "/#{attr}"},
961 "title" => "Invalid value"
962 }
963 ]
964 }
965 end)
966 end
967
968 setup do: clear_config([:instance, :account_activation_required])
969
970 test "returns bad_request if missing email params when :account_activation_required is enabled",
971 %{conn: conn, valid_params: valid_params} do
972 Pleroma.Config.put([:instance, :account_activation_required], true)
973
974 app_token = insert(:oauth_token, user: nil)
975
976 conn =
977 conn
978 |> put_req_header("authorization", "Bearer " <> app_token.token)
979 |> put_req_header("content-type", "application/json")
980
981 res =
982 conn
983 |> Map.put(:remote_ip, {127, 0, 0, 5})
984 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
985
986 assert json_response(res, 400) == %{"error" => "Missing parameters"}
987
988 res =
989 conn
990 |> Map.put(:remote_ip, {127, 0, 0, 6})
991 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
992
993 assert json_response(res, 400) == %{"error" => "{\"email\":[\"can't be blank\"]}"}
994 end
995
996 test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
997 app_token = insert(:oauth_token, user: nil)
998 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
999
1000 res =
1001 conn
1002 |> put_req_header("content-type", "application/json")
1003 |> Map.put(:remote_ip, {127, 0, 0, 7})
1004 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
1005
1006 assert json_response(res, 200)
1007 end
1008
1009 test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
1010 app_token = insert(:oauth_token, user: nil)
1011 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1012
1013 res =
1014 conn
1015 |> put_req_header("content-type", "application/json")
1016 |> Map.put(:remote_ip, {127, 0, 0, 8})
1017 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
1018
1019 assert json_response(res, 200)
1020 end
1021
1022 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
1023 res =
1024 conn
1025 |> put_req_header("authorization", "Bearer " <> "invalid-token")
1026 |> put_req_header("content-type", "multipart/form-data")
1027 |> post("/api/v1/accounts", valid_params)
1028
1029 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
1030 end
1031
1032 test "registration from trusted app" do
1033 clear_config([Pleroma.Captcha, :enabled], true)
1034 app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
1035
1036 conn =
1037 build_conn()
1038 |> post("/oauth/token", %{
1039 "grant_type" => "client_credentials",
1040 "client_id" => app.client_id,
1041 "client_secret" => app.client_secret
1042 })
1043
1044 assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
1045
1046 response =
1047 build_conn()
1048 |> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
1049 |> put_req_header("content-type", "multipart/form-data")
1050 |> post("/api/v1/accounts", %{
1051 nickname: "nickanme",
1052 agreement: true,
1053 email: "email@example.com",
1054 fullname: "Lain",
1055 username: "Lain",
1056 password: "some_password",
1057 confirm: "some_password"
1058 })
1059 |> json_response(200)
1060
1061 assert %{
1062 "access_token" => access_token,
1063 "created_at" => _,
1064 "scope" => ["read", "write", "follow", "push"],
1065 "token_type" => "Bearer"
1066 } = response
1067
1068 response =
1069 build_conn()
1070 |> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
1071 |> get("/api/v1/accounts/verify_credentials")
1072 |> json_response(200)
1073
1074 assert %{
1075 "acct" => "Lain",
1076 "bot" => false,
1077 "display_name" => "Lain",
1078 "follow_requests_count" => 0,
1079 "followers_count" => 0,
1080 "following_count" => 0,
1081 "locked" => false,
1082 "note" => "",
1083 "source" => %{
1084 "fields" => [],
1085 "note" => "",
1086 "pleroma" => %{
1087 "actor_type" => "Person",
1088 "discoverable" => false,
1089 "no_rich_text" => false,
1090 "show_role" => true
1091 },
1092 "privacy" => "public",
1093 "sensitive" => false
1094 },
1095 "statuses_count" => 0,
1096 "username" => "Lain"
1097 } = response
1098 end
1099 end
1100
1101 describe "create account by app / rate limit" do
1102 setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
1103
1104 test "respects rate limit setting", %{conn: conn} do
1105 app_token = insert(:oauth_token, user: nil)
1106
1107 conn =
1108 conn
1109 |> put_req_header("authorization", "Bearer " <> app_token.token)
1110 |> Map.put(:remote_ip, {15, 15, 15, 15})
1111 |> put_req_header("content-type", "multipart/form-data")
1112
1113 for i <- 1..2 do
1114 conn =
1115 conn
1116 |> post("/api/v1/accounts", %{
1117 username: "#{i}lain",
1118 email: "#{i}lain@example.org",
1119 password: "PlzDontHackLain",
1120 agreement: true
1121 })
1122
1123 %{
1124 "access_token" => token,
1125 "created_at" => _created_at,
1126 "scope" => _scope,
1127 "token_type" => "Bearer"
1128 } = json_response(conn, 200)
1129
1130 token_from_db = Repo.get_by(Token, token: token)
1131 assert token_from_db
1132 token_from_db = Repo.preload(token_from_db, :user)
1133 assert token_from_db.user
1134
1135 assert token_from_db.user.confirmation_pending
1136 end
1137
1138 conn =
1139 post(conn, "/api/v1/accounts", %{
1140 username: "6lain",
1141 email: "6lain@example.org",
1142 password: "PlzDontHackLain",
1143 agreement: true
1144 })
1145
1146 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
1147 end
1148 end
1149
1150 describe "GET /api/v1/accounts/:id/lists - account_lists" do
1151 test "returns lists to which the account belongs" do
1152 %{user: user, conn: conn} = oauth_access(["read:lists"])
1153 other_user = insert(:user)
1154 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
1155 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
1156
1157 res =
1158 conn
1159 |> get("/api/v1/accounts/#{other_user.id}/lists")
1160 |> json_response(200)
1161
1162 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
1163 assert_schema(res, "ListsResponse", ApiSpec.spec())
1164 end
1165 end
1166
1167 describe "verify_credentials" do
1168 test "verify_credentials" do
1169 %{user: user, conn: conn} = oauth_access(["read:accounts"])
1170 conn = get(conn, "/api/v1/accounts/verify_credentials")
1171
1172 response = json_response(conn, 200)
1173
1174 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
1175 assert response["pleroma"]["chat_token"]
1176 assert id == to_string(user.id)
1177 end
1178
1179 test "verify_credentials default scope unlisted" do
1180 user = insert(:user, default_scope: "unlisted")
1181 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1182
1183 conn = get(conn, "/api/v1/accounts/verify_credentials")
1184
1185 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
1186 assert id == to_string(user.id)
1187 end
1188
1189 test "locked accounts" do
1190 user = insert(:user, default_scope: "private")
1191 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1192
1193 conn = get(conn, "/api/v1/accounts/verify_credentials")
1194
1195 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1196 assert id == to_string(user.id)
1197 end
1198 end
1199
1200 describe "user relationships" do
1201 setup do: oauth_access(["read:follows"])
1202
1203 test "returns the relationships for the current user", %{user: user, conn: conn} do
1204 %{id: other_user_id} = other_user = insert(:user)
1205 {:ok, _user} = User.follow(user, other_user)
1206
1207 assert [%{"id" => ^other_user_id}] =
1208 conn
1209 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1210 |> json_response(200)
1211
1212 assert [%{"id" => ^other_user_id}] =
1213 conn
1214 |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
1215 |> json_response(200)
1216 end
1217
1218 test "returns an empty list on a bad request", %{conn: conn} do
1219 conn = get(conn, "/api/v1/accounts/relationships", %{})
1220
1221 assert [] = json_response(conn, 200)
1222 end
1223 end
1224
1225 test "getting a list of mutes" do
1226 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1227 other_user = insert(:user)
1228
1229 {:ok, _user_relationships} = User.mute(user, other_user)
1230
1231 conn = get(conn, "/api/v1/mutes")
1232
1233 other_user_id = to_string(other_user.id)
1234 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1235 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
1236 end
1237
1238 test "getting a list of blocks" do
1239 %{user: user, conn: conn} = oauth_access(["read:blocks"])
1240 other_user = insert(:user)
1241
1242 {:ok, _user_relationship} = User.block(user, other_user)
1243
1244 conn =
1245 conn
1246 |> assign(:user, user)
1247 |> get("/api/v1/blocks")
1248
1249 other_user_id = to_string(other_user.id)
1250 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1251 assert_schema(json_response(conn, 200), "AccountsResponse", ApiSpec.spec())
1252 end
1253 end