Change follow_operation schema to use type BooleanLike (#301)
[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 %{"showing_reblogs" => true} =
906 conn
907 |> put_req_header("content-type", "application/json")
908 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: "1"})
909 |> json_response_and_validate_schema(200)
910
911 assert [%{"id" => ^reblog_id}] =
912 conn
913 |> get("/api/v1/timelines/home")
914 |> json_response_and_validate_schema(200)
915 end
916
917 test "following with reblogs" do
918 %{conn: conn} = oauth_access(["follow", "read:statuses"])
919 followed = insert(:user)
920 other_user = insert(:user)
921
922 ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow")
923
924 assert %{"showing_reblogs" => true} = json_response_and_validate_schema(ret_conn, 200)
925
926 {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
927 {:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed)
928
929 assert [%{"id" => ^reblog_id}] =
930 conn
931 |> get("/api/v1/timelines/home")
932 |> json_response_and_validate_schema(200)
933
934 assert %{"showing_reblogs" => false} =
935 conn
936 |> put_req_header("content-type", "application/json")
937 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false})
938 |> json_response_and_validate_schema(200)
939
940 assert %{"showing_reblogs" => false} =
941 conn
942 |> put_req_header("content-type", "application/json")
943 |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: "0"})
944 |> json_response_and_validate_schema(200)
945
946 assert [] ==
947 conn
948 |> get("/api/v1/timelines/home")
949 |> json_response_and_validate_schema(200)
950 end
951
952 test "following with subscription and unsubscribing" do
953 %{conn: conn} = oauth_access(["follow"])
954 followed = insert(:user)
955
956 assert %{"subscribing" => true} =
957 conn
958 |> put_req_header("content-type", "application/json")
959 |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: true})
960 |> json_response_and_validate_schema(200)
961
962 assert %{"subscribing" => true} =
963 conn
964 |> put_req_header("content-type", "application/json")
965 |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: "1"})
966 |> json_response_and_validate_schema(200)
967
968 assert %{"subscribing" => false} =
969 conn
970 |> put_req_header("content-type", "application/json")
971 |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: false})
972 |> json_response_and_validate_schema(200)
973 end
974
975 test "following / unfollowing errors", %{user: user, conn: conn} do
976 # self follow
977 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
978
979 assert %{"error" => "Can not follow yourself"} =
980 json_response_and_validate_schema(conn_res, 400)
981
982 # self unfollow
983 user = User.get_cached_by_id(user.id)
984 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
985
986 assert %{"error" => "Can not unfollow yourself"} =
987 json_response_and_validate_schema(conn_res, 400)
988
989 # self follow via uri
990 user = User.get_cached_by_id(user.id)
991
992 assert %{"error" => "Can not follow yourself"} =
993 conn
994 |> put_req_header("content-type", "multipart/form-data")
995 |> post("/api/v1/follows", %{"uri" => user.nickname})
996 |> json_response_and_validate_schema(400)
997
998 # follow non existing user
999 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1000 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
1001
1002 # follow non existing user via uri
1003 conn_res =
1004 conn
1005 |> put_req_header("content-type", "multipart/form-data")
1006 |> post("/api/v1/follows", %{"uri" => "doesntexist"})
1007
1008 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
1009
1010 # unfollow non existing user
1011 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1012 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
1013 end
1014 end
1015
1016 describe "mute/unmute" do
1017 setup do: oauth_access(["write:mutes"])
1018
1019 test "with notifications", %{conn: conn} do
1020 other_user = insert(:user)
1021
1022 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} =
1023 conn
1024 |> post("/api/v1/accounts/#{other_user.id}/mute")
1025 |> json_response_and_validate_schema(200)
1026
1027 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
1028
1029 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
1030 json_response_and_validate_schema(conn, 200)
1031 end
1032
1033 test "without notifications", %{conn: conn} do
1034 other_user = insert(:user)
1035
1036 ret_conn =
1037 conn
1038 |> put_req_header("content-type", "multipart/form-data")
1039 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
1040
1041 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} =
1042 json_response_and_validate_schema(ret_conn, 200)
1043
1044 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unmute")
1045
1046 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} =
1047 json_response_and_validate_schema(conn, 200)
1048 end
1049 end
1050
1051 describe "pinned statuses" do
1052 setup do
1053 user = insert(:user)
1054 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
1055 %{conn: conn} = oauth_access(["read:statuses"], user: user)
1056
1057 [conn: conn, user: user, activity: activity]
1058 end
1059
1060 test "returns pinned statuses", %{conn: conn, user: user, activity: %{id: activity_id}} do
1061 {:ok, _} = CommonAPI.pin(activity_id, user)
1062
1063 assert [%{"id" => ^activity_id, "pinned" => true}] =
1064 conn
1065 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1066 |> json_response_and_validate_schema(200)
1067 end
1068 end
1069
1070 test "blocking / unblocking a user" do
1071 %{conn: conn} = oauth_access(["follow"])
1072 other_user = insert(:user)
1073
1074 ret_conn = post(conn, "/api/v1/accounts/#{other_user.id}/block")
1075
1076 assert %{"id" => _id, "blocking" => true} = json_response_and_validate_schema(ret_conn, 200)
1077
1078 conn = post(conn, "/api/v1/accounts/#{other_user.id}/unblock")
1079
1080 assert %{"id" => _id, "blocking" => false} = json_response_and_validate_schema(conn, 200)
1081 end
1082
1083 describe "create account by app" do
1084 setup do
1085 valid_params = %{
1086 username: "lain",
1087 email: "lain@example.org",
1088 password: "PlzDontHackLain",
1089 agreement: true
1090 }
1091
1092 [valid_params: valid_params]
1093 end
1094
1095 test "registers and logs in without :account_activation_required / :account_approval_required",
1096 %{conn: conn} do
1097 clear_config([:instance, :account_activation_required], false)
1098 clear_config([:instance, :account_approval_required], false)
1099
1100 conn =
1101 conn
1102 |> put_req_header("content-type", "application/json")
1103 |> post("/api/v1/apps", %{
1104 client_name: "client_name",
1105 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1106 scopes: "read, write, follow"
1107 })
1108
1109 assert %{
1110 "client_id" => client_id,
1111 "client_secret" => client_secret,
1112 "id" => _,
1113 "name" => "client_name",
1114 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1115 "vapid_key" => _,
1116 "website" => nil
1117 } = json_response_and_validate_schema(conn, 200)
1118
1119 conn =
1120 post(conn, "/oauth/token", %{
1121 grant_type: "client_credentials",
1122 client_id: client_id,
1123 client_secret: client_secret
1124 })
1125
1126 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1127 json_response(conn, 200)
1128
1129 assert token
1130 token_from_db = Repo.get_by(Token, token: token)
1131 assert token_from_db
1132 assert refresh
1133 assert scope == "read write follow"
1134
1135 clear_config([User, :email_blacklist], ["example.org"])
1136
1137 params = %{
1138 username: "lain",
1139 email: "lain@example.org",
1140 password: "PlzDontHackLain",
1141 bio: "Test Bio",
1142 agreement: true
1143 }
1144
1145 conn =
1146 build_conn()
1147 |> put_req_header("content-type", "multipart/form-data")
1148 |> put_req_header("authorization", "Bearer " <> token)
1149 |> post("/api/v1/accounts", params)
1150
1151 assert %{"error" => "{\"email\":[\"Invalid email\"]}"} =
1152 json_response_and_validate_schema(conn, 400)
1153
1154 clear_config([User, :email_blacklist], [])
1155
1156 conn =
1157 build_conn()
1158 |> put_req_header("content-type", "multipart/form-data")
1159 |> put_req_header("authorization", "Bearer " <> token)
1160 |> post("/api/v1/accounts", params)
1161
1162 %{
1163 "access_token" => token,
1164 "created_at" => _created_at,
1165 "scope" => ^scope,
1166 "token_type" => "Bearer"
1167 } = json_response_and_validate_schema(conn, 200)
1168
1169 token_from_db = Repo.get_by(Token, token: token)
1170 assert token_from_db
1171 user = Repo.preload(token_from_db, :user).user
1172
1173 assert user
1174 assert user.is_confirmed
1175 assert user.is_approved
1176 end
1177
1178 test "registers but does not log in with :account_activation_required", %{conn: conn} do
1179 clear_config([:instance, :account_activation_required], true)
1180 clear_config([:instance, :account_approval_required], false)
1181
1182 conn =
1183 conn
1184 |> put_req_header("content-type", "application/json")
1185 |> post("/api/v1/apps", %{
1186 client_name: "client_name",
1187 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1188 scopes: "read, write, follow"
1189 })
1190
1191 assert %{
1192 "client_id" => client_id,
1193 "client_secret" => client_secret,
1194 "id" => _,
1195 "name" => "client_name",
1196 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1197 "vapid_key" => _,
1198 "website" => nil
1199 } = json_response_and_validate_schema(conn, 200)
1200
1201 conn =
1202 post(conn, "/oauth/token", %{
1203 grant_type: "client_credentials",
1204 client_id: client_id,
1205 client_secret: client_secret
1206 })
1207
1208 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1209 json_response(conn, 200)
1210
1211 assert token
1212 token_from_db = Repo.get_by(Token, token: token)
1213 assert token_from_db
1214 assert refresh
1215 assert scope == "read write follow"
1216
1217 conn =
1218 build_conn()
1219 |> put_req_header("content-type", "multipart/form-data")
1220 |> put_req_header("authorization", "Bearer " <> token)
1221 |> post("/api/v1/accounts", %{
1222 username: "lain",
1223 email: "lain@example.org",
1224 password: "PlzDontHackLain",
1225 bio: "Test Bio",
1226 agreement: true
1227 })
1228
1229 response = json_response_and_validate_schema(conn, 200)
1230 assert %{"identifier" => "missing_confirmed_email"} = response
1231 refute response["access_token"]
1232 refute response["token_type"]
1233
1234 user = Repo.get_by(User, email: "lain@example.org")
1235 refute user.is_confirmed
1236 end
1237
1238 test "registers but does not log in with :account_approval_required", %{conn: conn} do
1239 clear_config([:instance, :account_approval_required], true)
1240 clear_config([:instance, :account_activation_required], false)
1241
1242 conn =
1243 conn
1244 |> put_req_header("content-type", "application/json")
1245 |> post("/api/v1/apps", %{
1246 client_name: "client_name",
1247 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1248 scopes: "read, write, follow"
1249 })
1250
1251 assert %{
1252 "client_id" => client_id,
1253 "client_secret" => client_secret,
1254 "id" => _,
1255 "name" => "client_name",
1256 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1257 "vapid_key" => _,
1258 "website" => nil
1259 } = json_response_and_validate_schema(conn, 200)
1260
1261 conn =
1262 post(conn, "/oauth/token", %{
1263 grant_type: "client_credentials",
1264 client_id: client_id,
1265 client_secret: client_secret
1266 })
1267
1268 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1269 json_response(conn, 200)
1270
1271 assert token
1272 token_from_db = Repo.get_by(Token, token: token)
1273 assert token_from_db
1274 assert refresh
1275 assert scope == "read write follow"
1276
1277 conn =
1278 build_conn()
1279 |> put_req_header("content-type", "multipart/form-data")
1280 |> put_req_header("authorization", "Bearer " <> token)
1281 |> post("/api/v1/accounts", %{
1282 username: "lain",
1283 email: "lain@example.org",
1284 password: "PlzDontHackLain",
1285 bio: "Test Bio",
1286 agreement: true,
1287 reason: "I'm a cool dude, bro"
1288 })
1289
1290 response = json_response_and_validate_schema(conn, 200)
1291 assert %{"identifier" => "awaiting_approval"} = response
1292 refute response["access_token"]
1293 refute response["token_type"]
1294
1295 user = Repo.get_by(User, email: "lain@example.org")
1296
1297 refute user.is_approved
1298 assert user.registration_reason == "I'm a cool dude, bro"
1299 end
1300
1301 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
1302 _user = insert(:user, email: "lain@example.org")
1303 app_token = insert(:oauth_token, user: nil)
1304
1305 res =
1306 conn
1307 |> put_req_header("authorization", "Bearer " <> app_token.token)
1308 |> put_req_header("content-type", "application/json")
1309 |> post("/api/v1/accounts", valid_params)
1310
1311 assert json_response_and_validate_schema(res, 400) == %{
1312 "error" => "{\"email\":[\"has already been taken\"]}"
1313 }
1314 end
1315
1316 test "returns bad_request if missing required params", %{
1317 conn: conn,
1318 valid_params: valid_params
1319 } do
1320 app_token = insert(:oauth_token, user: nil)
1321
1322 conn =
1323 conn
1324 |> put_req_header("authorization", "Bearer " <> app_token.token)
1325 |> put_req_header("content-type", "application/json")
1326
1327 res = post(conn, "/api/v1/accounts", valid_params)
1328 assert json_response_and_validate_schema(res, 200)
1329
1330 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
1331 |> Stream.zip(Map.delete(valid_params, :email))
1332 |> Enum.each(fn {ip, {attr, _}} ->
1333 res =
1334 conn
1335 |> Map.put(:remote_ip, ip)
1336 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
1337 |> json_response_and_validate_schema(400)
1338
1339 assert res == %{
1340 "error" => "Missing field: #{attr}.",
1341 "errors" => [
1342 %{
1343 "message" => "Missing field: #{attr}",
1344 "source" => %{"pointer" => "/#{attr}"},
1345 "title" => "Invalid value"
1346 }
1347 ]
1348 }
1349 end)
1350 end
1351
1352 test "returns bad_request if missing email params when :account_activation_required is enabled",
1353 %{conn: conn, valid_params: valid_params} do
1354 clear_config([:instance, :account_activation_required], true)
1355
1356 app_token = insert(:oauth_token, user: nil)
1357
1358 conn =
1359 conn
1360 |> put_req_header("authorization", "Bearer " <> app_token.token)
1361 |> put_req_header("content-type", "application/json")
1362
1363 res =
1364 conn
1365 |> Map.put(:remote_ip, {127, 0, 0, 5})
1366 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
1367
1368 assert json_response_and_validate_schema(res, 400) ==
1369 %{"error" => "Missing parameter: email"}
1370
1371 res =
1372 conn
1373 |> Map.put(:remote_ip, {127, 0, 0, 6})
1374 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
1375
1376 assert json_response_and_validate_schema(res, 400) == %{
1377 "error" => "{\"email\":[\"can't be blank\"]}"
1378 }
1379 end
1380
1381 test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
1382 app_token = insert(:oauth_token, user: nil)
1383 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1384
1385 res =
1386 conn
1387 |> put_req_header("content-type", "application/json")
1388 |> Map.put(:remote_ip, {127, 0, 0, 7})
1389 |> post("/api/v1/accounts", Map.delete(valid_params, :email))
1390
1391 assert json_response_and_validate_schema(res, 200)
1392 end
1393
1394 test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
1395 app_token = insert(:oauth_token, user: nil)
1396 conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1397
1398 res =
1399 conn
1400 |> put_req_header("content-type", "application/json")
1401 |> Map.put(:remote_ip, {127, 0, 0, 8})
1402 |> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
1403
1404 assert json_response_and_validate_schema(res, 200)
1405 end
1406
1407 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
1408 res =
1409 conn
1410 |> put_req_header("authorization", "Bearer " <> "invalid-token")
1411 |> put_req_header("content-type", "multipart/form-data")
1412 |> post("/api/v1/accounts", valid_params)
1413
1414 assert json_response_and_validate_schema(res, 403) == %{"error" => "Invalid credentials"}
1415 end
1416
1417 test "registration from trusted app" do
1418 clear_config([Pleroma.Captcha, :enabled], true)
1419 app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
1420
1421 conn =
1422 build_conn()
1423 |> post("/oauth/token", %{
1424 "grant_type" => "client_credentials",
1425 "client_id" => app.client_id,
1426 "client_secret" => app.client_secret
1427 })
1428
1429 assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
1430
1431 response =
1432 build_conn()
1433 |> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
1434 |> put_req_header("content-type", "multipart/form-data")
1435 |> post("/api/v1/accounts", %{
1436 nickname: "nickanme",
1437 agreement: true,
1438 email: "email@example.com",
1439 fullname: "Lain",
1440 username: "Lain",
1441 password: "some_password",
1442 confirm: "some_password"
1443 })
1444 |> json_response_and_validate_schema(200)
1445
1446 assert %{
1447 "access_token" => access_token,
1448 "created_at" => _,
1449 "scope" => "read write follow push",
1450 "token_type" => "Bearer"
1451 } = response
1452
1453 response =
1454 build_conn()
1455 |> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
1456 |> get("/api/v1/accounts/verify_credentials")
1457 |> json_response_and_validate_schema(200)
1458
1459 assert %{
1460 "acct" => "Lain",
1461 "bot" => false,
1462 "display_name" => "Lain",
1463 "follow_requests_count" => 0,
1464 "followers_count" => 0,
1465 "following_count" => 0,
1466 "locked" => false,
1467 "note" => "",
1468 "source" => %{
1469 "fields" => [],
1470 "note" => "",
1471 "pleroma" => %{
1472 "actor_type" => "Person",
1473 "discoverable" => false,
1474 "no_rich_text" => false,
1475 "show_role" => true
1476 },
1477 "privacy" => "public",
1478 "sensitive" => false
1479 },
1480 "statuses_count" => 0,
1481 "username" => "Lain"
1482 } = response
1483 end
1484 end
1485
1486 describe "create account by app / rate limit" do
1487 setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
1488
1489 test "respects rate limit setting", %{conn: conn} do
1490 app_token = insert(:oauth_token, user: nil)
1491
1492 conn =
1493 conn
1494 |> put_req_header("authorization", "Bearer " <> app_token.token)
1495 |> Map.put(:remote_ip, {15, 15, 15, 15})
1496 |> put_req_header("content-type", "multipart/form-data")
1497
1498 for i <- 1..2 do
1499 conn =
1500 conn
1501 |> post("/api/v1/accounts", %{
1502 username: "#{i}lain",
1503 email: "#{i}lain@example.org",
1504 password: "PlzDontHackLain",
1505 agreement: true
1506 })
1507
1508 %{
1509 "access_token" => token,
1510 "created_at" => _created_at,
1511 "scope" => _scope,
1512 "token_type" => "Bearer"
1513 } = json_response_and_validate_schema(conn, 200)
1514
1515 token_from_db = Repo.get_by(Token, token: token)
1516 assert token_from_db
1517 token_from_db = Repo.preload(token_from_db, :user)
1518 assert token_from_db.user
1519 end
1520
1521 conn =
1522 post(conn, "/api/v1/accounts", %{
1523 username: "6lain",
1524 email: "6lain@example.org",
1525 password: "PlzDontHackLain",
1526 agreement: true
1527 })
1528
1529 assert json_response_and_validate_schema(conn, :too_many_requests) == %{
1530 "error" => "Throttled"
1531 }
1532 end
1533 end
1534
1535 describe "create account with enabled captcha" do
1536 setup %{conn: conn} do
1537 app_token = insert(:oauth_token, user: nil)
1538
1539 conn =
1540 conn
1541 |> put_req_header("authorization", "Bearer " <> app_token.token)
1542 |> put_req_header("content-type", "multipart/form-data")
1543
1544 [conn: conn]
1545 end
1546
1547 setup do: clear_config([Pleroma.Captcha, :enabled], true)
1548
1549 test "creates an account and returns 200 if captcha is valid", %{conn: conn} do
1550 %{token: token, answer_data: answer_data} = Pleroma.Captcha.new()
1551
1552 params = %{
1553 username: "lain",
1554 email: "lain@example.org",
1555 password: "PlzDontHackLain",
1556 agreement: true,
1557 captcha_solution: Pleroma.Captcha.Mock.solution(),
1558 captcha_token: token,
1559 captcha_answer_data: answer_data
1560 }
1561
1562 assert %{
1563 "access_token" => access_token,
1564 "created_at" => _,
1565 "scope" => "read",
1566 "token_type" => "Bearer"
1567 } =
1568 conn
1569 |> post("/api/v1/accounts", params)
1570 |> json_response_and_validate_schema(:ok)
1571
1572 assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user)
1573 end
1574
1575 test "returns 400 if any captcha field is not provided", %{conn: conn} do
1576 captcha_fields = [:captcha_solution, :captcha_token, :captcha_answer_data]
1577
1578 valid_params = %{
1579 username: "lain",
1580 email: "lain@example.org",
1581 password: "PlzDontHackLain",
1582 agreement: true,
1583 captcha_solution: "xx",
1584 captcha_token: "xx",
1585 captcha_answer_data: "xx"
1586 }
1587
1588 for field <- captcha_fields do
1589 expected = %{
1590 "error" => "{\"captcha\":[\"Invalid CAPTCHA (Missing parameter: #{field})\"]}"
1591 }
1592
1593 assert expected ==
1594 conn
1595 |> post("/api/v1/accounts", Map.delete(valid_params, field))
1596 |> json_response_and_validate_schema(:bad_request)
1597 end
1598 end
1599
1600 test "returns an error if captcha is invalid", %{conn: conn} do
1601 params = %{
1602 username: "lain",
1603 email: "lain@example.org",
1604 password: "PlzDontHackLain",
1605 agreement: true,
1606 captcha_solution: "cofe",
1607 captcha_token: "cofe",
1608 captcha_answer_data: "cofe"
1609 }
1610
1611 assert %{"error" => "{\"captcha\":[\"Invalid answer data\"]}"} ==
1612 conn
1613 |> post("/api/v1/accounts", params)
1614 |> json_response_and_validate_schema(:bad_request)
1615 end
1616 end
1617
1618 describe "create account with language" do
1619 setup %{conn: conn} do
1620 app_token = insert(:oauth_token, user: nil)
1621
1622 conn =
1623 conn
1624 |> put_req_header("authorization", "Bearer " <> app_token.token)
1625 |> put_req_header("content-type", "multipart/form-data")
1626 |> put_req_cookie(SetLocalePlug.frontend_language_cookie_name(), "zh-Hans")
1627 |> SetLocalePlug.call([])
1628
1629 [conn: conn]
1630 end
1631
1632 test "creates an account with language parameter", %{conn: conn} do
1633 params = %{
1634 username: "foo",
1635 email: "foo@example.org",
1636 password: "dupa.8",
1637 agreement: true,
1638 language: "ru"
1639 }
1640
1641 res =
1642 conn
1643 |> post("/api/v1/accounts", params)
1644
1645 assert json_response_and_validate_schema(res, 200)
1646
1647 assert %{language: "ru"} = Pleroma.User.get_by_nickname("foo")
1648 end
1649
1650 test "language parameter should be normalized", %{conn: conn} do
1651 params = %{
1652 username: "foo",
1653 email: "foo@example.org",
1654 password: "dupa.8",
1655 agreement: true,
1656 language: "ru-RU"
1657 }
1658
1659 res =
1660 conn
1661 |> post("/api/v1/accounts", params)
1662
1663 assert json_response_and_validate_schema(res, 200)
1664
1665 assert %{language: "ru_RU"} = Pleroma.User.get_by_nickname("foo")
1666 end
1667
1668 test "createing an account without language parameter should fallback to cookie/header language",
1669 %{conn: conn} do
1670 params = %{
1671 username: "foo2",
1672 email: "foo2@example.org",
1673 password: "dupa.8",
1674 agreement: true
1675 }
1676
1677 res =
1678 conn
1679 |> post("/api/v1/accounts", params)
1680
1681 assert json_response_and_validate_schema(res, 200)
1682
1683 assert %{language: "zh_Hans"} = Pleroma.User.get_by_nickname("foo2")
1684 end
1685 end
1686
1687 describe "GET /api/v1/accounts/:id/lists - account_lists" do
1688 test "returns lists to which the account belongs" do
1689 %{user: user, conn: conn} = oauth_access(["read:lists"])
1690 other_user = insert(:user)
1691 assert {:ok, %Pleroma.List{id: _list_id} = list} = Pleroma.List.create("Test List", user)
1692 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
1693
1694 assert [%{"id" => _list_id, "title" => "Test List"}] =
1695 conn
1696 |> get("/api/v1/accounts/#{other_user.id}/lists")
1697 |> json_response_and_validate_schema(200)
1698 end
1699 end
1700
1701 describe "verify_credentials" do
1702 test "verify_credentials" do
1703 %{user: user, conn: conn} = oauth_access(["read:accounts"])
1704
1705 [notification | _] =
1706 insert_list(7, :notification, user: user, activity: insert(:note_activity))
1707
1708 Pleroma.Notification.set_read_up_to(user, notification.id)
1709 conn = get(conn, "/api/v1/accounts/verify_credentials")
1710
1711 response = json_response_and_validate_schema(conn, 200)
1712
1713 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
1714 assert response["pleroma"]["unread_notifications_count"] == 6
1715 assert id == to_string(user.id)
1716 end
1717
1718 test "verify_credentials default scope unlisted" do
1719 user = insert(:user, default_scope: "unlisted")
1720 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1721
1722 conn = get(conn, "/api/v1/accounts/verify_credentials")
1723
1724 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} =
1725 json_response_and_validate_schema(conn, 200)
1726
1727 assert id == to_string(user.id)
1728 end
1729
1730 test "locked accounts" do
1731 user = insert(:user, default_scope: "private")
1732 %{conn: conn} = oauth_access(["read:accounts"], user: user)
1733
1734 conn = get(conn, "/api/v1/accounts/verify_credentials")
1735
1736 assert %{"id" => id, "source" => %{"privacy" => "private"}} =
1737 json_response_and_validate_schema(conn, 200)
1738
1739 assert id == to_string(user.id)
1740 end
1741 end
1742
1743 describe "user relationships" do
1744 setup do: oauth_access(["read:follows"])
1745
1746 test "returns the relationships for the current user", %{user: user, conn: conn} do
1747 %{id: other_user_id} = other_user = insert(:user)
1748 {:ok, _user, _other_user} = User.follow(user, other_user)
1749
1750 assert [%{"id" => ^other_user_id}] =
1751 conn
1752 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1753 |> json_response_and_validate_schema(200)
1754
1755 assert [%{"id" => ^other_user_id}] =
1756 conn
1757 |> get("/api/v1/accounts/relationships?id[]=#{other_user.id}")
1758 |> json_response_and_validate_schema(200)
1759 end
1760
1761 test "returns an empty list on a bad request", %{conn: conn} do
1762 conn = get(conn, "/api/v1/accounts/relationships", %{})
1763
1764 assert [] = json_response_and_validate_schema(conn, 200)
1765 end
1766 end
1767
1768 test "getting a list of mutes" do
1769 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1770 %{id: id1} = other_user1 = insert(:user)
1771 %{id: id2} = other_user2 = insert(:user)
1772 %{id: id3} = other_user3 = insert(:user)
1773
1774 {:ok, _user_relationships} = User.mute(user, other_user1)
1775 {:ok, _user_relationships} = User.mute(user, other_user2)
1776 {:ok, _user_relationships} = User.mute(user, other_user3)
1777
1778 result =
1779 conn
1780 |> get("/api/v1/mutes")
1781 |> json_response_and_validate_schema(200)
1782
1783 assert [id1, id2, id3] == Enum.map(result, & &1["id"])
1784
1785 result =
1786 conn
1787 |> get("/api/v1/mutes?limit=1")
1788 |> json_response_and_validate_schema(200)
1789
1790 assert [%{"id" => ^id1}] = result
1791
1792 result =
1793 conn
1794 |> get("/api/v1/mutes?since_id=#{id1}")
1795 |> json_response_and_validate_schema(200)
1796
1797 assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
1798
1799 result =
1800 conn
1801 |> get("/api/v1/mutes?since_id=#{id1}&max_id=#{id3}")
1802 |> json_response_and_validate_schema(200)
1803
1804 assert [%{"id" => ^id2}] = result
1805
1806 result =
1807 conn
1808 |> get("/api/v1/mutes?since_id=#{id1}&limit=1")
1809 |> json_response_and_validate_schema(200)
1810
1811 assert [%{"id" => ^id2}] = result
1812 end
1813
1814 test "list of mutes with with_relationships parameter" do
1815 %{user: user, conn: conn} = oauth_access(["read:mutes"])
1816 %{id: id1} = other_user1 = insert(:user)
1817 %{id: id2} = other_user2 = insert(:user)
1818 %{id: id3} = other_user3 = insert(:user)
1819
1820 {:ok, _, _} = User.follow(other_user1, user)
1821 {:ok, _, _} = User.follow(other_user2, user)
1822 {:ok, _, _} = User.follow(other_user3, user)
1823
1824 {:ok, _} = User.mute(user, other_user1)
1825 {:ok, _} = User.mute(user, other_user2)
1826 {:ok, _} = User.mute(user, other_user3)
1827
1828 assert [
1829 %{
1830 "id" => ^id1,
1831 "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
1832 },
1833 %{
1834 "id" => ^id2,
1835 "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
1836 },
1837 %{
1838 "id" => ^id3,
1839 "pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
1840 }
1841 ] =
1842 conn
1843 |> get("/api/v1/mutes?with_relationships=true")
1844 |> json_response_and_validate_schema(200)
1845 end
1846
1847 test "getting a list of blocks" do
1848 %{user: user, conn: conn} = oauth_access(["read:blocks"])
1849 %{id: id1} = other_user1 = insert(:user)
1850 %{id: id2} = other_user2 = insert(:user)
1851 %{id: id3} = other_user3 = insert(:user)
1852
1853 {:ok, _user_relationship} = User.block(user, other_user1)
1854 {:ok, _user_relationship} = User.block(user, other_user3)
1855 {:ok, _user_relationship} = User.block(user, other_user2)
1856
1857 result =
1858 conn
1859 |> assign(:user, user)
1860 |> get("/api/v1/blocks")
1861 |> json_response_and_validate_schema(200)
1862
1863 assert [id1, id2, id3] == Enum.map(result, & &1["id"])
1864
1865 result =
1866 conn
1867 |> assign(:user, user)
1868 |> get("/api/v1/blocks?limit=1")
1869 |> json_response_and_validate_schema(200)
1870
1871 assert [%{"id" => ^id1}] = result
1872
1873 result =
1874 conn
1875 |> assign(:user, user)
1876 |> get("/api/v1/blocks?since_id=#{id1}")
1877 |> json_response_and_validate_schema(200)
1878
1879 assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
1880
1881 result =
1882 conn
1883 |> assign(:user, user)
1884 |> get("/api/v1/blocks?since_id=#{id1}&max_id=#{id3}")
1885 |> json_response_and_validate_schema(200)
1886
1887 assert [%{"id" => ^id2}] = result
1888
1889 result =
1890 conn
1891 |> assign(:user, user)
1892 |> get("/api/v1/blocks?since_id=#{id1}&limit=1")
1893 |> json_response_and_validate_schema(200)
1894
1895 assert [%{"id" => ^id2}] = result
1896 end
1897
1898 test "account lookup", %{conn: conn} do
1899 %{nickname: acct} = insert(:user, %{nickname: "nickname"})
1900 %{nickname: acct_two} = insert(:user, %{nickname: "nickname@notlocaldoma.in"})
1901
1902 result =
1903 conn
1904 |> get("/api/v1/accounts/lookup?acct=#{acct}")
1905 |> json_response_and_validate_schema(200)
1906
1907 assert %{"acct" => ^acct} = result
1908
1909 result =
1910 conn
1911 |> get("/api/v1/accounts/lookup?acct=#{acct_two}")
1912 |> json_response_and_validate_schema(200)
1913
1914 assert %{"acct" => ^acct_two} = result
1915
1916 _result =
1917 conn
1918 |> get("/api/v1/accounts/lookup?acct=unexisting_nickname")
1919 |> json_response_and_validate_schema(404)
1920 end
1921
1922 test "create a note on a user" do
1923 %{conn: conn} = oauth_access(["write:accounts", "read:follows"])
1924 other_user = insert(:user)
1925
1926 conn
1927 |> put_req_header("content-type", "application/json")
1928 |> post("/api/v1/accounts/#{other_user.id}/note", %{
1929 "comment" => "Example note"
1930 })
1931
1932 assert [%{"note" => "Example note"}] =
1933 conn
1934 |> put_req_header("content-type", "application/json")
1935 |> get("/api/v1/accounts/relationships?id=#{other_user.id}")
1936 |> json_response_and_validate_schema(200)
1937 end
1938
1939 describe "remove from followers" do
1940 setup do: oauth_access(["follow"])
1941
1942 test "removing user from followers", %{conn: conn, user: user} do
1943 %{id: other_user_id} = other_user = insert(:user)
1944
1945 CommonAPI.follow(other_user, user)
1946
1947 assert %{"id" => ^other_user_id, "followed_by" => false} =
1948 conn
1949 |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
1950 |> json_response_and_validate_schema(200)
1951
1952 refute User.following?(other_user, user)
1953 end
1954
1955 test "removing remote user from followers", %{conn: conn, user: user} do
1956 %{id: other_user_id} = other_user = insert(:user, local: false)
1957
1958 CommonAPI.follow(other_user, user)
1959
1960 assert User.following?(other_user, user)
1961
1962 assert %{"id" => ^other_user_id, "followed_by" => false} =
1963 conn
1964 |> post("/api/v1/accounts/#{other_user_id}/remove_from_followers")
1965 |> json_response_and_validate_schema(200)
1966
1967 refute User.following?(other_user, user)
1968 end
1969
1970 test "removing user from followers errors", %{user: user, conn: conn} do
1971 # self remove
1972 conn_res = post(conn, "/api/v1/accounts/#{user.id}/remove_from_followers")
1973
1974 assert %{"error" => "Can not unfollow yourself"} =
1975 json_response_and_validate_schema(conn_res, 400)
1976
1977 # remove non existing user
1978 conn_res = post(conn, "/api/v1/accounts/doesntexist/remove_from_followers")
1979 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn_res, 404)
1980 end
1981 end
1982 end