Unilateral remove from followers (#232)
[akkoma] / test / pleroma / web / mastodon_api / controllers / account_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 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.Repo
9 alias Pleroma.User
10 alias Pleroma.Web.ActivityPub.ActivityPub
11 alias Pleroma.Web.ActivityPub.InternalFetchActor
12 alias Pleroma.Web.CommonAPI
13 alias Pleroma.Web.OAuth.Token
14 alias Pleroma.Web.Plugs.SetLocalePlug
15
16 import Pleroma.Factory
17
18 describe "account fetching" do
19 test "works by id" do
20 %User{id: user_id} = insert(:user)
21
22 assert %{"id" => ^user_id} =
23 build_conn()
24 |> get("/api/v1/accounts/#{user_id}")
25 |> json_response_and_validate_schema(200)
26
27 assert %{"error" => "Can't find user"} =
28 build_conn()
29 |> get("/api/v1/accounts/-1")
30 |> json_response_and_validate_schema(404)
31 end
32
33 test "relationship field" do
34 %{conn: conn, user: user} = oauth_access(["read"])
35
36 other_user = insert(:user)
37
38 response =
39 conn
40 |> get("/api/v1/accounts/#{other_user.id}")
41 |> json_response_and_validate_schema(200)
42
43 assert response["id"] == other_user.id
44 assert response["pleroma"]["relationship"] == %{}
45
46 assert %{"pleroma" => %{"relationship" => %{"following" => false, "followed_by" => false}}} =
47 conn
48 |> get("/api/v1/accounts/#{other_user.id}?with_relationships=true")
49 |> json_response_and_validate_schema(200)
50
51 {:ok, _, %{id: other_id}} = User.follow(user, other_user)
52
53 assert %{
54 "id" => ^other_id,
55 "pleroma" => %{"relationship" => %{"following" => true, "followed_by" => false}}
56 } =
57 conn
58 |> get("/api/v1/accounts/#{other_id}?with_relationships=true")
59 |> json_response_and_validate_schema(200)
60
61 {:ok, _, _} = User.follow(other_user, user)
62
63 assert %{
64 "id" => ^other_id,
65 "pleroma" => %{"relationship" => %{"following" => true, "followed_by" => true}}
66 } =
67 conn
68 |> get("/api/v1/accounts/#{other_id}?with_relationships=true")
69 |> json_response_and_validate_schema(200)
70 end
71
72 test "works by nickname" do
73 user = insert(:user)
74
75 assert %{"id" => _user_id} =
76 build_conn()
77 |> get("/api/v1/accounts/#{user.nickname}")
78 |> json_response_and_validate_schema(200)
79 end
80
81 test "works by nickname for remote users" do
82 clear_config([:instance, :limit_to_local_content], false)
83
84 user = insert(:user, nickname: "user@example.com", local: false)
85
86 assert %{"id" => _user_id} =
87 build_conn()
88 |> get("/api/v1/accounts/#{user.nickname}")
89 |> json_response_and_validate_schema(200)
90 end
91
92 test "respects limit_to_local_content == :all for remote user nicknames" do
93 clear_config([:instance, :limit_to_local_content], :all)
94
95 user = insert(:user, nickname: "user@example.com", local: false)
96
97 assert build_conn()
98 |> get("/api/v1/accounts/#{user.nickname}")
99 |> json_response_and_validate_schema(404)
100 end
101
102 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
103 clear_config([:instance, :limit_to_local_content], :unauthenticated)
104
105 user = insert(:user, nickname: "user@example.com", local: false)
106 reading_user = insert(:user)
107
108 conn =
109 build_conn()
110 |> get("/api/v1/accounts/#{user.nickname}")
111
112 assert json_response_and_validate_schema(conn, 404)
113
114 conn =
115 build_conn()
116 |> assign(:user, reading_user)
117 |> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
118 |> get("/api/v1/accounts/#{user.nickname}")
119
120 assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
121 assert id == user.id
122 end
123
124 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
125 # Need to set an old-style integer ID to reproduce the problem
126 # (these are no longer assigned to new accounts but were preserved
127 # for existing accounts during the migration to flakeIDs)
128 user_one = insert(:user, %{id: 1212})
129 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
130
131 acc_one =
132 conn
133 |> get("/api/v1/accounts/#{user_one.id}")
134 |> json_response_and_validate_schema(:ok)
135
136 acc_two =
137 conn
138 |> get("/api/v1/accounts/#{user_two.nickname}")
139 |> json_response_and_validate_schema(:ok)
140
141 acc_three =
142 conn
143 |> get("/api/v1/accounts/#{user_two.id}")
144 |> json_response_and_validate_schema(:ok)
145
146 refute acc_one == acc_two
147 assert acc_two == acc_three
148 end
149
150 test "returns 404 when user is invisible", %{conn: conn} do
151 user = insert(:user, %{invisible: true})
152
153 assert %{"error" => "Can't find user"} =
154 conn
155 |> get("/api/v1/accounts/#{user.nickname}")
156 |> json_response_and_validate_schema(404)
157 end
158
159 test "returns 404 for internal.fetch actor", %{conn: conn} do
160 %User{nickname: "internal.fetch"} = InternalFetchActor.get_actor()
161
162 assert %{"error" => "Can't find user"} =
163 conn
164 |> get("/api/v1/accounts/internal.fetch")
165 |> json_response_and_validate_schema(404)
166 end
167
168 test "returns 404 for deactivated user", %{conn: conn} do
169 user = insert(:user, is_active: false)
170
171 assert %{"error" => "Can't find user"} =
172 conn
173 |> get("/api/v1/accounts/#{user.id}")
174 |> json_response_and_validate_schema(:not_found)
175 end
176 end
177
178 defp local_and_remote_users do
179 local = insert(:user)
180 remote = insert(:user, local: false)
181 {:ok, local: local, remote: remote}
182 end
183
184 describe "user fetching with restrict unauthenticated profiles for local and remote" do
185 setup do: local_and_remote_users()
186
187 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
188
189 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
190
191 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
192 assert %{"error" => "This API requires an authenticated user"} ==
193 conn
194 |> get("/api/v1/accounts/#{local.id}")
195 |> json_response_and_validate_schema(:unauthorized)
196
197 assert %{"error" => "This API requires an authenticated user"} ==
198 conn
199 |> get("/api/v1/accounts/#{remote.id}")
200 |> json_response_and_validate_schema(:unauthorized)
201 end
202
203 test "if user is authenticated", %{local: local, remote: remote} do
204 %{conn: conn} = oauth_access(["read"])
205
206 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
207 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
208
209 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
210 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
211 end
212 end
213
214 describe "user fetching with restrict unauthenticated profiles for local" do
215 setup do: local_and_remote_users()
216
217 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
218
219 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
220 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
221
222 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
223 "error" => "This API requires an authenticated user"
224 }
225
226 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
227 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
228 end
229
230 test "if user is authenticated", %{local: local, remote: remote} do
231 %{conn: conn} = oauth_access(["read"])
232
233 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
234 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
235
236 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
237 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
238 end
239 end
240
241 describe "user fetching with restrict unauthenticated profiles for remote" do
242 setup do: local_and_remote_users()
243
244 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
245
246 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
247 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
248 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
249
250 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
251
252 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
253 "error" => "This API requires an authenticated user"
254 }
255 end
256
257 test "if user is authenticated", %{local: local, remote: remote} do
258 %{conn: conn} = oauth_access(["read"])
259
260 res_conn = get(conn, "/api/v1/accounts/#{local.id}")
261 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
262
263 res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
264 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
265 end
266 end
267
268 describe "user timelines" do
269 setup do: oauth_access(["read:statuses"])
270
271 test "works with announces that are just addressed to public", %{conn: conn} do
272 user = insert(:user, ap_id: "https://honktest/u/test", local: false)
273 other_user = insert(:user)
274
275 {:ok, post} = CommonAPI.post(other_user, %{status: "bonkeronk"})
276
277 {:ok, announce, _} =
278 %{
279 "@context" => "https://www.w3.org/ns/activitystreams",
280 "actor" => "https://honktest/u/test",
281 "id" => "https://honktest/u/test/bonk/1793M7B9MQ48847vdx",
282 "object" => post.data["object"],
283 "published" => "2019-06-25T19:33:58Z",
284 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
285 "type" => "Announce"
286 }
287 |> ActivityPub.persist(local: false)
288
289 assert resp =
290 conn
291 |> get("/api/v1/accounts/#{user.id}/statuses")
292 |> json_response_and_validate_schema(200)
293
294 assert [%{"id" => id}] = resp
295 assert id == announce.id
296 end
297
298 test "deactivated user", %{conn: conn} do
299 user = insert(:user, is_active: false)
300
301 assert %{"error" => "Can't find user"} ==
302 conn
303 |> get("/api/v1/accounts/#{user.id}/statuses")
304 |> json_response_and_validate_schema(:not_found)
305 end
306
307 test "returns 404 when user is invisible", %{conn: conn} do
308 user = insert(:user, %{invisible: true})
309
310 assert %{"error" => "Can't find user"} =
311 conn
312 |> get("/api/v1/accounts/#{user.id}")
313 |> json_response_and_validate_schema(404)
314 end
315
316 test "respects blocks", %{user: user_one, conn: conn} do
317 user_two = insert(:user)
318 user_three = insert(:user)
319
320 User.block(user_one, user_two)
321
322 {:ok, activity} = CommonAPI.post(user_two, %{status: "User one sux0rz"})
323 {:ok, repeat} = CommonAPI.repeat(activity.id, user_three)
324
325 assert resp =
326 conn
327 |> get("/api/v1/accounts/#{user_two.id}/statuses")
328 |> json_response_and_validate_schema(200)
329
330 assert [%{"id" => id}] = resp
331 assert id == activity.id
332
333 # Even a blocked user will deliver the full user timeline, there would be
334 # no point in looking at a blocked users timeline otherwise
335 assert resp =
336 conn
337 |> get("/api/v1/accounts/#{user_two.id}/statuses")
338 |> json_response_and_validate_schema(200)
339
340 assert [%{"id" => id}] = resp
341 assert id == activity.id
342
343 # Third user's timeline includes the repeat when viewed by unauthenticated user
344 resp =
345 build_conn()
346 |> get("/api/v1/accounts/#{user_three.id}/statuses")
347 |> json_response_and_validate_schema(200)
348
349 assert [%{"id" => id}] = resp
350 assert id == repeat.id
351
352 # When viewing a third user's timeline, the blocked users' statuses will NOT be shown
353 resp = get(conn, "/api/v1/accounts/#{user_three.id}/statuses")
354
355 assert [] == json_response_and_validate_schema(resp, 200)
356 end
357
358 test "gets users statuses", %{conn: conn} do
359 user_one = insert(:user)
360 user_two = insert(:user)
361 user_three = insert(:user)
362
363 {:ok, _user_three, _user_one} = User.follow(user_three, user_one)
364
365 {:ok, activity} = CommonAPI.post(user_one, %{status: "HI!!!"})
366
367 {:ok, direct_activity} =
368 CommonAPI.post(user_one, %{
369 status: "Hi, @#{user_two.nickname}.",
370 visibility: "direct"
371 })
372
373 {:ok, private_activity} =
374 CommonAPI.post(user_one, %{status: "private", visibility: "private"})
375
376 # TODO!!!
377 resp =
378 conn
379 |> get("/api/v1/accounts/#{user_one.id}/statuses")
380 |> json_response_and_validate_schema(200)
381
382 assert [%{"id" => id}] = resp
383 assert id == to_string(activity.id)
384
385 resp =
386 conn
387 |> assign(:user, user_two)
388 |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
389 |> get("/api/v1/accounts/#{user_one.id}/statuses")
390 |> json_response_and_validate_schema(200)
391
392 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
393 assert id_one == to_string(direct_activity.id)
394 assert id_two == to_string(activity.id)
395
396 resp =
397 conn
398 |> assign(:user, user_three)
399 |> assign(:token, insert(:oauth_token, user: user_three, scopes: ["read:statuses"]))
400 |> get("/api/v1/accounts/#{user_one.id}/statuses")
401 |> json_response_and_validate_schema(200)
402
403 assert [%{"id" => id_one}, %{"id" => id_two}] = resp
404 assert id_one == to_string(private_activity.id)
405 assert id_two == to_string(activity.id)
406 end
407
408 test "unimplemented pinned statuses feature", %{conn: conn} do
409 note = insert(:note_activity)
410 user = User.get_cached_by_ap_id(note.data["actor"])
411
412 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?pinned=true")
413
414 assert json_response_and_validate_schema(conn, 200) == []
415 end
416
417 test "gets local-only statuses for authenticated users", %{user: _user, conn: conn} do
418 user_one = insert(:user)
419
420 {:ok, activity} = CommonAPI.post(user_one, %{status: "HI!!!", visibility: "local"})
421
422 resp =
423 conn
424 |> get("/api/v1/accounts/#{user_one.id}/statuses")
425 |> json_response_and_validate_schema(200)
426
427 assert [%{"id" => id}] = resp
428 assert id == to_string(activity.id)
429 end
430
431 test "gets an users media, excludes reblogs", %{conn: conn} do
432 note = insert(:note_activity)
433 user = User.get_cached_by_ap_id(note.data["actor"])
434 other_user = insert(:user)
435
436 file = %Plug.Upload{
437 content_type: "image/jpeg",
438 path: Path.absname("test/fixtures/image.jpg"),
439 filename: "an_image.jpg"
440 }
441
442 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
443
444 {:ok, %{id: image_post_id}} = CommonAPI.post(user, %{status: "cofe", media_ids: [media_id]})
445
446 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: other_user.ap_id)
447
448 {:ok, %{id: other_image_post_id}} =
449 CommonAPI.post(other_user, %{status: "cofe2", media_ids: [media_id]})
450
451 {:ok, _announce} = CommonAPI.repeat(other_image_post_id, user)
452
453 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")
454
455 assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
456
457 conn = get(build_conn(), "/api/v1/accounts/#{user.id}/statuses?only_media=1")
458
459 assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
460 end
461
462 test "gets a user's statuses without reblogs", %{user: user, conn: conn} do
463 {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "HI!!!"})
464 {:ok, _} = CommonAPI.repeat(post_id, user)
465
466 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=true")
467 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
468
469 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_reblogs=1")
470 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
471 end
472
473 test "filters user's statuses by a hashtag", %{user: user, conn: conn} do
474 {:ok, %{id: post_id}} = CommonAPI.post(user, %{status: "#hashtag"})
475 {:ok, _post} = CommonAPI.post(user, %{status: "hashtag"})
476
477 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?tagged=hashtag")
478 assert [%{"id" => ^post_id}] = json_response_and_validate_schema(conn, 200)
479 end
480
481 test "the user views their own timelines and excludes direct messages", %{
482 user: user,
483 conn: conn
484 } do
485 {:ok, %{id: public_activity_id}} =
486 CommonAPI.post(user, %{status: ".", visibility: "public"})
487
488 {:ok, _direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
489
490 conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?exclude_visibilities[]=direct")
491 assert [%{"id" => ^public_activity_id}] = json_response_and_validate_schema(conn, 200)
492 end
493
494 test "muted reactions", %{user: user, conn: conn} do
495 user2 = insert(:user)
496 User.mute(user, user2)
497 {:ok, activity} = CommonAPI.post(user, %{status: "."})
498 {:ok, _} = CommonAPI.react_with_emoji(activity.id, user2, "🎅")
499
500 result =
501 conn
502 |> get("/api/v1/accounts/#{user.id}/statuses")
503 |> json_response_and_validate_schema(200)
504
505 assert [
506 %{
507 "pleroma" => %{
508 "emoji_reactions" => []
509 }
510 }
511 ] = result
512
513 result =
514 conn
515 |> get("/api/v1/accounts/#{user.id}/statuses?with_muted=true")
516 |> json_response_and_validate_schema(200)
517
518 assert [
519 %{
520 "pleroma" => %{
521 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
522 }
523 }
524 ] = result
525 end
526
527 test "paginates a user's statuses", %{user: user, conn: conn} do
528 {:ok, post_1} = CommonAPI.post(user, %{status: "first post"})
529 {:ok, post_2} = CommonAPI.post(user, %{status: "second post"})
530
531 response_1 = get(conn, "/api/v1/accounts/#{user.id}/statuses?limit=1")
532 assert [res] = json_response_and_validate_schema(response_1, 200)
533 assert res["id"] == post_2.id
534
535 response_2 = get(conn, "/api/v1/accounts/#{user.id}/statuses?limit=1&max_id=#{res["id"]}")
536 assert [res] = json_response_and_validate_schema(response_2, 200)
537 assert res["id"] == post_1.id
538
539 refute response_1 == response_2
540 end
541 end
542
543 defp local_and_remote_activities(%{local: local, remote: remote}) do
544 insert(:note_activity, user: local)
545 insert(:note_activity, user: remote, local: false)
546
547 :ok
548 end
549
550 describe "statuses with restrict unauthenticated profiles for local and remote" do
551 setup do: local_and_remote_users()
552 setup :local_and_remote_activities
553
554 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
555
556 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
557
558 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
559 assert %{"error" => "This API requires an authenticated user"} ==
560 conn
561 |> get("/api/v1/accounts/#{local.id}/statuses")
562 |> json_response_and_validate_schema(:unauthorized)
563
564 assert %{"error" => "This API requires an authenticated user"} ==
565 conn
566 |> get("/api/v1/accounts/#{remote.id}/statuses")
567 |> json_response_and_validate_schema(:unauthorized)
568 end
569
570 test "if user is authenticated", %{local: local, remote: remote} do
571 %{conn: conn} = oauth_access(["read"])
572
573 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
574 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
575
576 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
577 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
578 end
579 end
580
581 describe "statuses with restrict unauthenticated profiles for local" do
582 setup do: local_and_remote_users()
583 setup :local_and_remote_activities
584
585 setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
586
587 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
588 assert %{"error" => "This API requires an authenticated user"} ==
589 conn
590 |> get("/api/v1/accounts/#{local.id}/statuses")
591 |> json_response_and_validate_schema(:unauthorized)
592
593 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
594 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
595 end
596
597 test "if user is authenticated", %{local: local, remote: remote} do
598 %{conn: conn} = oauth_access(["read"])
599
600 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
601 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
602
603 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
604 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
605 end
606 end
607
608 describe "statuses with restrict unauthenticated profiles for remote" do
609 setup do: local_and_remote_users()
610 setup :local_and_remote_activities
611
612 setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
613
614 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
615 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
616 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
617
618 assert %{"error" => "This API requires an authenticated user"} ==
619 conn
620 |> get("/api/v1/accounts/#{remote.id}/statuses")
621 |> json_response_and_validate_schema(:unauthorized)
622 end
623
624 test "if user is authenticated", %{local: local, remote: remote} do
625 %{conn: conn} = oauth_access(["read"])
626
627 res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
628 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
629
630 res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
631 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
632 end
633 end
634
635 describe "followers" do
636 setup do: oauth_access(["read:accounts"])
637
638 test "getting followers", %{user: user, conn: conn} do
639 other_user = insert(:user)
640 {:ok, %{id: user_id}, other_user} = User.follow(user, other_user)
641
642 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
643
644 assert [%{"id" => ^user_id}] = json_response_and_validate_schema(conn, 200)
645 end
646
647 test "following with relationship", %{conn: conn, user: user} do
648 other_user = insert(:user)
649 {:ok, %{id: id}, _} = User.follow(other_user, user)
650
651 assert [
652 %{
653 "id" => ^id,
654 "pleroma" => %{
655 "relationship" => %{
656 "id" => ^id,
657 "following" => false,
658 "followed_by" => true
659 }
660 }
661 }
662 ] =
663 conn
664 |> get("/api/v1/accounts/#{user.id}/followers?with_relationships=true")
665 |> json_response_and_validate_schema(200)
666
667 {:ok, _, _} = User.follow(user, other_user)
668
669 assert [
670 %{
671 "id" => ^id,
672 "pleroma" => %{
673 "relationship" => %{
674 "id" => ^id,
675 "following" => true,
676 "followed_by" => true
677 }
678 }
679 }
680 ] =
681 conn
682 |> get("/api/v1/accounts/#{user.id}/followers?with_relationships=true")
683 |> json_response_and_validate_schema(200)
684 end
685
686 test "getting followers, hide_followers", %{user: user, conn: conn} do
687 other_user = insert(:user, hide_followers: true)
688 {:ok, _user, _other_user} = User.follow(user, other_user)
689
690 conn = get(conn, "/api/v1/accounts/#{other_user.id}/followers")
691
692 assert [] == json_response_and_validate_schema(conn, 200)
693 end
694
695 test "getting followers, hide_followers, same user requesting" do
696 user = insert(:user)
697 other_user = insert(:user, hide_followers: true)
698 {:ok, _user, _other_user} = User.follow(user, other_user)
699
700 conn =
701 build_conn()
702 |> assign(:user, other_user)
703 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
704 |> get("/api/v1/accounts/#{other_user.id}/followers")
705
706 refute [] == json_response_and_validate_schema(conn, 200)
707 end
708
709 test "getting followers, pagination", %{user: user, conn: conn} do
710 {:ok, %User{id: follower1_id}, _user} = :user |> insert() |> User.follow(user)
711 {:ok, %User{id: follower2_id}, _user} = :user |> insert() |> User.follow(user)
712 {:ok, %User{id: follower3_id}, _user} = :user |> insert() |> User.follow(user)
713
714 assert [%{"id" => ^follower3_id}, %{"id" => ^follower2_id}] =
715 conn
716 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1_id}")
717 |> json_response_and_validate_schema(200)
718
719 assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] =
720 conn
721 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3_id}")
722 |> json_response_and_validate_schema(200)
723
724 assert [%{"id" => ^follower2_id}, %{"id" => ^follower1_id}] =
725 conn
726 |> get(
727 "/api/v1/accounts/#{user.id}/followers?id=#{user.id}&limit=20&max_id=#{follower3_id}"
728 )
729 |> json_response_and_validate_schema(200)
730
731 res_conn = get(conn, "/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3_id}")
732
733 assert [%{"id" => ^follower2_id}] = json_response_and_validate_schema(res_conn, 200)
734
735 assert [link_header] = get_resp_header(res_conn, "link")
736 assert link_header =~ ~r/min_id=#{follower2_id}/
737 assert link_header =~ ~r/max_id=#{follower2_id}/
738 end
739 end
740
741 describe "following" do
742 setup do: oauth_access(["read:accounts"])
743
744 test "getting following", %{user: user, conn: conn} do
745 other_user = insert(:user)
746 {:ok, user, other_user} = User.follow(user, other_user)
747
748 conn = get(conn, "/api/v1/accounts/#{user.id}/following")
749
750 assert [%{"id" => id}] = json_response_and_validate_schema(conn, 200)
751 assert id == to_string(other_user.id)
752 end
753
754 test "following with relationship", %{conn: conn, user: user} do
755 other_user = insert(:user)
756 {:ok, user, other_user} = User.follow(user, other_user)
757
758 conn = get(conn, "/api/v1/accounts/#{user.id}/following?with_relationships=true")
759
760 id = other_user.id
761
762 assert [
763 %{
764 "id" => ^id,
765 "pleroma" => %{
766 "relationship" => %{"id" => ^id, "following" => true, "followed_by" => false}
767 }
768 }
769 ] = json_response_and_validate_schema(conn, 200)
770 end
771
772 test "getting following, hide_follows, other user requesting" do
773 user = insert(:user, hide_follows: true)
774 other_user = insert(:user)
775 {:ok, user, other_user} = User.follow(user, other_user)
776
777 conn =
778 build_conn()
779 |> assign(:user, other_user)
780 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
781 |> get("/api/v1/accounts/#{user.id}/following")
782
783 assert [] == json_response_and_validate_schema(conn, 200)
784 end
785
786 test "getting following, hide_follows, same user requesting" do
787 user = insert(:user, hide_follows: true)
788 other_user = insert(:user)
789 {:ok, user, _other_user} = User.follow(user, other_user)
790
791 conn =
792 build_conn()
793 |> assign(:user, user)
794 |> assign(:token, insert(:oauth_token, user: user, scopes: ["read:accounts"]))
795 |> get("/api/v1/accounts/#{user.id}/following")
796
797 refute [] == json_response_and_validate_schema(conn, 200)
798 end
799
800 test "getting following, pagination", %{user: user, conn: conn} do
801 following1 = insert(:user)
802 following2 = insert(:user)
803 following3 = insert(:user)
804 {:ok, _, _} = User.follow(user, following1)
805 {:ok, _, _} = User.follow(user, following2)
806 {:ok, _, _} = User.follow(user, following3)
807
808 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
809
810 assert [%{"id" => id3}, %{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
811 assert id3 == following3.id
812 assert id2 == following2.id
813
814 res_conn = get(conn, "/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
815
816 assert [%{"id" => id2}, %{"id" => id1}] = json_response_and_validate_schema(res_conn, 200)
817 assert id2 == following2.id
818 assert id1 == following1.id
819
820 res_conn =
821 get(
822 conn,
823 "/api/v1/accounts/#{user.id}/following?id=#{user.id}&limit=20&max_id=#{following3.id}"
824 )
825
826 assert [%{"id" => id2}, %{"id" => id1}] = json_response_and_validate_schema(res_conn, 200)
827 assert id2 == following2.id
828 assert id1 == following1.id
829
830 res_conn =
831 get(conn, "/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
832
833 assert [%{"id" => id2}] = json_response_and_validate_schema(res_conn, 200)
834 assert id2 == following2.id
835
836 assert [link_header] = get_resp_header(res_conn, "link")
837 assert link_header =~ ~r/min_id=#{following2.id}/
838 assert link_header =~ ~r/max_id=#{following2.id}/
839 end
840 end
841
842 describe "follow/unfollow" do
843 setup do: oauth_access(["follow"])
844
845 test "following / unfollowing a user", %{conn: conn} do
846 %{id: other_user_id, nickname: other_user_nickname} = insert(:user)
847
848 assert %{"id" => _id, "following" => true} =
849 conn
850 |> post("/api/v1/accounts/#{other_user_id}/follow")
851 |> json_response_and_validate_schema(200)
852
853 assert %{"id" => _id, "following" => false} =
854 conn
855 |> post("/api/v1/accounts/#{other_user_id}/unfollow")
856 |> json_response_and_validate_schema(200)
857
858 assert %{"id" => ^other_user_id} =
859 conn
860 |> put_req_header("content-type", "application/json")
861 |> post("/api/v1/follows", %{"uri" => other_user_nickname})
862 |> json_response_and_validate_schema(200)
863 end
864
865 test "cancelling follow request", %{conn: conn} do
866 %{id: other_user_id} = insert(:user, %{is_locked: true})
867
868 assert %{"id" => ^other_user_id, "following" => false, "requested" => true} =
869 conn
870 |> post("/api/v1/accounts/#{other_user_id}/follow")
871 |> json_response_and_validate_schema(:ok)
872
873 assert %{"id" => ^other_user_id, "following" => false, "requested" => false} =
874 conn
875 |> post("/api/v1/accounts/#{other_user_id}/unfollow")
876 |> json_response_and_validate_schema(:ok)
877 end
878
879 test "following without reblogs" do
880 %{conn: conn} = oauth_access(["follow", "read:statuses"])
881 followed = insert(:user)
882 other_user = insert(:user)
883
884 ret_conn =
885 conn
886 |> put_req_header("content-type", "application/json")
887 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false})
888
889 assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200)
890
891 {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
892 {:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
893
894 assert [] ==
895 conn
896 |> get("/api/v1/timelines/home")
897 |> json_response_and_validate_schema(200)
898
899 assert %{"showing_reblogs" => true} =
900 conn
901 |> put_req_header("content-type", "application/json")
902 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: true})
903 |> json_response_and_validate_schema(200)
904
905 assert [%{"id" => ^reblog_id}] =
906 conn
907 |> get("/api/v1/timelines/home")
908 |> json_response_and_validate_schema(200)
909 end
910
911 test "following with reblogs" do
912 %{conn: conn} = oauth_access(["follow", "read:statuses"])
913 followed = insert(:user)
914 other_user = insert(:user)
915
916 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow")
917
918 assert %{"showing_reblogs" => true} = json_response_and_validate_schema(ret_conn, 200)
919
920 {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
921 {:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
922
923 assert [%{"id" => ^reblog_id}] =
924 conn
925 |> get("/api/v1/timelines/home")
926 |> json_response_and_validate_schema(200)
927
928 assert %{"showing_reblogs" => false} =
929 conn
930 |> put_req_header("content-type", "application/json")
931 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false})
932 |> json_response_and_validate_schema(200)
933
934 assert [] ==
935 conn
936 |> get("/api/v1/timelines/home")
937 |> json_response_and_validate_schema(200)
938 end
939
940 test "following with subscription and unsubscribing" do
941 %{conn: conn} = oauth_access(["follow"])
942 followed = insert(:user)
943
944 ret_conn =
945 conn
946 |> put_req_header("content-type", "application/json")
947 |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: true})
948
949 assert %{"id" => _id, "subscribing" => true} =
950 json_response_and_validate_schema(ret_conn, 200)
951
952 ret_conn =
953 conn
954 |> put_req_header("content-type", "application/json")
955 |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: false})
956
957 assert %{"id" => _id, "subscribing" => false} =
958 json_response_and_validate_schema(ret_conn, 200)
959 end
960
961 test "following / unfollowing errors", %{user: user, conn: conn} do
962 # self follow
963 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
964
965 assert %{"error" => "Can not follow yourself"} =
966 json_response_and_validate_schema(conn_res, 400)
967
968 # self unfollow
969 user = User.get_cached_by_id(user.id)
970 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
971
972 assert %{"error" => "Can not unfollow yourself"} =
973 json_response_and_validate_schema(conn_res, 400)
974
975 # self follow via uri
976 user = User.get_cached_by_id(user.id)
977
978 assert %{"error" => "Can not follow yourself"} =
979 conn
980 |> put_req_header("content-type", "multipart/form-data")
981 |> post("/api/v1/follows", %{"uri" => user.nickname})
982 |> json_response_and_validate_schema(400)
983
984 # follow non existing user
985 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
986 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
987
988 # follow non existing user via uri
989 conn_res =
990 conn
991 |> put_req_header("content-type", "multipart/form-data")
992 |> post("/api/v1/follows", %{"uri" => "doesntexist"})
993
994 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
995
996 # unfollow non existing user
997 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
998 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
999 end
1000 end
1001
1002 describe "mute/unmute" do
1003 setup do: oauth_access(["write:mutes"])
1004
1005 test "with notifications", %{conn: conn} do
1006 other_user = insert(:user)
1007
1008 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} =
1009 conn
1010 |> post("/api/v1/accounts/#{other_user.id}/mute")
1011 |> json_response_and_validate_schema(200)
1012
1013 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
1014
1015 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
1016 json_response_and_validate_schema(conn, 200)
1017 end
1018
1019 test "without notifications", %{conn: conn} do
1020 other_user = insert(:user)
1021
1022 ret_conn =
1023 conn
1024 |> put_req_header("content-type", "multipart/form-data")
1025 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
1026
1027 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} =
1028 json_response_and_validate_schema(ret_conn, 200)
1029
1030 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
1031
1032 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
1033 json_response_and_validate_schema(conn, 200)
1034 end
1035 end
1036
1037 describe "pinned statuses" do
1038 setup do
1039 user = insert(:user)
1040 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
1041 %{conn: conn} = oauth_access(["read:statuses"], user: user)
1042
1043 [conn: conn, user: user, activity: activity]
1044 end
1045
1046 test "returns pinned statuses", %{conn: conn, user: user, activity: %{id: activity_id}} do
1047 {:ok, _} = CommonAPI.pin(activity_id, user)
1048
1049 assert [%{"id" => ^activity_id, "pinned" => true}] =
1050 conn
1051 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1052 |> json_response_and_validate_schema(200)
1053 end
1054 end
1055
1056 test "blocking / unblocking a user" do
1057 %{conn: conn} = oauth_access(["follow"])
1058 other_user = insert(:user)
1059
1060 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
1061
1062 assert %{"id" => _id, "blocking" => true} = json_response_and_validate_schema(ret_conn, 200)
1063
1064 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
1065
1066 assert %{"id" => _id, "blocking" => false} = json_response_and_validate_schema(conn, 200)
1067 end
1068
1069 describe "create account by app" do
1070 setup do
1071 valid_params = %{
1072 username: "lain",
1073 email: "lain@example.org",
1074 password: "PlzDontHackLain",
1075 agreement: true
1076 }
1077
1078 [valid_params: valid_params]
1079 end
1080
1081 test "registers and logs in without :account_activation_required / :account_approval_required",
1082 %{conn: conn} do
1083 clear_config([:instance, :account_activation_required], false)
1084 clear_config([:instance, :account_approval_required], false)
1085
1086 conn =
1087 conn
1088 |> put_req_header("content-type", "application/json")
1089 |> post("/api/v1/apps", %{
1090 client_name: "client_name",
1091 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1092 scopes: "read, write, follow"
1093 })
1094
1095 assert %{
1096 "client_id" => client_id,
1097 "client_secret" => client_secret,
1098 "id" => _,
1099 "name" => "client_name",
1100 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1101 "vapid_key" => _,
1102 "website" => nil
1103 } = json_response_and_validate_schema(conn, 200)
1104
1105 conn =
1106 post(conn, "/oauth/token", %{
1107 grant_type: "client_credentials",
1108 client_id: client_id,
1109 client_secret: client_secret
1110 })
1111
1112 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1113 json_response(conn, 200)
1114
1115 assert token
1116 token_from_db = Repo.get_by(Token, token: token)
1117 assert token_from_db
1118 assert refresh
1119 assert scope == "read write follow"
1120
1121 clear_config([User, :email_blacklist], ["example.org"])
1122
1123 params = %{
1124 username: "lain",
1125 email: "lain@example.org",
1126 password: "PlzDontHackLain",
1127 bio: "Test Bio",
1128 agreement: true
1129 }
1130
1131 conn =
1132 build_conn()
1133 |> put_req_header("content-type", "multipart/form-data")
1134 |> put_req_header("authorization", "Bearer " <> token)
1135 |> post("/api/v1/accounts", params)
1136
1137 assert %{"error" => "{\"email\":[\"Invalid email\"]}"} =
1138 json_response_and_validate_schema(conn, 400)
1139
1140 clear_config([User, :email_blacklist], [])
1141
1142 conn =
1143 build_conn()
1144 |> put_req_header("content-type", "multipart/form-data")
1145 |> put_req_header("authorization", "Bearer " <> token)
1146 |> post("/api/v1/accounts", params)
1147
1148 %{
1149 "access_token" => token,
1150 "created_at" => _created_at,
1151 "scope" => ^scope,
1152 "token_type" => "Bearer"
1153 } = json_response_and_validate_schema(conn, 200)
1154
1155 token_from_db = Repo.get_by(Token, token: token)
1156 assert token_from_db
1157 user = Repo.preload(token_from_db, :user).user
1158
1159 assert user
1160 assert user.is_confirmed
1161 assert user.is_approved
1162 end
1163
1164 test "registers but does not log in with :account_activation_required", %{conn: conn} do
1165 clear_config([:instance, :account_activation_required], true)
1166 clear_config([:instance, :account_approval_required], false)
1167
1168 conn =
1169 conn
1170 |> put_req_header("content-type", "application/json")
1171 |> post("/api/v1/apps", %{
1172 client_name: "client_name",
1173 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1174 scopes: "read, write, follow"
1175 })
1176
1177 assert %{
1178 "client_id" => client_id,
1179 "client_secret" => client_secret,
1180 "id" => _,
1181 "name" => "client_name",
1182 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1183 "vapid_key" => _,
1184 "website" => nil
1185 } = json_response_and_validate_schema(conn, 200)
1186
1187 conn =
1188 post(conn, "/oauth/token", %{
1189 grant_type: "client_credentials",
1190 client_id: client_id,
1191 client_secret: client_secret
1192 })
1193
1194 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1195 json_response(conn, 200)
1196
1197 assert token
1198 token_from_db = Repo.get_by(Token, token: token)
1199 assert token_from_db
1200 assert refresh
1201 assert scope == "read write follow"
1202
1203 conn =
1204 build_conn()
1205 |> put_req_header("content-type", "multipart/form-data")
1206 |> put_req_header("authorization", "Bearer " <> token)
1207 |> post("/api/v1/accounts", %{
1208 username: "lain",
1209 email: "lain@example.org",
1210 password: "PlzDontHackLain",
1211 bio: "Test Bio",
1212 agreement: true
1213 })
1214
1215 response = json_response_and_validate_schema(conn, 200)
1216 assert %{"identifier" => "missing_confirmed_email"} = response
1217 refute response["access_token"]
1218 refute response["token_type"]
1219
1220 user = Repo.get_by(User, email: "lain@example.org")
1221 refute user.is_confirmed
1222 end
1223
1224 test "registers but does not log in with :account_approval_required", %{conn: conn} do
1225 clear_config([:instance, :account_approval_required], true)
1226 clear_config([:instance, :account_activation_required], false)
1227
1228 conn =
1229 conn
1230 |> put_req_header("content-type", "application/json")
1231 |> post("/api/v1/apps", %{
1232 client_name: "client_name",
1233 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1234 scopes: "read, write, follow"
1235 })
1236
1237 assert %{
1238 "client_id" => client_id,
1239 "client_secret" => client_secret,
1240 "id" => _,
1241 "name" => "client_name",
1242 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1243 "vapid_key" => _,
1244 "website" => nil
1245 } = json_response_and_validate_schema(conn, 200)
1246
1247 conn =
1248 post(conn, "/oauth/token", %{
1249 grant_type: "client_credentials",
1250 client_id: client_id,
1251 client_secret: client_secret
1252 })
1253
1254 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1255 json_response(conn, 200)
1256
1257 assert token
1258 token_from_db = Repo.get_by(Token, token: token)
1259 assert token_from_db
1260 assert refresh
1261 assert scope == "read write follow"
1262
1263 conn =
1264 build_conn()
1265 |> put_req_header("content-type", "multipart/form-data")
1266 |> put_req_header("authorization", "Bearer " <> token)
1267 |> post("/api/v1/accounts", %{
1268 username: "lain",
1269 email: "lain@example.org",
1270 password: "PlzDontHackLain",
1271 bio: "Test Bio",
1272 agreement: true,
1273 reason: "I'm a cool dude, bro"
1274 })
1275
1276 response = json_response_and_validate_schema(conn, 200)
1277 assert %{"identifier" => "awaiting_approval"} = response
1278 refute response["access_token"]
1279 refute response["token_type"]
1280
1281 user = Repo.get_by(User, email: "lain@example.org")
1282
1283 refute user.is_approved
1284 assert user.registration_reason == "I'm a cool dude, bro"
1285 end
1286
1287 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
1288 _user = insert(:user, email: "lain@example.org")
1289 app_token = insert(:oauth_token, user: nil)
1290
1291 res =
1292 conn
1293 |> put_req_header("authorization", "Bearer " <> app_token.token)
1294 |> put_req_header("content-type", "application/json")
1295 |> post("/api/v1/accounts", valid_params)
1296
1297 assert json_response_and_validate_schema(res, 400) == %{
1298 "error" => "{\"email\":[\"has already been taken\"]}"
1299 }
1300 end
1301
1302 test "returns bad_request if missing required params", %{
1303 conn: conn,
1304 valid_params: valid_params
1305 } do
1306 app_token = insert(:oauth_token, user: nil)
1307
1308 conn =
1309 conn
1310 |> put_req_header("authorization", "Bearer " <> app_token.token)
1311 |> put_req_header("content-type", "application/json")
1312
1313 res = post(conn, "/api/v1/accounts", valid_params)
1314 assert json_response_and_validate_schema(res, 200)
1315
1316 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
1317 |> Stream.zip(Map.delete(valid_params, :email))
1318 |> Enum.each(fn {ip, {attr, _}} ->
1319 res =
1320 conn
1321 |> Map.put(:remote_ip, ip)
1322 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
1323 |> json_response_and_validate_schema(400)
1324
1325 assert res == %{
1326 "error" => "Missing field: #{attr}.",
1327 "errors" => [
1328 %{
1329 "message" => "Missing field: #{attr}",
1330 "source" => %{"pointer" => "/#{attr}"},
1331 "title" => "Invalid value"
1332 }
1333 ]
1334 }
1335 end)
1336 end
1337
1338 test "returns bad_request if missing email params when :account_activation_required is enabled",
1339 %{conn: conn, valid_params: valid_params} do
1340 clear_config([:instance, :account_activation_required], true)
1341
1342 app_token = insert(:oauth_token, user: nil)
1343
1344 conn =
1345 conn
1346 |> put_req_header("authorization", "Bearer " <> app_token.token)
1347 |> put_req_header("content-type", "application/json")
1348
1349 res =
1350 conn
1351 |> Map.put(:remote_ip, {127, 0, 0, 5})
1352 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
1353
1354 assert json_response_and_validate_schema(res, 400) ==
1355 %{"error" => "Missing parameter: email"}
1356
1357 res =
1358 conn
1359 |> Map.put(:remote_ip, {127, 0, 0, 6})
1360 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
1361
1362 assert json_response_and_validate_schema(res, 400) == %{
1363 "error" => "{\"email\":[\"can't be blank\"]}"
1364 }
1365 end
1366
1367 test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
1368 app_token = insert(:oauth_token, user: nil)
1369 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1370
1371 res =
1372 conn
1373 |> put_req_header("content-type", "application/json")
1374 |> Map.put(:remote_ip, {127, 0, 0, 7})
1375 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
1376
1377 assert json_response_and_validate_schema(res, 200)
1378 end
1379
1380 test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
1381 app_token = insert(:oauth_token, user: nil)
1382 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1383
1384 res =
1385 conn
1386 |> put_req_header("content-type", "application/json")
1387 |> Map.put(:remote_ip, {127, 0, 0, 8})
1388 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
1389
1390 assert json_response_and_validate_schema(res, 200)
1391 end
1392
1393 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
1394 res =
1395 conn
1396 |> put_req_header("authorization", "Bearer " <> "invalid-token")
1397 |> put_req_header("content-type", "multipart/form-data")
1398 |> post("/api/v1/accounts", valid_params)
1399
1400 assert json_response_and_validate_schema(res, 403) == %{"error" => "Invalid credentials"}
1401 end
1402
1403 test "registration from trusted app" do
1404 clear_config([Pleroma.Captcha, :enabled], true)
1405 app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
1406
1407 conn =
1408 build_conn()
1409 |> post("/oauth/token", %{
1410 "grant_type" => "client_credentials",
1411 "client_id" => app.client_id,
1412 "client_secret" => app.client_secret
1413 })
1414
1415 assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
1416
1417 response =
1418 build_conn()
1419 |> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
1420 |> put_req_header("content-type", "multipart/form-data")
1421 |> post("/api/v1/accounts", %{
1422 nickname: "nickanme",
1423 agreement: true,
1424 email: "email@example.com",
1425 fullname: "Lain",
1426 username: "Lain",
1427 password: "some_password",
1428 confirm: "some_password"
1429 })
1430 |> json_response_and_validate_schema(200)
1431
1432 assert %{
1433 "access_token" => access_token,
1434 "created_at" => _,
1435 "scope" => "read write follow push",
1436 "token_type" => "Bearer"
1437 } = response
1438
1439 response =
1440 build_conn()
1441 |> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
1442 |> get("/api/v1/accounts/verify_credentials")
1443 |> json_response_and_validate_schema(200)
1444
1445 assert %{
1446 "acct" => "Lain",
1447 "bot" => false,
1448 "display_name" => "Lain",
1449 "follow_requests_count" => 0,
1450 "followers_count" => 0,
1451 "following_count" => 0,
1452 "locked" => false,
1453 "note" => "",
1454 "source" => %{
1455 "fields" => [],
1456 "note" => "",
1457 "pleroma" => %{
1458 "actor_type" => "Person",
1459 "discoverable" => false,
1460 "no_rich_text" => false,
1461 "show_role" => true
1462 },
1463 "privacy" => "public",
1464 "sensitive" => false
1465 },
1466 "statuses_count" => 0,
1467 "username" => "Lain"
1468 } = response
1469 end
1470 end
1471
1472 describe "create account by app / rate limit" do
1473 setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
1474
1475 test "respects rate limit setting", %{conn: conn} do
1476 app_token = insert(:oauth_token, user: nil)
1477
1478 conn =
1479 conn
1480 |> put_req_header("authorization", "Bearer " <> app_token.token)
1481 |> Map.put(:remote_ip, {15, 15, 15, 15})
1482 |> put_req_header("content-type", "multipart/form-data")
1483
1484 for i <- 1..2 do
1485 conn =
1486 conn
1487 |> post("/api/v1/accounts", %{
1488 username: "#{i}lain",
1489 email: "#{i}lain@example.org",
1490 password: "PlzDontHackLain",
1491 agreement: true
1492 })
1493
1494 %{
1495 "access_token" => token,
1496 "created_at" => _created_at,
1497 "scope" => _scope,
1498 "token_type" => "Bearer"
1499 } = json_response_and_validate_schema(conn, 200)
1500
1501 token_from_db = Repo.get_by(Token, token: token)
1502 assert token_from_db
1503 token_from_db = Repo.preload(token_from_db, :user)
1504 assert token_from_db.user
1505 end
1506
1507 conn =
1508 post(conn, "/api/v1/accounts", %{
1509 username: "6lain",
1510 email: "6lain@example.org",
1511 password: "PlzDontHackLain",
1512 agreement: true
1513 })
1514
1515 assert json_response_and_validate_schema(conn, :too_many_requests) == %{
1516 "error" => "Throttled"
1517 }
1518 end
1519 end
1520
1521 describe "create account with enabled captcha" do
1522 setup %{conn: conn} do
1523 app_token = insert(:oauth_token, user: nil)
1524
1525 conn =
1526 conn
1527 |> put_req_header("authorization", "Bearer " <> app_token.token)
1528 |> put_req_header("content-type", "multipart/form-data")
1529
1530 [conn: conn]
1531 end
1532
1533 setup do: clear_config([Pleroma.Captcha, :enabled], true)
1534
1535 test "creates an account and returns 200 if captcha is valid", %{conn: conn} do
1536 %{token: token, answer_data: answer_data} = Pleroma.Captcha.new()
1537
1538 params = %{
1539 username: "lain",
1540 email: "lain@example.org",
1541 password: "PlzDontHackLain",
1542 agreement: true,
1543 captcha_solution: Pleroma.Captcha.Mock.solution(),
1544 captcha_token: token,
1545 captcha_answer_data: answer_data
1546 }
1547
1548 assert %{
1549 "access_token" => access_token,
1550 "created_at" => _,
1551 "scope" => "read",
1552 "token_type" => "Bearer"
1553 } =
1554 conn
1555 |> post("/api/v1/accounts", params)
1556 |> json_response_and_validate_schema(:ok)
1557
1558 assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user)
1559 end
1560
1561 test "returns 400 if any captcha field is not provided", %{conn: conn} do
1562 captcha_fields = [:captcha_solution, :captcha_token, :captcha_answer_data]
1563
1564 valid_params = %{
1565 username: "lain",
1566 email: "lain@example.org",
1567 password: "PlzDontHackLain",
1568 agreement: true,
1569 captcha_solution: "xx",
1570 captcha_token: "xx",
1571 captcha_answer_data: "xx"
1572 }
1573
1574 for field <- captcha_fields do
1575 expected = %{
1576 "error" => "{\"captcha\":[\"Invalid CAPTCHA (Missing parameter: #{field})\"]}"
1577 }
1578
1579 assert expected ==
1580 conn
1581 |> post("/api/v1/accounts", Map.delete(valid_params, field))
1582 |> json_response_and_validate_schema(:bad_request)
1583 end
1584 end
1585
1586 test "returns an error if captcha is invalid", %{conn: conn} do
1587 params = %{
1588 username: "lain",
1589 email: "lain@example.org",
1590 password: "PlzDontHackLain",
1591 agreement: true,
1592 captcha_solution: "cofe",
1593 captcha_token: "cofe",
1594 captcha_answer_data: "cofe"
1595 }
1596
1597 assert %{"error" => "{\"captcha\":[\"Invalid answer data\"]}"} ==
1598 conn
1599 |> post("/api/v1/accounts", params)
1600 |> json_response_and_validate_schema(:bad_request)
1601 end
1602 end
1603
1604 describe "create account with language" do
1605 setup %{conn: conn} do
1606 app_token = insert(:oauth_token, user: nil)
1607
1608 conn =
1609 conn
1610 |> put_req_header("authorization", "Bearer " <> app_token.token)
1611 |> put_req_header("content-type", "multipart/form-data")
1612 |> put_req_cookie(SetLocalePlug.frontend_language_cookie_name(), "zh-Hans")
1613 |> SetLocalePlug.call([])
1614
1615 [conn: conn]
1616 end
1617
1618 test "creates an account with language parameter", %{conn: conn} do
1619 params = %{
1620 username: "foo",
1621 email: "foo@example.org",
1622 password: "dupa.8",
1623 agreement: true,
1624 language: "ru"
1625 }
1626
1627 res =
1628 conn
1629 |> post("/api/v1/accounts", params)
1630
1631 assert json_response_and_validate_schema(res, 200)
1632
1633 assert %{language: "ru"} = Pleroma.User.get_by_nickname("foo")
1634 end
1635
1636 test "language parameter should be normalized", %{conn: conn} do
1637 params = %{
1638 username: "foo",
1639 email: "foo@example.org",
1640 password: "dupa.8",
1641 agreement: true,
1642 language: "ru-RU"
1643 }
1644
1645 res =
1646 conn
1647 |> post("/api/v1/accounts", params)
1648
1649 assert json_response_and_validate_schema(res, 200)
1650
1651 assert %{language: "ru_RU"} = Pleroma.User.get_by_nickname("foo")
1652 end
1653
1654 test "createing an account without language parameter should fallback to cookie/header language",
1655 %{conn: conn} do
1656 params = %{
1657 username: "foo2",
1658 email: "foo2@example.org",
1659 password: "dupa.8",
1660 agreement: true
1661 }
1662
1663 res =
1664 conn
1665 |> post("/api/v1/accounts", params)
1666
1667 assert json_response_and_validate_schema(res, 200)
1668
1669 assert %{language: "zh_Hans"} = Pleroma.User.get_by_nickname("foo2")
1670 end
1671 end
1672
1673 describe "GET /api/v1/accounts/:id/lists - account_lists" do
1674 test "returns lists to which the account belongs" do
1675 %{user: user, conn: conn} = oauth_access(["read:lists"])
1676 other_user = insert(:user)
1677 assert {:ok, %Pleroma.List{id: _list_id} = list} = Pleroma.List.create("Test List", user)
1678 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
1679
1680 assert [%{"id" => _list_id, "title" => "Test List"}] =
1681 conn
1682 |> get("/api/v1/accounts/#{other_user.id}/lists")
1683 |> json_response_and_validate_schema(200)
1684 end
1685 end
1686
1687 describe "verify_credentials" do
1688 test "verify_credentials" do
1689 %{user: user, conn: conn} = oauth_access(["read:accounts"])
1690
1691 [notification | _] =
1692 insert_list(7, :notification, user: user, activity: insert(:note_activity))
1693
1694 Pleroma.Notification.set_read_up_to(user, notification.id)
1695 conn = get(conn, "/api/v1/accounts/verify_credentials")
1696
1697 response = json_response_and_validate_schema(conn, 200)
1698
1699 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
1700 assert response["pleroma"]["unread_notifications_count"] == 6
1701 assert id == to_string(user.id)
1702 end
1703
1704 test "verify_credentials default scope unlisted" do
1705 user = insert(:user, default_scope: "unlisted")
1706 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1707
1708 conn = get(conn, "/api/v1/accounts/verify_credentials")
1709
1710 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} =
1711 json_response_and_validate_schema(conn, 200)
1712
1713 assert id == to_string(user.id)
1714 end
1715
1716 test "locked accounts" do
1717 user = insert(:user, default_scope: "private")
1718 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1719
1720 conn = get(conn, "/api/v1/accounts/verify_credentials")
1721
1722 assert %{"id" => id, "source" => %{"privacy" => "private"}} =
1723 json_response_and_validate_schema(conn, 200)
1724
1725 assert id == to_string(user.id)
1726 end
1727 end
1728
1729 describe "user relationships" do
1730 setup do: oauth_access(["read:follows"])
1731
1732 test "returns the relationships for the current user", %{user: user, conn: conn} do
1733 %{id: other_user_id} = other_user = insert(:user)
1734 {:ok, _user, _other_user} = User.follow(user, other_user)
1735
1736 assert [%{"id" => ^other_user_id}] =
1737 conn
1738 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1739 |> json_response_and_validate_schema(200)
1740
1741 assert [%{"id" => ^other_user_id}] =
1742 conn
1743 |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
1744 |> json_response_and_validate_schema(200)
1745 end
1746
1747 test "returns an empty list on a bad request", %{conn: conn} do
1748 conn = get(conn, "/api/v1/accounts/relationships", %{})
1749
1750 assert [] = json_response_and_validate_schema(conn, 200)
1751 end
1752 end
1753
1754 test "getting a list of mutes" do
1755 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1756 %{id: id1} = other_user1 = insert(:user)
1757 %{id: id2} = other_user2 = insert(:user)
1758 %{id: id3} = other_user3 = insert(:user)
1759
1760 {:ok, _user_relationships} = User.mute(user, other_user1)
1761 {:ok, _user_relationships} = User.mute(user, other_user2)
1762 {:ok, _user_relationships} = User.mute(user, other_user3)
1763
1764 result =
1765 conn
1766 |> get("/api/v1/mutes")
1767 |> json_response_and_validate_schema(200)
1768
1769 assert [id1, id2, id3] == Enum.map(result, & &1["id"])
1770
1771 result =
1772 conn
1773 |> get("/api/v1/mutes?limit=1")
1774 |> json_response_and_validate_schema(200)
1775
1776 assert [%{"id" => ^id1}] = result
1777
1778 result =
1779 conn
1780 |> get("/api/v1/mutes?since_id=#{id1}")
1781 |> json_response_and_validate_schema(200)
1782
1783 assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
1784
1785 result =
1786 conn
1787 |> get("/api/v1/mutes?since_id=#{id1}&max_id=#{id3}")
1788 |> json_response_and_validate_schema(200)
1789
1790 assert [%{"id" => ^id2}] = result
1791
1792 result =
1793 conn
1794 |> get("/api/v1/mutes?since_id=#{id1}&limit=1")
1795 |> json_response_and_validate_schema(200)
1796
1797 assert [%{"id" => ^id2}] = result
1798 end
1799
1800 test "list of mutes with with_relationships parameter" do
1801 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1802 %{id: id1} = other_user1 = insert(:user)
1803 %{id: id2} = other_user2 = insert(:user)
1804 %{id: id3} = other_user3 = insert(:user)
1805
1806 {:ok, _, _} = User.follow(other_user1, user)
1807 {:ok, _, _} = User.follow(other_user2, user)
1808 {:ok, _, _} = User.follow(other_user3, user)
1809
1810 {:ok, _} = User.mute(user, other_user1)
1811 {:ok, _} = User.mute(user, other_user2)
1812 {:ok, _} = User.mute(user, other_user3)
1813
1814 assert [
1815 %{
1816 "id" => ^id1,
1817 "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
1818 },
1819 %{
1820 "id" => ^id2,
1821 "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
1822 },
1823 %{
1824 "id" => ^id3,
1825 "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
1826 }
1827 ] =
1828 conn
1829 |> get("/api/v1/mutes?with_relationships=true")
1830 |> json_response_and_validate_schema(200)
1831 end
1832
1833 test "getting a list of blocks" do
1834 %{user: user, conn: conn} = oauth_access(["read:blocks"])
1835 %{id: id1} = other_user1 = insert(:user)
1836 %{id: id2} = other_user2 = insert(:user)
1837 %{id: id3} = other_user3 = insert(:user)
1838
1839 {:ok, _user_relationship} = User.block(user, other_user1)
1840 {:ok, _user_relationship} = User.block(user, other_user3)
1841 {:ok, _user_relationship} = User.block(user, other_user2)
1842
1843 result =
1844 conn
1845 |> assign(:user, user)
1846 |> get("/api/v1/blocks")
1847 |> json_response_and_validate_schema(200)
1848
1849 assert [id1, id2, id3] == Enum.map(result, & &1["id"])
1850
1851 result =
1852 conn
1853 |> assign(:user, user)
1854 |> get("/api/v1/blocks?limit=1")
1855 |> json_response_and_validate_schema(200)
1856
1857 assert [%{"id" => ^id1}] = result
1858
1859 result =
1860 conn
1861 |> assign(:user, user)
1862 |> get("/api/v1/blocks?since_id=#{id1}")
1863 |> json_response_and_validate_schema(200)
1864
1865 assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
1866
1867 result =
1868 conn
1869 |> assign(:user, user)
1870 |> get("/api/v1/blocks?since_id=#{id1}&max_id=#{id3}")
1871 |> json_response_and_validate_schema(200)
1872
1873 assert [%{"id" => ^id2}] = result
1874
1875 result =
1876 conn
1877 |> assign(:user, user)
1878 |> get("/api/v1/blocks?since_id=#{id1}&limit=1")
1879 |> json_response_and_validate_schema(200)
1880
1881 assert [%{"id" => ^id2}] = result
1882 end
1883
1884 test "account lookup", %{conn: conn} do
1885 %{nickname: acct} = insert(:user, %{nickname: "nickname"})
1886 %{nickname: acct_two} = insert(:user, %{nickname: "nickname@notlocaldoma.in"})
1887
1888 result =
1889 conn
1890 |> get("/api/v1/accounts/lookup?acct=#{acct}")
1891 |> json_response_and_validate_schema(200)
1892
1893 assert %{"acct" => ^acct} = result
1894
1895 result =
1896 conn
1897 |> get("/api/v1/accounts/lookup?acct=#{acct_two}")
1898 |> json_response_and_validate_schema(200)
1899
1900 assert %{"acct" => ^acct_two} = result
1901
1902 _result =
1903 conn
1904 |> get("/api/v1/accounts/lookup?acct=unexisting_nickname")
1905 |> json_response_and_validate_schema(404)
1906 end
1907
1908 test "create a note on a user" do
1909 %{conn: conn} = oauth_access(["write:accounts", "read:follows"])
1910 other_user = insert(:user)
1911
1912 conn
1913 |> put_req_header("content-type", "application/json")
1914 |> post("/api/v1/accounts/#{other_user.id}/note", %{
1915 "comment" => "Example note"
1916 })
1917
1918 assert [%{"note" => "Example note"}] =
1919 conn
1920 |> put_req_header("content-type", "application/json")
1921 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1922 |> json_response_and_validate_schema(200)
1923 end
1924
1925 describe "remove from followers" do
1926 setup do: oauth_access(["follow"])
1927
1928 test "removing user from followers", %{conn: conn, user: user} do
1929 %{id: other_user_id} = other_user = insert(:user)
1930
1931 CommonAPI.follow(other_user, user)
1932
1933 assert %{"id" => ^other_user_id, "followed_by" => false} =
1934 conn
1935 |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
1936 |> json_response_and_validate_schema(200)
1937
1938 refute User.following?(other_user, user)
1939 end
1940
1941 test "removing remote user from followers", %{conn: conn, user: user} do
1942 %{id: other_user_id} = other_user = insert(:user, local: false)
1943
1944 CommonAPI.follow(other_user, user)
1945
1946 assert User.following?(other_user, user)
1947
1948 assert %{"id" => ^other_user_id, "followed_by" => false} =
1949 conn
1950 |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
1951 |> json_response_and_validate_schema(200)
1952
1953 refute User.following?(other_user, user)
1954 end
1955
1956 test "removing user from followers errors", %{user: user, conn: conn} do
1957 # self remove
1958 conn_res = post(conn, "/api/v1/accounts/#{user.id}/remove_from_followers")
1959
1960 assert %{"error" => "Can not unfollow yourself"} =
1961 json_response_and_validate_schema(conn_res, 400)
1962
1963 # remove non existing user
1964 conn_res = post(conn, "/api/v1/accounts/doesntexist/remove_from_followers")
1965 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
1966 end
1967 end
1968 end