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