Merge branch 'bugfix/post-empty-status' into 'develop'
[akkoma] / test / web / mastodon_api / mastodon_api_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
7
8 alias Ecto.Changeset
9 alias Pleroma.Config
10 alias Pleroma.Notification
11 alias Pleroma.Object
12 alias Pleroma.Repo
13 alias Pleroma.Tests.ObanHelpers
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.OAuth.App
18 alias Pleroma.Web.OAuth.Token
19 alias Pleroma.Web.Push
20
21 import ExUnit.CaptureLog
22 import Pleroma.Factory
23 import Swoosh.TestAssertions
24 import Tesla.Mock
25
26 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
27
28 setup do
29 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
30 :ok
31 end
32
33 clear_config([:instance, :public])
34 clear_config([:rich_media, :enabled])
35
36 test "Conversations", %{conn: conn} do
37 user_one = insert(:user)
38 user_two = insert(:user)
39 user_three = insert(:user)
40
41 {:ok, user_two} = User.follow(user_two, user_one)
42
43 {:ok, direct} =
44 CommonAPI.post(user_one, %{
45 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
46 "visibility" => "direct"
47 })
48
49 {:ok, _follower_only} =
50 CommonAPI.post(user_one, %{
51 "status" => "Hi @#{user_two.nickname}!",
52 "visibility" => "private"
53 })
54
55 res_conn =
56 conn
57 |> assign(:user, user_one)
58 |> get("/api/v1/conversations")
59
60 assert response = json_response(res_conn, 200)
61
62 assert [
63 %{
64 "id" => res_id,
65 "accounts" => res_accounts,
66 "last_status" => res_last_status,
67 "unread" => unread
68 }
69 ] = response
70
71 account_ids = Enum.map(res_accounts, & &1["id"])
72 assert length(res_accounts) == 2
73 assert user_two.id in account_ids
74 assert user_three.id in account_ids
75 assert is_binary(res_id)
76 assert unread == true
77 assert res_last_status["id"] == direct.id
78
79 # Apparently undocumented API endpoint
80 res_conn =
81 conn
82 |> assign(:user, user_one)
83 |> post("/api/v1/conversations/#{res_id}/read")
84
85 assert response = json_response(res_conn, 200)
86 assert length(response["accounts"]) == 2
87 assert response["last_status"]["id"] == direct.id
88 assert response["unread"] == false
89
90 # (vanilla) Mastodon frontend behaviour
91 res_conn =
92 conn
93 |> assign(:user, user_one)
94 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
95
96 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
97 end
98
99 test "verify_credentials", %{conn: conn} do
100 user = insert(:user)
101
102 conn =
103 conn
104 |> assign(:user, user)
105 |> get("/api/v1/accounts/verify_credentials")
106
107 response = json_response(conn, 200)
108
109 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
110 assert response["pleroma"]["chat_token"]
111 assert id == to_string(user.id)
112 end
113
114 test "verify_credentials default scope unlisted", %{conn: conn} do
115 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
116
117 conn =
118 conn
119 |> assign(:user, user)
120 |> get("/api/v1/accounts/verify_credentials")
121
122 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
123 assert id == to_string(user.id)
124 end
125
126 test "apps/verify_credentials", %{conn: conn} do
127 token = insert(:oauth_token)
128
129 conn =
130 conn
131 |> assign(:user, token.user)
132 |> assign(:token, token)
133 |> get("/api/v1/apps/verify_credentials")
134
135 app = Repo.preload(token, :app).app
136
137 expected = %{
138 "name" => app.client_name,
139 "website" => app.website,
140 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
141 }
142
143 assert expected == json_response(conn, 200)
144 end
145
146 test "user avatar can be set", %{conn: conn} do
147 user = insert(:user)
148 avatar_image = File.read!("test/fixtures/avatar_data_uri")
149
150 conn =
151 conn
152 |> assign(:user, user)
153 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
154
155 user = refresh_record(user)
156
157 assert %{
158 "name" => _,
159 "type" => _,
160 "url" => [
161 %{
162 "href" => _,
163 "mediaType" => _,
164 "type" => _
165 }
166 ]
167 } = user.avatar
168
169 assert %{"url" => _} = json_response(conn, 200)
170 end
171
172 test "user avatar can be reset", %{conn: conn} do
173 user = insert(:user)
174
175 conn =
176 conn
177 |> assign(:user, user)
178 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
179
180 user = User.get_cached_by_id(user.id)
181
182 assert user.avatar == nil
183
184 assert %{"url" => nil} = json_response(conn, 200)
185 end
186
187 test "can set profile banner", %{conn: conn} do
188 user = insert(:user)
189
190 conn =
191 conn
192 |> assign(:user, user)
193 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
194
195 user = refresh_record(user)
196 assert user.info.banner["type"] == "Image"
197
198 assert %{"url" => _} = json_response(conn, 200)
199 end
200
201 test "can reset profile banner", %{conn: conn} do
202 user = insert(:user)
203
204 conn =
205 conn
206 |> assign(:user, user)
207 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
208
209 user = refresh_record(user)
210 assert user.info.banner == %{}
211
212 assert %{"url" => nil} = json_response(conn, 200)
213 end
214
215 test "background image can be set", %{conn: conn} do
216 user = insert(:user)
217
218 conn =
219 conn
220 |> assign(:user, user)
221 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
222
223 user = refresh_record(user)
224 assert user.info.background["type"] == "Image"
225 assert %{"url" => _} = json_response(conn, 200)
226 end
227
228 test "background image can be reset", %{conn: conn} do
229 user = insert(:user)
230
231 conn =
232 conn
233 |> assign(:user, user)
234 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
235
236 user = refresh_record(user)
237 assert user.info.background == %{}
238 assert %{"url" => nil} = json_response(conn, 200)
239 end
240
241 test "creates an oauth app", %{conn: conn} do
242 user = insert(:user)
243 app_attrs = build(:oauth_app)
244
245 conn =
246 conn
247 |> assign(:user, user)
248 |> post("/api/v1/apps", %{
249 client_name: app_attrs.client_name,
250 redirect_uris: app_attrs.redirect_uris
251 })
252
253 [app] = Repo.all(App)
254
255 expected = %{
256 "name" => app.client_name,
257 "website" => app.website,
258 "client_id" => app.client_id,
259 "client_secret" => app.client_secret,
260 "id" => app.id |> to_string(),
261 "redirect_uri" => app.redirect_uris,
262 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
263 }
264
265 assert expected == json_response(conn, 200)
266 end
267
268 describe "user timelines" do
269 test "gets a users statuses", %{conn: conn} do
270 user_one = insert(:user)
271 user_two = insert(:user)
272 user_three = insert(:user)
273
274 {:ok, user_three} = User.follow(user_three, user_one)
275
276 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
277
278 {:ok, direct_activity} =
279 CommonAPI.post(user_one, %{
280 "status" => "Hi, @#{user_two.nickname}.",
281 "visibility" => "direct"
282 })
283
284 {:ok, private_activity} =
285 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
286
287 resp =
288 conn
289 |> get("/api/v1/accounts/#{user_one.id}/statuses")
290
291 assert [%{"id" => id}] = json_response(resp, 200)
292 assert id == to_string(activity.id)
293
294 resp =
295 conn
296 |> assign(:user, user_two)
297 |> get("/api/v1/accounts/#{user_one.id}/statuses")
298
299 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
300 assert id_one == to_string(direct_activity.id)
301 assert id_two == to_string(activity.id)
302
303 resp =
304 conn
305 |> assign(:user, user_three)
306 |> get("/api/v1/accounts/#{user_one.id}/statuses")
307
308 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
309 assert id_one == to_string(private_activity.id)
310 assert id_two == to_string(activity.id)
311 end
312
313 test "unimplemented pinned statuses feature", %{conn: conn} do
314 note = insert(:note_activity)
315 user = User.get_cached_by_ap_id(note.data["actor"])
316
317 conn =
318 conn
319 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
320
321 assert json_response(conn, 200) == []
322 end
323
324 test "gets an users media", %{conn: conn} do
325 note = insert(:note_activity)
326 user = User.get_cached_by_ap_id(note.data["actor"])
327
328 file = %Plug.Upload{
329 content_type: "image/jpg",
330 path: Path.absname("test/fixtures/image.jpg"),
331 filename: "an_image.jpg"
332 }
333
334 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
335
336 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
337
338 conn =
339 conn
340 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
341
342 assert [%{"id" => id}] = json_response(conn, 200)
343 assert id == to_string(image_post.id)
344
345 conn =
346 build_conn()
347 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
348
349 assert [%{"id" => id}] = json_response(conn, 200)
350 assert id == to_string(image_post.id)
351 end
352
353 test "gets a user's statuses without reblogs", %{conn: conn} do
354 user = insert(:user)
355 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
356 {:ok, _, _} = CommonAPI.repeat(post.id, user)
357
358 conn =
359 conn
360 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
361
362 assert [%{"id" => id}] = json_response(conn, 200)
363 assert id == to_string(post.id)
364
365 conn =
366 conn
367 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
368
369 assert [%{"id" => id}] = json_response(conn, 200)
370 assert id == to_string(post.id)
371 end
372
373 test "filters user's statuses by a hashtag", %{conn: conn} do
374 user = insert(:user)
375 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
376 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
377
378 conn =
379 conn
380 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
381
382 assert [%{"id" => id}] = json_response(conn, 200)
383 assert id == to_string(post.id)
384 end
385 end
386
387 describe "user relationships" do
388 test "returns the relationships for the current user", %{conn: conn} do
389 user = insert(:user)
390 other_user = insert(:user)
391 {:ok, user} = User.follow(user, other_user)
392
393 conn =
394 conn
395 |> assign(:user, user)
396 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
397
398 assert [relationship] = json_response(conn, 200)
399
400 assert to_string(other_user.id) == relationship["id"]
401 end
402 end
403
404 describe "media upload" do
405 setup do
406 user = insert(:user)
407
408 conn =
409 build_conn()
410 |> assign(:user, user)
411
412 image = %Plug.Upload{
413 content_type: "image/jpg",
414 path: Path.absname("test/fixtures/image.jpg"),
415 filename: "an_image.jpg"
416 }
417
418 [conn: conn, image: image]
419 end
420
421 clear_config([:media_proxy])
422 clear_config([Pleroma.Upload])
423
424 test "returns uploaded image", %{conn: conn, image: image} do
425 desc = "Description of the image"
426
427 media =
428 conn
429 |> post("/api/v1/media", %{"file" => image, "description" => desc})
430 |> json_response(:ok)
431
432 assert media["type"] == "image"
433 assert media["description"] == desc
434 assert media["id"]
435
436 object = Repo.get(Object, media["id"])
437 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
438 end
439 end
440
441 describe "locked accounts" do
442 test "verify_credentials", %{conn: conn} do
443 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
444
445 conn =
446 conn
447 |> assign(:user, user)
448 |> get("/api/v1/accounts/verify_credentials")
449
450 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
451 assert id == to_string(user.id)
452 end
453 end
454
455 describe "account fetching" do
456 test "works by id" do
457 user = insert(:user)
458
459 conn =
460 build_conn()
461 |> get("/api/v1/accounts/#{user.id}")
462
463 assert %{"id" => id} = json_response(conn, 200)
464 assert id == to_string(user.id)
465
466 conn =
467 build_conn()
468 |> get("/api/v1/accounts/-1")
469
470 assert %{"error" => "Can't find user"} = json_response(conn, 404)
471 end
472
473 test "works by nickname" do
474 user = insert(:user)
475
476 conn =
477 build_conn()
478 |> get("/api/v1/accounts/#{user.nickname}")
479
480 assert %{"id" => id} = json_response(conn, 200)
481 assert id == user.id
482 end
483
484 test "works by nickname for remote users" do
485 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
486 Pleroma.Config.put([:instance, :limit_to_local_content], false)
487 user = insert(:user, nickname: "user@example.com", local: false)
488
489 conn =
490 build_conn()
491 |> get("/api/v1/accounts/#{user.nickname}")
492
493 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
494 assert %{"id" => id} = json_response(conn, 200)
495 assert id == user.id
496 end
497
498 test "respects limit_to_local_content == :all for remote user nicknames" do
499 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
500 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
501
502 user = insert(:user, nickname: "user@example.com", local: false)
503
504 conn =
505 build_conn()
506 |> get("/api/v1/accounts/#{user.nickname}")
507
508 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
509 assert json_response(conn, 404)
510 end
511
512 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
513 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
514 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
515
516 user = insert(:user, nickname: "user@example.com", local: false)
517 reading_user = insert(:user)
518
519 conn =
520 build_conn()
521 |> get("/api/v1/accounts/#{user.nickname}")
522
523 assert json_response(conn, 404)
524
525 conn =
526 build_conn()
527 |> assign(:user, reading_user)
528 |> get("/api/v1/accounts/#{user.nickname}")
529
530 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
531 assert %{"id" => id} = json_response(conn, 200)
532 assert id == user.id
533 end
534 end
535
536 test "mascot upload", %{conn: conn} do
537 user = insert(:user)
538
539 non_image_file = %Plug.Upload{
540 content_type: "audio/mpeg",
541 path: Path.absname("test/fixtures/sound.mp3"),
542 filename: "sound.mp3"
543 }
544
545 conn =
546 conn
547 |> assign(:user, user)
548 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
549
550 assert json_response(conn, 415)
551
552 file = %Plug.Upload{
553 content_type: "image/jpg",
554 path: Path.absname("test/fixtures/image.jpg"),
555 filename: "an_image.jpg"
556 }
557
558 conn =
559 build_conn()
560 |> assign(:user, user)
561 |> put("/api/v1/pleroma/mascot", %{"file" => file})
562
563 assert %{"id" => _, "type" => image} = json_response(conn, 200)
564 end
565
566 test "mascot retrieving", %{conn: conn} do
567 user = insert(:user)
568 # When user hasn't set a mascot, we should just get pleroma tan back
569 conn =
570 conn
571 |> assign(:user, user)
572 |> get("/api/v1/pleroma/mascot")
573
574 assert %{"url" => url} = json_response(conn, 200)
575 assert url =~ "pleroma-fox-tan-smol"
576
577 # When a user sets their mascot, we should get that back
578 file = %Plug.Upload{
579 content_type: "image/jpg",
580 path: Path.absname("test/fixtures/image.jpg"),
581 filename: "an_image.jpg"
582 }
583
584 conn =
585 build_conn()
586 |> assign(:user, user)
587 |> put("/api/v1/pleroma/mascot", %{"file" => file})
588
589 assert json_response(conn, 200)
590
591 user = User.get_cached_by_id(user.id)
592
593 conn =
594 build_conn()
595 |> assign(:user, user)
596 |> get("/api/v1/pleroma/mascot")
597
598 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
599 assert url =~ "an_image"
600 end
601
602 test "getting followers", %{conn: conn} do
603 user = insert(:user)
604 other_user = insert(:user)
605 {:ok, user} = User.follow(user, other_user)
606
607 conn =
608 conn
609 |> get("/api/v1/accounts/#{other_user.id}/followers")
610
611 assert [%{"id" => id}] = json_response(conn, 200)
612 assert id == to_string(user.id)
613 end
614
615 test "getting followers, hide_followers", %{conn: conn} do
616 user = insert(:user)
617 other_user = insert(:user, %{info: %{hide_followers: true}})
618 {:ok, _user} = User.follow(user, other_user)
619
620 conn =
621 conn
622 |> get("/api/v1/accounts/#{other_user.id}/followers")
623
624 assert [] == json_response(conn, 200)
625 end
626
627 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
628 user = insert(:user)
629 other_user = insert(:user, %{info: %{hide_followers: true}})
630 {:ok, _user} = User.follow(user, other_user)
631
632 conn =
633 conn
634 |> assign(:user, other_user)
635 |> get("/api/v1/accounts/#{other_user.id}/followers")
636
637 refute [] == json_response(conn, 200)
638 end
639
640 test "getting followers, pagination", %{conn: conn} do
641 user = insert(:user)
642 follower1 = insert(:user)
643 follower2 = insert(:user)
644 follower3 = insert(:user)
645 {:ok, _} = User.follow(follower1, user)
646 {:ok, _} = User.follow(follower2, user)
647 {:ok, _} = User.follow(follower3, user)
648
649 conn =
650 conn
651 |> assign(:user, user)
652
653 res_conn =
654 conn
655 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
656
657 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
658 assert id3 == follower3.id
659 assert id2 == follower2.id
660
661 res_conn =
662 conn
663 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
664
665 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
666 assert id2 == follower2.id
667 assert id1 == follower1.id
668
669 res_conn =
670 conn
671 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
672
673 assert [%{"id" => id2}] = json_response(res_conn, 200)
674 assert id2 == follower2.id
675
676 assert [link_header] = get_resp_header(res_conn, "link")
677 assert link_header =~ ~r/min_id=#{follower2.id}/
678 assert link_header =~ ~r/max_id=#{follower2.id}/
679 end
680
681 test "getting following", %{conn: conn} do
682 user = insert(:user)
683 other_user = insert(:user)
684 {:ok, user} = User.follow(user, other_user)
685
686 conn =
687 conn
688 |> get("/api/v1/accounts/#{user.id}/following")
689
690 assert [%{"id" => id}] = json_response(conn, 200)
691 assert id == to_string(other_user.id)
692 end
693
694 test "getting following, hide_follows", %{conn: conn} do
695 user = insert(:user, %{info: %{hide_follows: true}})
696 other_user = insert(:user)
697 {:ok, user} = User.follow(user, other_user)
698
699 conn =
700 conn
701 |> get("/api/v1/accounts/#{user.id}/following")
702
703 assert [] == json_response(conn, 200)
704 end
705
706 test "getting following, hide_follows, same user requesting", %{conn: conn} do
707 user = insert(:user, %{info: %{hide_follows: true}})
708 other_user = insert(:user)
709 {:ok, user} = User.follow(user, other_user)
710
711 conn =
712 conn
713 |> assign(:user, user)
714 |> get("/api/v1/accounts/#{user.id}/following")
715
716 refute [] == json_response(conn, 200)
717 end
718
719 test "getting following, pagination", %{conn: conn} do
720 user = insert(:user)
721 following1 = insert(:user)
722 following2 = insert(:user)
723 following3 = insert(:user)
724 {:ok, _} = User.follow(user, following1)
725 {:ok, _} = User.follow(user, following2)
726 {:ok, _} = User.follow(user, following3)
727
728 conn =
729 conn
730 |> assign(:user, user)
731
732 res_conn =
733 conn
734 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
735
736 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
737 assert id3 == following3.id
738 assert id2 == following2.id
739
740 res_conn =
741 conn
742 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
743
744 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
745 assert id2 == following2.id
746 assert id1 == following1.id
747
748 res_conn =
749 conn
750 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
751
752 assert [%{"id" => id2}] = json_response(res_conn, 200)
753 assert id2 == following2.id
754
755 assert [link_header] = get_resp_header(res_conn, "link")
756 assert link_header =~ ~r/min_id=#{following2.id}/
757 assert link_header =~ ~r/max_id=#{following2.id}/
758 end
759
760 test "following / unfollowing a user", %{conn: conn} do
761 user = insert(:user)
762 other_user = insert(:user)
763
764 conn =
765 conn
766 |> assign(:user, user)
767 |> post("/api/v1/accounts/#{other_user.id}/follow")
768
769 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
770
771 user = User.get_cached_by_id(user.id)
772
773 conn =
774 build_conn()
775 |> assign(:user, user)
776 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
777
778 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
779
780 user = User.get_cached_by_id(user.id)
781
782 conn =
783 build_conn()
784 |> assign(:user, user)
785 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
786
787 assert %{"id" => id} = json_response(conn, 200)
788 assert id == to_string(other_user.id)
789 end
790
791 test "following without reblogs" do
792 follower = insert(:user)
793 followed = insert(:user)
794 other_user = insert(:user)
795
796 conn =
797 build_conn()
798 |> assign(:user, follower)
799 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
800
801 assert %{"showing_reblogs" => false} = json_response(conn, 200)
802
803 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
804 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
805
806 conn =
807 build_conn()
808 |> assign(:user, User.get_cached_by_id(follower.id))
809 |> get("/api/v1/timelines/home")
810
811 assert [] == json_response(conn, 200)
812
813 conn =
814 build_conn()
815 |> assign(:user, follower)
816 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
817
818 assert %{"showing_reblogs" => true} = json_response(conn, 200)
819
820 conn =
821 build_conn()
822 |> assign(:user, User.get_cached_by_id(follower.id))
823 |> get("/api/v1/timelines/home")
824
825 expected_activity_id = reblog.id
826 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
827 end
828
829 test "following / unfollowing errors" do
830 user = insert(:user)
831
832 conn =
833 build_conn()
834 |> assign(:user, user)
835
836 # self follow
837 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
838 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
839
840 # self unfollow
841 user = User.get_cached_by_id(user.id)
842 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
843 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
844
845 # self follow via uri
846 user = User.get_cached_by_id(user.id)
847 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
848 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
849
850 # follow non existing user
851 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
852 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
853
854 # follow non existing user via uri
855 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
856 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
857
858 # unfollow non existing user
859 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
860 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
861 end
862
863 describe "mute/unmute" do
864 test "with notifications", %{conn: conn} do
865 user = insert(:user)
866 other_user = insert(:user)
867
868 conn =
869 conn
870 |> assign(:user, user)
871 |> post("/api/v1/accounts/#{other_user.id}/mute")
872
873 response = json_response(conn, 200)
874
875 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
876 user = User.get_cached_by_id(user.id)
877
878 conn =
879 build_conn()
880 |> assign(:user, user)
881 |> post("/api/v1/accounts/#{other_user.id}/unmute")
882
883 response = json_response(conn, 200)
884 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
885 end
886
887 test "without notifications", %{conn: conn} do
888 user = insert(:user)
889 other_user = insert(:user)
890
891 conn =
892 conn
893 |> assign(:user, user)
894 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
895
896 response = json_response(conn, 200)
897
898 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
899 user = User.get_cached_by_id(user.id)
900
901 conn =
902 build_conn()
903 |> assign(:user, user)
904 |> post("/api/v1/accounts/#{other_user.id}/unmute")
905
906 response = json_response(conn, 200)
907 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
908 end
909 end
910
911 test "subscribing / unsubscribing to a user", %{conn: conn} do
912 user = insert(:user)
913 subscription_target = insert(:user)
914
915 conn =
916 conn
917 |> assign(:user, user)
918 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
919
920 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
921
922 conn =
923 build_conn()
924 |> assign(:user, user)
925 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
926
927 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
928 end
929
930 test "getting a list of mutes", %{conn: conn} do
931 user = insert(:user)
932 other_user = insert(:user)
933
934 {:ok, user} = User.mute(user, other_user)
935
936 conn =
937 conn
938 |> assign(:user, user)
939 |> get("/api/v1/mutes")
940
941 other_user_id = to_string(other_user.id)
942 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
943 end
944
945 test "blocking / unblocking a user", %{conn: conn} do
946 user = insert(:user)
947 other_user = insert(:user)
948
949 conn =
950 conn
951 |> assign(:user, user)
952 |> post("/api/v1/accounts/#{other_user.id}/block")
953
954 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
955
956 user = User.get_cached_by_id(user.id)
957
958 conn =
959 build_conn()
960 |> assign(:user, user)
961 |> post("/api/v1/accounts/#{other_user.id}/unblock")
962
963 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
964 end
965
966 test "getting a list of blocks", %{conn: conn} do
967 user = insert(:user)
968 other_user = insert(:user)
969
970 {:ok, user} = User.block(user, other_user)
971
972 conn =
973 conn
974 |> assign(:user, user)
975 |> get("/api/v1/blocks")
976
977 other_user_id = to_string(other_user.id)
978 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
979 end
980
981 test "unimplemented follow_requests, blocks, domain blocks" do
982 user = insert(:user)
983
984 ["blocks", "domain_blocks", "follow_requests"]
985 |> Enum.each(fn endpoint ->
986 conn =
987 build_conn()
988 |> assign(:user, user)
989 |> get("/api/v1/#{endpoint}")
990
991 assert [] = json_response(conn, 200)
992 end)
993 end
994
995 test "returns the favorites of a user", %{conn: conn} do
996 user = insert(:user)
997 other_user = insert(:user)
998
999 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1000 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1001
1002 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1003
1004 first_conn =
1005 conn
1006 |> assign(:user, user)
1007 |> get("/api/v1/favourites")
1008
1009 assert [status] = json_response(first_conn, 200)
1010 assert status["id"] == to_string(activity.id)
1011
1012 assert [{"link", _link_header}] =
1013 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1014
1015 # Honours query params
1016 {:ok, second_activity} =
1017 CommonAPI.post(other_user, %{
1018 "status" =>
1019 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1020 })
1021
1022 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1023
1024 last_like = status["id"]
1025
1026 second_conn =
1027 conn
1028 |> assign(:user, user)
1029 |> get("/api/v1/favourites?since_id=#{last_like}")
1030
1031 assert [second_status] = json_response(second_conn, 200)
1032 assert second_status["id"] == to_string(second_activity.id)
1033
1034 third_conn =
1035 conn
1036 |> assign(:user, user)
1037 |> get("/api/v1/favourites?limit=0")
1038
1039 assert [] = json_response(third_conn, 200)
1040 end
1041
1042 describe "getting favorites timeline of specified user" do
1043 setup do
1044 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
1045 [current_user: current_user, user: user]
1046 end
1047
1048 test "returns list of statuses favorited by specified user", %{
1049 conn: conn,
1050 current_user: current_user,
1051 user: user
1052 } do
1053 [activity | _] = insert_pair(:note_activity)
1054 CommonAPI.favorite(activity.id, user)
1055
1056 response =
1057 conn
1058 |> assign(:user, current_user)
1059 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1060 |> json_response(:ok)
1061
1062 [like] = response
1063
1064 assert length(response) == 1
1065 assert like["id"] == activity.id
1066 end
1067
1068 test "returns favorites for specified user_id when user is not logged in", %{
1069 conn: conn,
1070 user: user
1071 } do
1072 activity = insert(:note_activity)
1073 CommonAPI.favorite(activity.id, user)
1074
1075 response =
1076 conn
1077 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1078 |> json_response(:ok)
1079
1080 assert length(response) == 1
1081 end
1082
1083 test "returns favorited DM only when user is logged in and he is one of recipients", %{
1084 conn: conn,
1085 current_user: current_user,
1086 user: user
1087 } do
1088 {:ok, direct} =
1089 CommonAPI.post(current_user, %{
1090 "status" => "Hi @#{user.nickname}!",
1091 "visibility" => "direct"
1092 })
1093
1094 CommonAPI.favorite(direct.id, user)
1095
1096 response =
1097 conn
1098 |> assign(:user, current_user)
1099 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1100 |> json_response(:ok)
1101
1102 assert length(response) == 1
1103
1104 anonymous_response =
1105 conn
1106 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1107 |> json_response(:ok)
1108
1109 assert Enum.empty?(anonymous_response)
1110 end
1111
1112 test "does not return others' favorited DM when user is not one of recipients", %{
1113 conn: conn,
1114 current_user: current_user,
1115 user: user
1116 } do
1117 user_two = insert(:user)
1118
1119 {:ok, direct} =
1120 CommonAPI.post(user_two, %{
1121 "status" => "Hi @#{user.nickname}!",
1122 "visibility" => "direct"
1123 })
1124
1125 CommonAPI.favorite(direct.id, user)
1126
1127 response =
1128 conn
1129 |> assign(:user, current_user)
1130 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1131 |> json_response(:ok)
1132
1133 assert Enum.empty?(response)
1134 end
1135
1136 test "paginates favorites using since_id and max_id", %{
1137 conn: conn,
1138 current_user: current_user,
1139 user: user
1140 } do
1141 activities = insert_list(10, :note_activity)
1142
1143 Enum.each(activities, fn activity ->
1144 CommonAPI.favorite(activity.id, user)
1145 end)
1146
1147 third_activity = Enum.at(activities, 2)
1148 seventh_activity = Enum.at(activities, 6)
1149
1150 response =
1151 conn
1152 |> assign(:user, current_user)
1153 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
1154 since_id: third_activity.id,
1155 max_id: seventh_activity.id
1156 })
1157 |> json_response(:ok)
1158
1159 assert length(response) == 3
1160 refute third_activity in response
1161 refute seventh_activity in response
1162 end
1163
1164 test "limits favorites using limit parameter", %{
1165 conn: conn,
1166 current_user: current_user,
1167 user: user
1168 } do
1169 7
1170 |> insert_list(:note_activity)
1171 |> Enum.each(fn activity ->
1172 CommonAPI.favorite(activity.id, user)
1173 end)
1174
1175 response =
1176 conn
1177 |> assign(:user, current_user)
1178 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
1179 |> json_response(:ok)
1180
1181 assert length(response) == 3
1182 end
1183
1184 test "returns empty response when user does not have any favorited statuses", %{
1185 conn: conn,
1186 current_user: current_user,
1187 user: user
1188 } do
1189 response =
1190 conn
1191 |> assign(:user, current_user)
1192 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1193 |> json_response(:ok)
1194
1195 assert Enum.empty?(response)
1196 end
1197
1198 test "returns 404 error when specified user is not exist", %{conn: conn} do
1199 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
1200
1201 assert json_response(conn, 404) == %{"error" => "Record not found"}
1202 end
1203
1204 test "returns 403 error when user has hidden own favorites", %{
1205 conn: conn,
1206 current_user: current_user
1207 } do
1208 user = insert(:user, %{info: %{hide_favorites: true}})
1209 activity = insert(:note_activity)
1210 CommonAPI.favorite(activity.id, user)
1211
1212 conn =
1213 conn
1214 |> assign(:user, current_user)
1215 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1216
1217 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1218 end
1219
1220 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
1221 user = insert(:user)
1222 activity = insert(:note_activity)
1223 CommonAPI.favorite(activity.id, user)
1224
1225 conn =
1226 conn
1227 |> assign(:user, current_user)
1228 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1229
1230 assert user.info.hide_favorites
1231 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1232 end
1233 end
1234
1235 test "get instance information", %{conn: conn} do
1236 conn = get(conn, "/api/v1/instance")
1237 assert result = json_response(conn, 200)
1238
1239 email = Config.get([:instance, :email])
1240 # Note: not checking for "max_toot_chars" since it's optional
1241 assert %{
1242 "uri" => _,
1243 "title" => _,
1244 "description" => _,
1245 "version" => _,
1246 "email" => from_config_email,
1247 "urls" => %{
1248 "streaming_api" => _
1249 },
1250 "stats" => _,
1251 "thumbnail" => _,
1252 "languages" => _,
1253 "registrations" => _,
1254 "poll_limits" => _
1255 } = result
1256
1257 assert email == from_config_email
1258 end
1259
1260 test "get instance stats", %{conn: conn} do
1261 user = insert(:user, %{local: true})
1262
1263 user2 = insert(:user, %{local: true})
1264 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1265
1266 insert(:user, %{local: false, nickname: "u@peer1.com"})
1267 insert(:user, %{local: false, nickname: "u@peer2.com"})
1268
1269 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
1270
1271 # Stats should count users with missing or nil `info.deactivated` value
1272
1273 {:ok, _user} =
1274 user.id
1275 |> User.get_cached_by_id()
1276 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
1277
1278 Pleroma.Stats.force_update()
1279
1280 conn = get(conn, "/api/v1/instance")
1281
1282 assert result = json_response(conn, 200)
1283
1284 stats = result["stats"]
1285
1286 assert stats
1287 assert stats["user_count"] == 1
1288 assert stats["status_count"] == 1
1289 assert stats["domain_count"] == 2
1290 end
1291
1292 test "get peers", %{conn: conn} do
1293 insert(:user, %{local: false, nickname: "u@peer1.com"})
1294 insert(:user, %{local: false, nickname: "u@peer2.com"})
1295
1296 Pleroma.Stats.force_update()
1297
1298 conn = get(conn, "/api/v1/instance/peers")
1299
1300 assert result = json_response(conn, 200)
1301
1302 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
1303 end
1304
1305 test "put settings", %{conn: conn} do
1306 user = insert(:user)
1307
1308 conn =
1309 conn
1310 |> assign(:user, user)
1311 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
1312
1313 assert _result = json_response(conn, 200)
1314
1315 user = User.get_cached_by_ap_id(user.ap_id)
1316 assert user.info.settings == %{"programming" => "socks"}
1317 end
1318
1319 describe "pinned statuses" do
1320 setup do
1321 user = insert(:user)
1322 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
1323
1324 [user: user, activity: activity]
1325 end
1326
1327 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
1328 {:ok, _} = CommonAPI.pin(activity.id, user)
1329
1330 result =
1331 conn
1332 |> assign(:user, user)
1333 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1334 |> json_response(200)
1335
1336 id_str = to_string(activity.id)
1337
1338 assert [%{"id" => ^id_str, "pinned" => true}] = result
1339 end
1340 end
1341
1342 describe "reports" do
1343 setup do
1344 reporter = insert(:user)
1345 target_user = insert(:user)
1346
1347 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
1348
1349 [reporter: reporter, target_user: target_user, activity: activity]
1350 end
1351
1352 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
1353 assert %{"action_taken" => false, "id" => _} =
1354 conn
1355 |> assign(:user, reporter)
1356 |> post("/api/v1/reports", %{"account_id" => target_user.id})
1357 |> json_response(200)
1358 end
1359
1360 test "submit a report with statuses and comment", %{
1361 conn: conn,
1362 reporter: reporter,
1363 target_user: target_user,
1364 activity: activity
1365 } do
1366 assert %{"action_taken" => false, "id" => _} =
1367 conn
1368 |> assign(:user, reporter)
1369 |> post("/api/v1/reports", %{
1370 "account_id" => target_user.id,
1371 "status_ids" => [activity.id],
1372 "comment" => "bad status!",
1373 "forward" => "false"
1374 })
1375 |> json_response(200)
1376 end
1377
1378 test "account_id is required", %{
1379 conn: conn,
1380 reporter: reporter,
1381 activity: activity
1382 } do
1383 assert %{"error" => "Valid `account_id` required"} =
1384 conn
1385 |> assign(:user, reporter)
1386 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
1387 |> json_response(400)
1388 end
1389
1390 test "comment must be up to the size specified in the config", %{
1391 conn: conn,
1392 reporter: reporter,
1393 target_user: target_user
1394 } do
1395 max_size = Config.get([:instance, :max_report_comment_size], 1000)
1396 comment = String.pad_trailing("a", max_size + 1, "a")
1397
1398 error = %{"error" => "Comment must be up to #{max_size} characters"}
1399
1400 assert ^error =
1401 conn
1402 |> assign(:user, reporter)
1403 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
1404 |> json_response(400)
1405 end
1406
1407 test "returns error when account is not exist", %{
1408 conn: conn,
1409 reporter: reporter,
1410 activity: activity
1411 } do
1412 conn =
1413 conn
1414 |> assign(:user, reporter)
1415 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
1416
1417 assert json_response(conn, 400) == %{"error" => "Account not found"}
1418 end
1419 end
1420
1421 describe "link headers" do
1422 test "preserves parameters in link headers", %{conn: conn} do
1423 user = insert(:user)
1424 other_user = insert(:user)
1425
1426 {:ok, activity1} =
1427 CommonAPI.post(other_user, %{
1428 "status" => "hi @#{user.nickname}",
1429 "visibility" => "public"
1430 })
1431
1432 {:ok, activity2} =
1433 CommonAPI.post(other_user, %{
1434 "status" => "hi @#{user.nickname}",
1435 "visibility" => "public"
1436 })
1437
1438 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
1439 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
1440
1441 conn =
1442 conn
1443 |> assign(:user, user)
1444 |> get("/api/v1/notifications", %{media_only: true})
1445
1446 assert [link_header] = get_resp_header(conn, "link")
1447 assert link_header =~ ~r/media_only=true/
1448 assert link_header =~ ~r/min_id=#{notification2.id}/
1449 assert link_header =~ ~r/max_id=#{notification1.id}/
1450 end
1451 end
1452
1453 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
1454 # Need to set an old-style integer ID to reproduce the problem
1455 # (these are no longer assigned to new accounts but were preserved
1456 # for existing accounts during the migration to flakeIDs)
1457 user_one = insert(:user, %{id: 1212})
1458 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
1459
1460 resp_one =
1461 conn
1462 |> get("/api/v1/accounts/#{user_one.id}")
1463
1464 resp_two =
1465 conn
1466 |> get("/api/v1/accounts/#{user_two.nickname}")
1467
1468 resp_three =
1469 conn
1470 |> get("/api/v1/accounts/#{user_two.id}")
1471
1472 acc_one = json_response(resp_one, 200)
1473 acc_two = json_response(resp_two, 200)
1474 acc_three = json_response(resp_three, 200)
1475 refute acc_one == acc_two
1476 assert acc_two == acc_three
1477 end
1478
1479 describe "custom emoji" do
1480 test "with tags", %{conn: conn} do
1481 [emoji | _body] =
1482 conn
1483 |> get("/api/v1/custom_emojis")
1484 |> json_response(200)
1485
1486 assert Map.has_key?(emoji, "shortcode")
1487 assert Map.has_key?(emoji, "static_url")
1488 assert Map.has_key?(emoji, "tags")
1489 assert is_list(emoji["tags"])
1490 assert Map.has_key?(emoji, "category")
1491 assert Map.has_key?(emoji, "url")
1492 assert Map.has_key?(emoji, "visible_in_picker")
1493 end
1494 end
1495
1496 describe "index/2 redirections" do
1497 setup %{conn: conn} do
1498 session_opts = [
1499 store: :cookie,
1500 key: "_test",
1501 signing_salt: "cooldude"
1502 ]
1503
1504 conn =
1505 conn
1506 |> Plug.Session.call(Plug.Session.init(session_opts))
1507 |> fetch_session()
1508
1509 test_path = "/web/statuses/test"
1510 %{conn: conn, path: test_path}
1511 end
1512
1513 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
1514 conn = get(conn, path)
1515
1516 assert conn.status == 302
1517 assert redirected_to(conn) == "/web/login"
1518 end
1519
1520 test "redirects not logged-in users to the login page on private instances", %{
1521 conn: conn,
1522 path: path
1523 } do
1524 Config.put([:instance, :public], false)
1525
1526 conn = get(conn, path)
1527
1528 assert conn.status == 302
1529 assert redirected_to(conn) == "/web/login"
1530 end
1531
1532 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
1533 token = insert(:oauth_token)
1534
1535 conn =
1536 conn
1537 |> assign(:user, token.user)
1538 |> put_session(:oauth_token, token.token)
1539 |> get(path)
1540
1541 assert conn.status == 200
1542 end
1543
1544 test "saves referer path to session", %{conn: conn, path: path} do
1545 conn = get(conn, path)
1546 return_to = Plug.Conn.get_session(conn, :return_to)
1547
1548 assert return_to == path
1549 end
1550
1551 test "redirects to the saved path after log in", %{conn: conn, path: path} do
1552 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1553 auth = insert(:oauth_authorization, app: app)
1554
1555 conn =
1556 conn
1557 |> put_session(:return_to, path)
1558 |> get("/web/login", %{code: auth.token})
1559
1560 assert conn.status == 302
1561 assert redirected_to(conn) == path
1562 end
1563
1564 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
1565 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1566 auth = insert(:oauth_authorization, app: app)
1567
1568 conn = get(conn, "/web/login", %{code: auth.token})
1569
1570 assert conn.status == 302
1571 assert redirected_to(conn) == "/web/getting-started"
1572 end
1573 end
1574
1575 describe "create account by app" do
1576 test "Account registration via Application", %{conn: conn} do
1577 conn =
1578 conn
1579 |> post("/api/v1/apps", %{
1580 client_name: "client_name",
1581 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1582 scopes: "read, write, follow"
1583 })
1584
1585 %{
1586 "client_id" => client_id,
1587 "client_secret" => client_secret,
1588 "id" => _,
1589 "name" => "client_name",
1590 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1591 "vapid_key" => _,
1592 "website" => nil
1593 } = json_response(conn, 200)
1594
1595 conn =
1596 conn
1597 |> post("/oauth/token", %{
1598 grant_type: "client_credentials",
1599 client_id: client_id,
1600 client_secret: client_secret
1601 })
1602
1603 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1604 json_response(conn, 200)
1605
1606 assert token
1607 token_from_db = Repo.get_by(Token, token: token)
1608 assert token_from_db
1609 assert refresh
1610 assert scope == "read write follow"
1611
1612 conn =
1613 build_conn()
1614 |> put_req_header("authorization", "Bearer " <> token)
1615 |> post("/api/v1/accounts", %{
1616 username: "lain",
1617 email: "lain@example.org",
1618 password: "PlzDontHackLain",
1619 agreement: true
1620 })
1621
1622 %{
1623 "access_token" => token,
1624 "created_at" => _created_at,
1625 "scope" => _scope,
1626 "token_type" => "Bearer"
1627 } = json_response(conn, 200)
1628
1629 token_from_db = Repo.get_by(Token, token: token)
1630 assert token_from_db
1631 token_from_db = Repo.preload(token_from_db, :user)
1632 assert token_from_db.user
1633
1634 assert token_from_db.user.info.confirmation_pending
1635 end
1636
1637 test "rate limit", %{conn: conn} do
1638 app_token = insert(:oauth_token, user: nil)
1639
1640 conn =
1641 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1642 |> Map.put(:remote_ip, {15, 15, 15, 15})
1643
1644 for i <- 1..5 do
1645 conn =
1646 conn
1647 |> post("/api/v1/accounts", %{
1648 username: "#{i}lain",
1649 email: "#{i}lain@example.org",
1650 password: "PlzDontHackLain",
1651 agreement: true
1652 })
1653
1654 %{
1655 "access_token" => token,
1656 "created_at" => _created_at,
1657 "scope" => _scope,
1658 "token_type" => "Bearer"
1659 } = json_response(conn, 200)
1660
1661 token_from_db = Repo.get_by(Token, token: token)
1662 assert token_from_db
1663 token_from_db = Repo.preload(token_from_db, :user)
1664 assert token_from_db.user
1665
1666 assert token_from_db.user.info.confirmation_pending
1667 end
1668
1669 conn =
1670 conn
1671 |> post("/api/v1/accounts", %{
1672 username: "6lain",
1673 email: "6lain@example.org",
1674 password: "PlzDontHackLain",
1675 agreement: true
1676 })
1677
1678 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
1679 end
1680 end
1681
1682 describe "GET /api/v1/polls/:id" do
1683 test "returns poll entity for object id", %{conn: conn} do
1684 user = insert(:user)
1685
1686 {:ok, activity} =
1687 CommonAPI.post(user, %{
1688 "status" => "Pleroma does",
1689 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
1690 })
1691
1692 object = Object.normalize(activity)
1693
1694 conn =
1695 conn
1696 |> assign(:user, user)
1697 |> get("/api/v1/polls/#{object.id}")
1698
1699 response = json_response(conn, 200)
1700 id = to_string(object.id)
1701 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
1702 end
1703
1704 test "does not expose polls for private statuses", %{conn: conn} do
1705 user = insert(:user)
1706 other_user = insert(:user)
1707
1708 {:ok, activity} =
1709 CommonAPI.post(user, %{
1710 "status" => "Pleroma does",
1711 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
1712 "visibility" => "private"
1713 })
1714
1715 object = Object.normalize(activity)
1716
1717 conn =
1718 conn
1719 |> assign(:user, other_user)
1720 |> get("/api/v1/polls/#{object.id}")
1721
1722 assert json_response(conn, 404)
1723 end
1724 end
1725
1726 describe "POST /api/v1/polls/:id/votes" do
1727 test "votes are added to the poll", %{conn: conn} do
1728 user = insert(:user)
1729 other_user = insert(:user)
1730
1731 {:ok, activity} =
1732 CommonAPI.post(user, %{
1733 "status" => "A very delicious sandwich",
1734 "poll" => %{
1735 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
1736 "expires_in" => 20,
1737 "multiple" => true
1738 }
1739 })
1740
1741 object = Object.normalize(activity)
1742
1743 conn =
1744 conn
1745 |> assign(:user, other_user)
1746 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
1747
1748 assert json_response(conn, 200)
1749 object = Object.get_by_id(object.id)
1750
1751 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
1752 total_items == 1
1753 end)
1754 end
1755
1756 test "author can't vote", %{conn: conn} do
1757 user = insert(:user)
1758
1759 {:ok, activity} =
1760 CommonAPI.post(user, %{
1761 "status" => "Am I cute?",
1762 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
1763 })
1764
1765 object = Object.normalize(activity)
1766
1767 assert conn
1768 |> assign(:user, user)
1769 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
1770 |> json_response(422) == %{"error" => "Poll's author can't vote"}
1771
1772 object = Object.get_by_id(object.id)
1773
1774 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
1775 end
1776
1777 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
1778 user = insert(:user)
1779 other_user = insert(:user)
1780
1781 {:ok, activity} =
1782 CommonAPI.post(user, %{
1783 "status" => "The glass is",
1784 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
1785 })
1786
1787 object = Object.normalize(activity)
1788
1789 assert conn
1790 |> assign(:user, other_user)
1791 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
1792 |> json_response(422) == %{"error" => "Too many choices"}
1793
1794 object = Object.get_by_id(object.id)
1795
1796 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
1797 total_items == 1
1798 end)
1799 end
1800
1801 test "does not allow choice index to be greater than options count", %{conn: conn} do
1802 user = insert(:user)
1803 other_user = insert(:user)
1804
1805 {:ok, activity} =
1806 CommonAPI.post(user, %{
1807 "status" => "Am I cute?",
1808 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
1809 })
1810
1811 object = Object.normalize(activity)
1812
1813 conn =
1814 conn
1815 |> assign(:user, other_user)
1816 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
1817
1818 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
1819 end
1820
1821 test "returns 404 error when object is not exist", %{conn: conn} do
1822 user = insert(:user)
1823
1824 conn =
1825 conn
1826 |> assign(:user, user)
1827 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
1828
1829 assert json_response(conn, 404) == %{"error" => "Record not found"}
1830 end
1831
1832 test "returns 404 when poll is private and not available for user", %{conn: conn} do
1833 user = insert(:user)
1834 other_user = insert(:user)
1835
1836 {:ok, activity} =
1837 CommonAPI.post(user, %{
1838 "status" => "Am I cute?",
1839 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
1840 "visibility" => "private"
1841 })
1842
1843 object = Object.normalize(activity)
1844
1845 conn =
1846 conn
1847 |> assign(:user, other_user)
1848 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
1849
1850 assert json_response(conn, 404) == %{"error" => "Record not found"}
1851 end
1852 end
1853
1854 describe "POST /auth/password, with valid parameters" do
1855 setup %{conn: conn} do
1856 user = insert(:user)
1857 conn = post(conn, "/auth/password?email=#{user.email}")
1858 %{conn: conn, user: user}
1859 end
1860
1861 test "it returns 204", %{conn: conn} do
1862 assert json_response(conn, :no_content)
1863 end
1864
1865 test "it creates a PasswordResetToken record for user", %{user: user} do
1866 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
1867 assert token_record
1868 end
1869
1870 test "it sends an email to user", %{user: user} do
1871 ObanHelpers.perform_all()
1872 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
1873
1874 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
1875 notify_email = Config.get([:instance, :notify_email])
1876 instance_name = Config.get([:instance, :name])
1877
1878 assert_email_sent(
1879 from: {instance_name, notify_email},
1880 to: {user.name, user.email},
1881 html_body: email.html_body
1882 )
1883 end
1884 end
1885
1886 describe "POST /auth/password, with invalid parameters" do
1887 setup do
1888 user = insert(:user)
1889 {:ok, user: user}
1890 end
1891
1892 test "it returns 404 when user is not found", %{conn: conn, user: user} do
1893 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
1894 assert conn.status == 404
1895 assert conn.resp_body == ""
1896 end
1897
1898 test "it returns 400 when user is not local", %{conn: conn, user: user} do
1899 {:ok, user} = Repo.update(Changeset.change(user, local: false))
1900 conn = post(conn, "/auth/password?email=#{user.email}")
1901 assert conn.status == 400
1902 assert conn.resp_body == ""
1903 end
1904 end
1905
1906 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
1907 setup do
1908 {:ok, user} =
1909 insert(:user)
1910 |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
1911 |> Repo.update()
1912
1913 assert user.info.confirmation_pending
1914
1915 [user: user]
1916 end
1917
1918 clear_config([:instance, :account_activation_required]) do
1919 Config.put([:instance, :account_activation_required], true)
1920 end
1921
1922 test "resend account confirmation email", %{conn: conn, user: user} do
1923 conn
1924 |> assign(:user, user)
1925 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
1926 |> json_response(:no_content)
1927
1928 ObanHelpers.perform_all()
1929
1930 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
1931 notify_email = Config.get([:instance, :notify_email])
1932 instance_name = Config.get([:instance, :name])
1933
1934 assert_email_sent(
1935 from: {instance_name, notify_email},
1936 to: {user.name, user.email},
1937 html_body: email.html_body
1938 )
1939 end
1940 end
1941
1942 describe "GET /api/v1/suggestions" do
1943 setup do
1944 user = insert(:user)
1945 other_user = insert(:user)
1946 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
1947 url500 = "http://test500?#{host}&#{user.nickname}"
1948 url200 = "http://test200?#{host}&#{user.nickname}"
1949
1950 mock(fn
1951 %{method: :get, url: ^url500} ->
1952 %Tesla.Env{status: 500, body: "bad request"}
1953
1954 %{method: :get, url: ^url200} ->
1955 %Tesla.Env{
1956 status: 200,
1957 body:
1958 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
1959 other_user.ap_id
1960 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
1961 }
1962 end)
1963
1964 [user: user, other_user: other_user]
1965 end
1966
1967 clear_config(:suggestions)
1968
1969 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
1970 Config.put([:suggestions, :enabled], false)
1971
1972 res =
1973 conn
1974 |> assign(:user, user)
1975 |> get("/api/v1/suggestions")
1976 |> json_response(200)
1977
1978 assert res == []
1979 end
1980
1981 test "returns error", %{conn: conn, user: user} do
1982 Config.put([:suggestions, :enabled], true)
1983 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
1984
1985 assert capture_log(fn ->
1986 res =
1987 conn
1988 |> assign(:user, user)
1989 |> get("/api/v1/suggestions")
1990 |> json_response(500)
1991
1992 assert res == "Something went wrong"
1993 end) =~ "Could not retrieve suggestions"
1994 end
1995
1996 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
1997 Config.put([:suggestions, :enabled], true)
1998 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
1999
2000 res =
2001 conn
2002 |> assign(:user, user)
2003 |> get("/api/v1/suggestions")
2004 |> json_response(200)
2005
2006 assert res == [
2007 %{
2008 "acct" => "yj455",
2009 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
2010 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
2011 "id" => 0
2012 },
2013 %{
2014 "acct" => other_user.ap_id,
2015 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
2016 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
2017 "id" => other_user.id
2018 }
2019 ]
2020 end
2021 end
2022 end