Merge branch 'tests/mastodon_api_controller.ex' 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
403 test "returns an empty list on a bad request", %{conn: conn} do
404 user = insert(:user)
405
406 conn =
407 conn
408 |> assign(:user, user)
409 |> get("/api/v1/accounts/relationships", %{})
410
411 assert [] = json_response(conn, 200)
412 end
413 end
414
415 describe "media upload" do
416 setup do
417 user = insert(:user)
418
419 conn =
420 build_conn()
421 |> assign(:user, user)
422
423 image = %Plug.Upload{
424 content_type: "image/jpg",
425 path: Path.absname("test/fixtures/image.jpg"),
426 filename: "an_image.jpg"
427 }
428
429 [conn: conn, image: image]
430 end
431
432 clear_config([:media_proxy])
433 clear_config([Pleroma.Upload])
434
435 test "returns uploaded image", %{conn: conn, image: image} do
436 desc = "Description of the image"
437
438 media =
439 conn
440 |> post("/api/v1/media", %{"file" => image, "description" => desc})
441 |> json_response(:ok)
442
443 assert media["type"] == "image"
444 assert media["description"] == desc
445 assert media["id"]
446
447 object = Repo.get(Object, media["id"])
448 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
449 end
450 end
451
452 describe "locked accounts" do
453 test "verify_credentials", %{conn: conn} do
454 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
455
456 conn =
457 conn
458 |> assign(:user, user)
459 |> get("/api/v1/accounts/verify_credentials")
460
461 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
462 assert id == to_string(user.id)
463 end
464 end
465
466 describe "account fetching" do
467 test "works by id" do
468 user = insert(:user)
469
470 conn =
471 build_conn()
472 |> get("/api/v1/accounts/#{user.id}")
473
474 assert %{"id" => id} = json_response(conn, 200)
475 assert id == to_string(user.id)
476
477 conn =
478 build_conn()
479 |> get("/api/v1/accounts/-1")
480
481 assert %{"error" => "Can't find user"} = json_response(conn, 404)
482 end
483
484 test "works by nickname" do
485 user = insert(:user)
486
487 conn =
488 build_conn()
489 |> get("/api/v1/accounts/#{user.nickname}")
490
491 assert %{"id" => id} = json_response(conn, 200)
492 assert id == user.id
493 end
494
495 test "works by nickname for remote users" do
496 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
497 Pleroma.Config.put([:instance, :limit_to_local_content], false)
498 user = insert(:user, nickname: "user@example.com", local: false)
499
500 conn =
501 build_conn()
502 |> get("/api/v1/accounts/#{user.nickname}")
503
504 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
505 assert %{"id" => id} = json_response(conn, 200)
506 assert id == user.id
507 end
508
509 test "respects limit_to_local_content == :all for remote user nicknames" do
510 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
511 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
512
513 user = insert(:user, nickname: "user@example.com", local: false)
514
515 conn =
516 build_conn()
517 |> get("/api/v1/accounts/#{user.nickname}")
518
519 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
520 assert json_response(conn, 404)
521 end
522
523 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
524 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
525 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
526
527 user = insert(:user, nickname: "user@example.com", local: false)
528 reading_user = insert(:user)
529
530 conn =
531 build_conn()
532 |> get("/api/v1/accounts/#{user.nickname}")
533
534 assert json_response(conn, 404)
535
536 conn =
537 build_conn()
538 |> assign(:user, reading_user)
539 |> get("/api/v1/accounts/#{user.nickname}")
540
541 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
542 assert %{"id" => id} = json_response(conn, 200)
543 assert id == user.id
544 end
545 end
546
547 describe "/api/v1/pleroma/mascot" do
548 test "mascot upload", %{conn: conn} do
549 user = insert(:user)
550
551 non_image_file = %Plug.Upload{
552 content_type: "audio/mpeg",
553 path: Path.absname("test/fixtures/sound.mp3"),
554 filename: "sound.mp3"
555 }
556
557 conn =
558 conn
559 |> assign(:user, user)
560 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
561
562 assert json_response(conn, 415)
563
564 file = %Plug.Upload{
565 content_type: "image/jpg",
566 path: Path.absname("test/fixtures/image.jpg"),
567 filename: "an_image.jpg"
568 }
569
570 conn =
571 build_conn()
572 |> assign(:user, user)
573 |> put("/api/v1/pleroma/mascot", %{"file" => file})
574
575 assert %{"id" => _, "type" => image} = json_response(conn, 200)
576 end
577
578 test "mascot retrieving", %{conn: conn} do
579 user = insert(:user)
580 # When user hasn't set a mascot, we should just get pleroma tan back
581 conn =
582 conn
583 |> assign(:user, user)
584 |> get("/api/v1/pleroma/mascot")
585
586 assert %{"url" => url} = json_response(conn, 200)
587 assert url =~ "pleroma-fox-tan-smol"
588
589 # When a user sets their mascot, we should get that back
590 file = %Plug.Upload{
591 content_type: "image/jpg",
592 path: Path.absname("test/fixtures/image.jpg"),
593 filename: "an_image.jpg"
594 }
595
596 conn =
597 build_conn()
598 |> assign(:user, user)
599 |> put("/api/v1/pleroma/mascot", %{"file" => file})
600
601 assert json_response(conn, 200)
602
603 user = User.get_cached_by_id(user.id)
604
605 conn =
606 build_conn()
607 |> assign(:user, user)
608 |> get("/api/v1/pleroma/mascot")
609
610 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
611 assert url =~ "an_image"
612 end
613 end
614
615 test "getting followers", %{conn: conn} do
616 user = insert(:user)
617 other_user = insert(:user)
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 [%{"id" => id}] = json_response(conn, 200)
625 assert id == to_string(user.id)
626 end
627
628 test "getting followers, hide_followers", %{conn: conn} do
629 user = insert(:user)
630 other_user = insert(:user, %{info: %{hide_followers: true}})
631 {:ok, _user} = User.follow(user, other_user)
632
633 conn =
634 conn
635 |> get("/api/v1/accounts/#{other_user.id}/followers")
636
637 assert [] == json_response(conn, 200)
638 end
639
640 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
641 user = insert(:user)
642 other_user = insert(:user, %{info: %{hide_followers: true}})
643 {:ok, _user} = User.follow(user, other_user)
644
645 conn =
646 conn
647 |> assign(:user, other_user)
648 |> get("/api/v1/accounts/#{other_user.id}/followers")
649
650 refute [] == json_response(conn, 200)
651 end
652
653 test "getting followers, pagination", %{conn: conn} do
654 user = insert(:user)
655 follower1 = insert(:user)
656 follower2 = insert(:user)
657 follower3 = insert(:user)
658 {:ok, _} = User.follow(follower1, user)
659 {:ok, _} = User.follow(follower2, user)
660 {:ok, _} = User.follow(follower3, user)
661
662 conn =
663 conn
664 |> assign(:user, user)
665
666 res_conn =
667 conn
668 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
669
670 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
671 assert id3 == follower3.id
672 assert id2 == follower2.id
673
674 res_conn =
675 conn
676 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
677
678 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
679 assert id2 == follower2.id
680 assert id1 == follower1.id
681
682 res_conn =
683 conn
684 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
685
686 assert [%{"id" => id2}] = json_response(res_conn, 200)
687 assert id2 == follower2.id
688
689 assert [link_header] = get_resp_header(res_conn, "link")
690 assert link_header =~ ~r/min_id=#{follower2.id}/
691 assert link_header =~ ~r/max_id=#{follower2.id}/
692 end
693
694 test "getting following", %{conn: conn} do
695 user = insert(:user)
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 [%{"id" => id}] = json_response(conn, 200)
704 assert id == to_string(other_user.id)
705 end
706
707 test "getting following, hide_follows", %{conn: conn} do
708 user = insert(:user, %{info: %{hide_follows: true}})
709 other_user = insert(:user)
710 {:ok, user} = User.follow(user, other_user)
711
712 conn =
713 conn
714 |> get("/api/v1/accounts/#{user.id}/following")
715
716 assert [] == json_response(conn, 200)
717 end
718
719 test "getting following, hide_follows, same user requesting", %{conn: conn} do
720 user = insert(:user, %{info: %{hide_follows: true}})
721 other_user = insert(:user)
722 {:ok, user} = User.follow(user, other_user)
723
724 conn =
725 conn
726 |> assign(:user, user)
727 |> get("/api/v1/accounts/#{user.id}/following")
728
729 refute [] == json_response(conn, 200)
730 end
731
732 test "getting following, pagination", %{conn: conn} do
733 user = insert(:user)
734 following1 = insert(:user)
735 following2 = insert(:user)
736 following3 = insert(:user)
737 {:ok, _} = User.follow(user, following1)
738 {:ok, _} = User.follow(user, following2)
739 {:ok, _} = User.follow(user, following3)
740
741 conn =
742 conn
743 |> assign(:user, user)
744
745 res_conn =
746 conn
747 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
748
749 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
750 assert id3 == following3.id
751 assert id2 == following2.id
752
753 res_conn =
754 conn
755 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
756
757 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
758 assert id2 == following2.id
759 assert id1 == following1.id
760
761 res_conn =
762 conn
763 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
764
765 assert [%{"id" => id2}] = json_response(res_conn, 200)
766 assert id2 == following2.id
767
768 assert [link_header] = get_resp_header(res_conn, "link")
769 assert link_header =~ ~r/min_id=#{following2.id}/
770 assert link_header =~ ~r/max_id=#{following2.id}/
771 end
772
773 test "following / unfollowing a user", %{conn: conn} do
774 user = insert(:user)
775 other_user = insert(:user)
776
777 conn =
778 conn
779 |> assign(:user, user)
780 |> post("/api/v1/accounts/#{other_user.id}/follow")
781
782 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
783
784 user = User.get_cached_by_id(user.id)
785
786 conn =
787 build_conn()
788 |> assign(:user, user)
789 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
790
791 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
792
793 user = User.get_cached_by_id(user.id)
794
795 conn =
796 build_conn()
797 |> assign(:user, user)
798 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
799
800 assert %{"id" => id} = json_response(conn, 200)
801 assert id == to_string(other_user.id)
802 end
803
804 test "following without reblogs" do
805 follower = insert(:user)
806 followed = insert(:user)
807 other_user = insert(:user)
808
809 conn =
810 build_conn()
811 |> assign(:user, follower)
812 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
813
814 assert %{"showing_reblogs" => false} = json_response(conn, 200)
815
816 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
817 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
818
819 conn =
820 build_conn()
821 |> assign(:user, User.get_cached_by_id(follower.id))
822 |> get("/api/v1/timelines/home")
823
824 assert [] == json_response(conn, 200)
825
826 conn =
827 build_conn()
828 |> assign(:user, follower)
829 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
830
831 assert %{"showing_reblogs" => true} = json_response(conn, 200)
832
833 conn =
834 build_conn()
835 |> assign(:user, User.get_cached_by_id(follower.id))
836 |> get("/api/v1/timelines/home")
837
838 expected_activity_id = reblog.id
839 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
840 end
841
842 test "following / unfollowing errors" do
843 user = insert(:user)
844
845 conn =
846 build_conn()
847 |> assign(:user, user)
848
849 # self follow
850 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
851 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
852
853 # self unfollow
854 user = User.get_cached_by_id(user.id)
855 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
856 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
857
858 # self follow via uri
859 user = User.get_cached_by_id(user.id)
860 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
861 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
862
863 # follow non existing user
864 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
865 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
866
867 # follow non existing user via uri
868 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
869 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
870
871 # unfollow non existing user
872 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
873 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
874 end
875
876 describe "mute/unmute" do
877 test "with notifications", %{conn: conn} do
878 user = insert(:user)
879 other_user = insert(:user)
880
881 conn =
882 conn
883 |> assign(:user, user)
884 |> post("/api/v1/accounts/#{other_user.id}/mute")
885
886 response = json_response(conn, 200)
887
888 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
889 user = User.get_cached_by_id(user.id)
890
891 conn =
892 build_conn()
893 |> assign(:user, user)
894 |> post("/api/v1/accounts/#{other_user.id}/unmute")
895
896 response = json_response(conn, 200)
897 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
898 end
899
900 test "without notifications", %{conn: conn} do
901 user = insert(:user)
902 other_user = insert(:user)
903
904 conn =
905 conn
906 |> assign(:user, user)
907 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
908
909 response = json_response(conn, 200)
910
911 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
912 user = User.get_cached_by_id(user.id)
913
914 conn =
915 build_conn()
916 |> assign(:user, user)
917 |> post("/api/v1/accounts/#{other_user.id}/unmute")
918
919 response = json_response(conn, 200)
920 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
921 end
922 end
923
924 describe "subscribing / unsubscribing" do
925 test "subscribing / unsubscribing to a user", %{conn: conn} do
926 user = insert(:user)
927 subscription_target = insert(:user)
928
929 conn =
930 conn
931 |> assign(:user, user)
932 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
933
934 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
935
936 conn =
937 build_conn()
938 |> assign(:user, user)
939 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
940
941 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
942 end
943 end
944
945 describe "subscribing" do
946 test "returns 404 when subscription_target not found", %{conn: conn} do
947 user = insert(:user)
948
949 conn =
950 conn
951 |> assign(:user, user)
952 |> post("/api/v1/pleroma/accounts/target_id/subscribe")
953
954 assert %{"error" => "Record not found"} = json_response(conn, 404)
955 end
956 end
957
958 describe "unsubscribing" do
959 test "returns 404 when subscription_target not found", %{conn: conn} do
960 user = insert(:user)
961
962 conn =
963 conn
964 |> assign(:user, user)
965 |> post("/api/v1/pleroma/accounts/target_id/unsubscribe")
966
967 assert %{"error" => "Record not found"} = json_response(conn, 404)
968 end
969 end
970
971 test "getting a list of mutes", %{conn: conn} do
972 user = insert(:user)
973 other_user = insert(:user)
974
975 {:ok, user} = User.mute(user, other_user)
976
977 conn =
978 conn
979 |> assign(:user, user)
980 |> get("/api/v1/mutes")
981
982 other_user_id = to_string(other_user.id)
983 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
984 end
985
986 test "blocking / unblocking a user", %{conn: conn} do
987 user = insert(:user)
988 other_user = insert(:user)
989
990 conn =
991 conn
992 |> assign(:user, user)
993 |> post("/api/v1/accounts/#{other_user.id}/block")
994
995 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
996
997 user = User.get_cached_by_id(user.id)
998
999 conn =
1000 build_conn()
1001 |> assign(:user, user)
1002 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1003
1004 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1005 end
1006
1007 test "getting a list of blocks", %{conn: conn} do
1008 user = insert(:user)
1009 other_user = insert(:user)
1010
1011 {:ok, user} = User.block(user, other_user)
1012
1013 conn =
1014 conn
1015 |> assign(:user, user)
1016 |> get("/api/v1/blocks")
1017
1018 other_user_id = to_string(other_user.id)
1019 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1020 end
1021
1022 test "unimplemented follow_requests, blocks, domain blocks" do
1023 user = insert(:user)
1024
1025 ["blocks", "domain_blocks", "follow_requests"]
1026 |> Enum.each(fn endpoint ->
1027 conn =
1028 build_conn()
1029 |> assign(:user, user)
1030 |> get("/api/v1/#{endpoint}")
1031
1032 assert [] = json_response(conn, 200)
1033 end)
1034 end
1035
1036 test "returns the favorites of a user", %{conn: conn} do
1037 user = insert(:user)
1038 other_user = insert(:user)
1039
1040 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1041 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1042
1043 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1044
1045 first_conn =
1046 conn
1047 |> assign(:user, user)
1048 |> get("/api/v1/favourites")
1049
1050 assert [status] = json_response(first_conn, 200)
1051 assert status["id"] == to_string(activity.id)
1052
1053 assert [{"link", _link_header}] =
1054 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1055
1056 # Honours query params
1057 {:ok, second_activity} =
1058 CommonAPI.post(other_user, %{
1059 "status" =>
1060 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1061 })
1062
1063 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1064
1065 last_like = status["id"]
1066
1067 second_conn =
1068 conn
1069 |> assign(:user, user)
1070 |> get("/api/v1/favourites?since_id=#{last_like}")
1071
1072 assert [second_status] = json_response(second_conn, 200)
1073 assert second_status["id"] == to_string(second_activity.id)
1074
1075 third_conn =
1076 conn
1077 |> assign(:user, user)
1078 |> get("/api/v1/favourites?limit=0")
1079
1080 assert [] = json_response(third_conn, 200)
1081 end
1082
1083 describe "getting favorites timeline of specified user" do
1084 setup do
1085 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
1086 [current_user: current_user, user: user]
1087 end
1088
1089 test "returns list of statuses favorited by specified user", %{
1090 conn: conn,
1091 current_user: current_user,
1092 user: user
1093 } do
1094 [activity | _] = insert_pair(:note_activity)
1095 CommonAPI.favorite(activity.id, user)
1096
1097 response =
1098 conn
1099 |> assign(:user, current_user)
1100 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1101 |> json_response(:ok)
1102
1103 [like] = response
1104
1105 assert length(response) == 1
1106 assert like["id"] == activity.id
1107 end
1108
1109 test "returns favorites for specified user_id when user is not logged in", %{
1110 conn: conn,
1111 user: user
1112 } do
1113 activity = insert(:note_activity)
1114 CommonAPI.favorite(activity.id, user)
1115
1116 response =
1117 conn
1118 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1119 |> json_response(:ok)
1120
1121 assert length(response) == 1
1122 end
1123
1124 test "returns favorited DM only when user is logged in and he is one of recipients", %{
1125 conn: conn,
1126 current_user: current_user,
1127 user: user
1128 } do
1129 {:ok, direct} =
1130 CommonAPI.post(current_user, %{
1131 "status" => "Hi @#{user.nickname}!",
1132 "visibility" => "direct"
1133 })
1134
1135 CommonAPI.favorite(direct.id, user)
1136
1137 response =
1138 conn
1139 |> assign(:user, current_user)
1140 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1141 |> json_response(:ok)
1142
1143 assert length(response) == 1
1144
1145 anonymous_response =
1146 conn
1147 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1148 |> json_response(:ok)
1149
1150 assert Enum.empty?(anonymous_response)
1151 end
1152
1153 test "does not return others' favorited DM when user is not one of recipients", %{
1154 conn: conn,
1155 current_user: current_user,
1156 user: user
1157 } do
1158 user_two = insert(:user)
1159
1160 {:ok, direct} =
1161 CommonAPI.post(user_two, %{
1162 "status" => "Hi @#{user.nickname}!",
1163 "visibility" => "direct"
1164 })
1165
1166 CommonAPI.favorite(direct.id, user)
1167
1168 response =
1169 conn
1170 |> assign(:user, current_user)
1171 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1172 |> json_response(:ok)
1173
1174 assert Enum.empty?(response)
1175 end
1176
1177 test "paginates favorites using since_id and max_id", %{
1178 conn: conn,
1179 current_user: current_user,
1180 user: user
1181 } do
1182 activities = insert_list(10, :note_activity)
1183
1184 Enum.each(activities, fn activity ->
1185 CommonAPI.favorite(activity.id, user)
1186 end)
1187
1188 third_activity = Enum.at(activities, 2)
1189 seventh_activity = Enum.at(activities, 6)
1190
1191 response =
1192 conn
1193 |> assign(:user, current_user)
1194 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
1195 since_id: third_activity.id,
1196 max_id: seventh_activity.id
1197 })
1198 |> json_response(:ok)
1199
1200 assert length(response) == 3
1201 refute third_activity in response
1202 refute seventh_activity in response
1203 end
1204
1205 test "limits favorites using limit parameter", %{
1206 conn: conn,
1207 current_user: current_user,
1208 user: user
1209 } do
1210 7
1211 |> insert_list(:note_activity)
1212 |> Enum.each(fn activity ->
1213 CommonAPI.favorite(activity.id, user)
1214 end)
1215
1216 response =
1217 conn
1218 |> assign(:user, current_user)
1219 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
1220 |> json_response(:ok)
1221
1222 assert length(response) == 3
1223 end
1224
1225 test "returns empty response when user does not have any favorited statuses", %{
1226 conn: conn,
1227 current_user: current_user,
1228 user: user
1229 } do
1230 response =
1231 conn
1232 |> assign(:user, current_user)
1233 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1234 |> json_response(:ok)
1235
1236 assert Enum.empty?(response)
1237 end
1238
1239 test "returns 404 error when specified user is not exist", %{conn: conn} do
1240 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
1241
1242 assert json_response(conn, 404) == %{"error" => "Record not found"}
1243 end
1244
1245 test "returns 403 error when user has hidden own favorites", %{
1246 conn: conn,
1247 current_user: current_user
1248 } do
1249 user = insert(:user, %{info: %{hide_favorites: true}})
1250 activity = insert(:note_activity)
1251 CommonAPI.favorite(activity.id, user)
1252
1253 conn =
1254 conn
1255 |> assign(:user, current_user)
1256 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1257
1258 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1259 end
1260
1261 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
1262 user = insert(:user)
1263 activity = insert(:note_activity)
1264 CommonAPI.favorite(activity.id, user)
1265
1266 conn =
1267 conn
1268 |> assign(:user, current_user)
1269 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
1270
1271 assert user.info.hide_favorites
1272 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
1273 end
1274 end
1275
1276 test "get instance information", %{conn: conn} do
1277 conn = get(conn, "/api/v1/instance")
1278 assert result = json_response(conn, 200)
1279
1280 email = Config.get([:instance, :email])
1281 # Note: not checking for "max_toot_chars" since it's optional
1282 assert %{
1283 "uri" => _,
1284 "title" => _,
1285 "description" => _,
1286 "version" => _,
1287 "email" => from_config_email,
1288 "urls" => %{
1289 "streaming_api" => _
1290 },
1291 "stats" => _,
1292 "thumbnail" => _,
1293 "languages" => _,
1294 "registrations" => _,
1295 "poll_limits" => _
1296 } = result
1297
1298 assert email == from_config_email
1299 end
1300
1301 test "get instance stats", %{conn: conn} do
1302 user = insert(:user, %{local: true})
1303
1304 user2 = insert(:user, %{local: true})
1305 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1306
1307 insert(:user, %{local: false, nickname: "u@peer1.com"})
1308 insert(:user, %{local: false, nickname: "u@peer2.com"})
1309
1310 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
1311
1312 # Stats should count users with missing or nil `info.deactivated` value
1313
1314 {:ok, _user} =
1315 user.id
1316 |> User.get_cached_by_id()
1317 |> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
1318
1319 Pleroma.Stats.force_update()
1320
1321 conn = get(conn, "/api/v1/instance")
1322
1323 assert result = json_response(conn, 200)
1324
1325 stats = result["stats"]
1326
1327 assert stats
1328 assert stats["user_count"] == 1
1329 assert stats["status_count"] == 1
1330 assert stats["domain_count"] == 2
1331 end
1332
1333 test "get peers", %{conn: conn} do
1334 insert(:user, %{local: false, nickname: "u@peer1.com"})
1335 insert(:user, %{local: false, nickname: "u@peer2.com"})
1336
1337 Pleroma.Stats.force_update()
1338
1339 conn = get(conn, "/api/v1/instance/peers")
1340
1341 assert result = json_response(conn, 200)
1342
1343 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
1344 end
1345
1346 test "put settings", %{conn: conn} do
1347 user = insert(:user)
1348
1349 conn =
1350 conn
1351 |> assign(:user, user)
1352 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
1353
1354 assert _result = json_response(conn, 200)
1355
1356 user = User.get_cached_by_ap_id(user.ap_id)
1357 assert user.info.settings == %{"programming" => "socks"}
1358 end
1359
1360 describe "pinned statuses" do
1361 setup do
1362 user = insert(:user)
1363 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
1364
1365 [user: user, activity: activity]
1366 end
1367
1368 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
1369 {:ok, _} = CommonAPI.pin(activity.id, user)
1370
1371 result =
1372 conn
1373 |> assign(:user, user)
1374 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1375 |> json_response(200)
1376
1377 id_str = to_string(activity.id)
1378
1379 assert [%{"id" => ^id_str, "pinned" => true}] = result
1380 end
1381 end
1382
1383 describe "reports" do
1384 setup do
1385 reporter = insert(:user)
1386 target_user = insert(:user)
1387
1388 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
1389
1390 [reporter: reporter, target_user: target_user, activity: activity]
1391 end
1392
1393 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
1394 assert %{"action_taken" => false, "id" => _} =
1395 conn
1396 |> assign(:user, reporter)
1397 |> post("/api/v1/reports", %{"account_id" => target_user.id})
1398 |> json_response(200)
1399 end
1400
1401 test "submit a report with statuses and comment", %{
1402 conn: conn,
1403 reporter: reporter,
1404 target_user: target_user,
1405 activity: activity
1406 } do
1407 assert %{"action_taken" => false, "id" => _} =
1408 conn
1409 |> assign(:user, reporter)
1410 |> post("/api/v1/reports", %{
1411 "account_id" => target_user.id,
1412 "status_ids" => [activity.id],
1413 "comment" => "bad status!",
1414 "forward" => "false"
1415 })
1416 |> json_response(200)
1417 end
1418
1419 test "account_id is required", %{
1420 conn: conn,
1421 reporter: reporter,
1422 activity: activity
1423 } do
1424 assert %{"error" => "Valid `account_id` required"} =
1425 conn
1426 |> assign(:user, reporter)
1427 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
1428 |> json_response(400)
1429 end
1430
1431 test "comment must be up to the size specified in the config", %{
1432 conn: conn,
1433 reporter: reporter,
1434 target_user: target_user
1435 } do
1436 max_size = Config.get([:instance, :max_report_comment_size], 1000)
1437 comment = String.pad_trailing("a", max_size + 1, "a")
1438
1439 error = %{"error" => "Comment must be up to #{max_size} characters"}
1440
1441 assert ^error =
1442 conn
1443 |> assign(:user, reporter)
1444 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
1445 |> json_response(400)
1446 end
1447
1448 test "returns error when account is not exist", %{
1449 conn: conn,
1450 reporter: reporter,
1451 activity: activity
1452 } do
1453 conn =
1454 conn
1455 |> assign(:user, reporter)
1456 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
1457
1458 assert json_response(conn, 400) == %{"error" => "Account not found"}
1459 end
1460 end
1461
1462 describe "link headers" do
1463 test "preserves parameters in link headers", %{conn: conn} do
1464 user = insert(:user)
1465 other_user = insert(:user)
1466
1467 {:ok, activity1} =
1468 CommonAPI.post(other_user, %{
1469 "status" => "hi @#{user.nickname}",
1470 "visibility" => "public"
1471 })
1472
1473 {:ok, activity2} =
1474 CommonAPI.post(other_user, %{
1475 "status" => "hi @#{user.nickname}",
1476 "visibility" => "public"
1477 })
1478
1479 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
1480 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
1481
1482 conn =
1483 conn
1484 |> assign(:user, user)
1485 |> get("/api/v1/notifications", %{media_only: true})
1486
1487 assert [link_header] = get_resp_header(conn, "link")
1488 assert link_header =~ ~r/media_only=true/
1489 assert link_header =~ ~r/min_id=#{notification2.id}/
1490 assert link_header =~ ~r/max_id=#{notification1.id}/
1491 end
1492 end
1493
1494 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
1495 # Need to set an old-style integer ID to reproduce the problem
1496 # (these are no longer assigned to new accounts but were preserved
1497 # for existing accounts during the migration to flakeIDs)
1498 user_one = insert(:user, %{id: 1212})
1499 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
1500
1501 resp_one =
1502 conn
1503 |> get("/api/v1/accounts/#{user_one.id}")
1504
1505 resp_two =
1506 conn
1507 |> get("/api/v1/accounts/#{user_two.nickname}")
1508
1509 resp_three =
1510 conn
1511 |> get("/api/v1/accounts/#{user_two.id}")
1512
1513 acc_one = json_response(resp_one, 200)
1514 acc_two = json_response(resp_two, 200)
1515 acc_three = json_response(resp_three, 200)
1516 refute acc_one == acc_two
1517 assert acc_two == acc_three
1518 end
1519
1520 describe "custom emoji" do
1521 test "with tags", %{conn: conn} do
1522 [emoji | _body] =
1523 conn
1524 |> get("/api/v1/custom_emojis")
1525 |> json_response(200)
1526
1527 assert Map.has_key?(emoji, "shortcode")
1528 assert Map.has_key?(emoji, "static_url")
1529 assert Map.has_key?(emoji, "tags")
1530 assert is_list(emoji["tags"])
1531 assert Map.has_key?(emoji, "category")
1532 assert Map.has_key?(emoji, "url")
1533 assert Map.has_key?(emoji, "visible_in_picker")
1534 end
1535 end
1536
1537 describe "index/2 redirections" do
1538 setup %{conn: conn} do
1539 session_opts = [
1540 store: :cookie,
1541 key: "_test",
1542 signing_salt: "cooldude"
1543 ]
1544
1545 conn =
1546 conn
1547 |> Plug.Session.call(Plug.Session.init(session_opts))
1548 |> fetch_session()
1549
1550 test_path = "/web/statuses/test"
1551 %{conn: conn, path: test_path}
1552 end
1553
1554 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
1555 conn = get(conn, path)
1556
1557 assert conn.status == 302
1558 assert redirected_to(conn) == "/web/login"
1559 end
1560
1561 test "redirects not logged-in users to the login page on private instances", %{
1562 conn: conn,
1563 path: path
1564 } do
1565 Config.put([:instance, :public], false)
1566
1567 conn = get(conn, path)
1568
1569 assert conn.status == 302
1570 assert redirected_to(conn) == "/web/login"
1571 end
1572
1573 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
1574 token = insert(:oauth_token)
1575
1576 conn =
1577 conn
1578 |> assign(:user, token.user)
1579 |> put_session(:oauth_token, token.token)
1580 |> get(path)
1581
1582 assert conn.status == 200
1583 end
1584
1585 test "saves referer path to session", %{conn: conn, path: path} do
1586 conn = get(conn, path)
1587 return_to = Plug.Conn.get_session(conn, :return_to)
1588
1589 assert return_to == path
1590 end
1591
1592 test "redirects to the saved path after log in", %{conn: conn, path: path} do
1593 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1594 auth = insert(:oauth_authorization, app: app)
1595
1596 conn =
1597 conn
1598 |> put_session(:return_to, path)
1599 |> get("/web/login", %{code: auth.token})
1600
1601 assert conn.status == 302
1602 assert redirected_to(conn) == path
1603 end
1604
1605 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
1606 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1607 auth = insert(:oauth_authorization, app: app)
1608
1609 conn = get(conn, "/web/login", %{code: auth.token})
1610
1611 assert conn.status == 302
1612 assert redirected_to(conn) == "/web/getting-started"
1613 end
1614 end
1615
1616 describe "create account by app" do
1617 setup do
1618 valid_params = %{
1619 username: "lain",
1620 email: "lain@example.org",
1621 password: "PlzDontHackLain",
1622 agreement: true
1623 }
1624
1625 [valid_params: valid_params]
1626 end
1627
1628 test "Account registration via Application", %{conn: conn} do
1629 conn =
1630 conn
1631 |> post("/api/v1/apps", %{
1632 client_name: "client_name",
1633 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1634 scopes: "read, write, follow"
1635 })
1636
1637 %{
1638 "client_id" => client_id,
1639 "client_secret" => client_secret,
1640 "id" => _,
1641 "name" => "client_name",
1642 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1643 "vapid_key" => _,
1644 "website" => nil
1645 } = json_response(conn, 200)
1646
1647 conn =
1648 conn
1649 |> post("/oauth/token", %{
1650 grant_type: "client_credentials",
1651 client_id: client_id,
1652 client_secret: client_secret
1653 })
1654
1655 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1656 json_response(conn, 200)
1657
1658 assert token
1659 token_from_db = Repo.get_by(Token, token: token)
1660 assert token_from_db
1661 assert refresh
1662 assert scope == "read write follow"
1663
1664 conn =
1665 build_conn()
1666 |> put_req_header("authorization", "Bearer " <> token)
1667 |> post("/api/v1/accounts", %{
1668 username: "lain",
1669 email: "lain@example.org",
1670 password: "PlzDontHackLain",
1671 bio: "Test Bio",
1672 agreement: true
1673 })
1674
1675 %{
1676 "access_token" => token,
1677 "created_at" => _created_at,
1678 "scope" => _scope,
1679 "token_type" => "Bearer"
1680 } = json_response(conn, 200)
1681
1682 token_from_db = Repo.get_by(Token, token: token)
1683 assert token_from_db
1684 token_from_db = Repo.preload(token_from_db, :user)
1685 assert token_from_db.user
1686
1687 assert token_from_db.user.info.confirmation_pending
1688 end
1689
1690 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
1691 _user = insert(:user, email: "lain@example.org")
1692 app_token = insert(:oauth_token, user: nil)
1693
1694 conn =
1695 conn
1696 |> put_req_header("authorization", "Bearer " <> app_token.token)
1697
1698 res = post(conn, "/api/v1/accounts", valid_params)
1699 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
1700 end
1701
1702 test "rate limit", %{conn: conn} do
1703 app_token = insert(:oauth_token, user: nil)
1704
1705 conn =
1706 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1707 |> Map.put(:remote_ip, {15, 15, 15, 15})
1708
1709 for i <- 1..5 do
1710 conn =
1711 conn
1712 |> post("/api/v1/accounts", %{
1713 username: "#{i}lain",
1714 email: "#{i}lain@example.org",
1715 password: "PlzDontHackLain",
1716 agreement: true
1717 })
1718
1719 %{
1720 "access_token" => token,
1721 "created_at" => _created_at,
1722 "scope" => _scope,
1723 "token_type" => "Bearer"
1724 } = json_response(conn, 200)
1725
1726 token_from_db = Repo.get_by(Token, token: token)
1727 assert token_from_db
1728 token_from_db = Repo.preload(token_from_db, :user)
1729 assert token_from_db.user
1730
1731 assert token_from_db.user.info.confirmation_pending
1732 end
1733
1734 conn =
1735 conn
1736 |> post("/api/v1/accounts", %{
1737 username: "6lain",
1738 email: "6lain@example.org",
1739 password: "PlzDontHackLain",
1740 agreement: true
1741 })
1742
1743 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
1744 end
1745
1746 test "returns bad_request if missing required params", %{
1747 conn: conn,
1748 valid_params: valid_params
1749 } do
1750 app_token = insert(:oauth_token, user: nil)
1751
1752 conn =
1753 conn
1754 |> put_req_header("authorization", "Bearer " <> app_token.token)
1755
1756 res = post(conn, "/api/v1/accounts", valid_params)
1757 assert json_response(res, 200)
1758
1759 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
1760 |> Stream.zip(valid_params)
1761 |> Enum.each(fn {ip, {attr, _}} ->
1762 res =
1763 conn
1764 |> Map.put(:remote_ip, ip)
1765 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
1766 |> json_response(400)
1767
1768 assert res == %{"error" => "Missing parameters"}
1769 end)
1770 end
1771
1772 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
1773 conn =
1774 conn
1775 |> put_req_header("authorization", "Bearer " <> "invalid-token")
1776
1777 res = post(conn, "/api/v1/accounts", valid_params)
1778 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
1779 end
1780 end
1781
1782 describe "GET /api/v1/polls/:id" do
1783 test "returns poll entity for object id", %{conn: conn} do
1784 user = insert(:user)
1785
1786 {:ok, activity} =
1787 CommonAPI.post(user, %{
1788 "status" => "Pleroma does",
1789 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
1790 })
1791
1792 object = Object.normalize(activity)
1793
1794 conn =
1795 conn
1796 |> assign(:user, user)
1797 |> get("/api/v1/polls/#{object.id}")
1798
1799 response = json_response(conn, 200)
1800 id = to_string(object.id)
1801 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
1802 end
1803
1804 test "does not expose polls for private statuses", %{conn: conn} do
1805 user = insert(:user)
1806 other_user = insert(:user)
1807
1808 {:ok, activity} =
1809 CommonAPI.post(user, %{
1810 "status" => "Pleroma does",
1811 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
1812 "visibility" => "private"
1813 })
1814
1815 object = Object.normalize(activity)
1816
1817 conn =
1818 conn
1819 |> assign(:user, other_user)
1820 |> get("/api/v1/polls/#{object.id}")
1821
1822 assert json_response(conn, 404)
1823 end
1824 end
1825
1826 describe "POST /api/v1/polls/:id/votes" do
1827 test "votes are added to the poll", %{conn: conn} do
1828 user = insert(:user)
1829 other_user = insert(:user)
1830
1831 {:ok, activity} =
1832 CommonAPI.post(user, %{
1833 "status" => "A very delicious sandwich",
1834 "poll" => %{
1835 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
1836 "expires_in" => 20,
1837 "multiple" => true
1838 }
1839 })
1840
1841 object = Object.normalize(activity)
1842
1843 conn =
1844 conn
1845 |> assign(:user, other_user)
1846 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
1847
1848 assert json_response(conn, 200)
1849 object = Object.get_by_id(object.id)
1850
1851 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
1852 total_items == 1
1853 end)
1854 end
1855
1856 test "author can't vote", %{conn: conn} do
1857 user = insert(:user)
1858
1859 {:ok, activity} =
1860 CommonAPI.post(user, %{
1861 "status" => "Am I cute?",
1862 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
1863 })
1864
1865 object = Object.normalize(activity)
1866
1867 assert conn
1868 |> assign(:user, user)
1869 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
1870 |> json_response(422) == %{"error" => "Poll's author can't vote"}
1871
1872 object = Object.get_by_id(object.id)
1873
1874 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
1875 end
1876
1877 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
1878 user = insert(:user)
1879 other_user = insert(:user)
1880
1881 {:ok, activity} =
1882 CommonAPI.post(user, %{
1883 "status" => "The glass is",
1884 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
1885 })
1886
1887 object = Object.normalize(activity)
1888
1889 assert conn
1890 |> assign(:user, other_user)
1891 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
1892 |> json_response(422) == %{"error" => "Too many choices"}
1893
1894 object = Object.get_by_id(object.id)
1895
1896 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
1897 total_items == 1
1898 end)
1899 end
1900
1901 test "does not allow choice index to be greater than options count", %{conn: conn} do
1902 user = insert(:user)
1903 other_user = insert(:user)
1904
1905 {:ok, activity} =
1906 CommonAPI.post(user, %{
1907 "status" => "Am I cute?",
1908 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
1909 })
1910
1911 object = Object.normalize(activity)
1912
1913 conn =
1914 conn
1915 |> assign(:user, other_user)
1916 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
1917
1918 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
1919 end
1920
1921 test "returns 404 error when object is not exist", %{conn: conn} do
1922 user = insert(:user)
1923
1924 conn =
1925 conn
1926 |> assign(:user, user)
1927 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
1928
1929 assert json_response(conn, 404) == %{"error" => "Record not found"}
1930 end
1931
1932 test "returns 404 when poll is private and not available for user", %{conn: conn} do
1933 user = insert(:user)
1934 other_user = insert(:user)
1935
1936 {:ok, activity} =
1937 CommonAPI.post(user, %{
1938 "status" => "Am I cute?",
1939 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
1940 "visibility" => "private"
1941 })
1942
1943 object = Object.normalize(activity)
1944
1945 conn =
1946 conn
1947 |> assign(:user, other_user)
1948 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
1949
1950 assert json_response(conn, 404) == %{"error" => "Record not found"}
1951 end
1952 end
1953
1954 describe "POST /auth/password, with valid parameters" do
1955 setup %{conn: conn} do
1956 user = insert(:user)
1957 conn = post(conn, "/auth/password?email=#{user.email}")
1958 %{conn: conn, user: user}
1959 end
1960
1961 test "it returns 204", %{conn: conn} do
1962 assert json_response(conn, :no_content)
1963 end
1964
1965 test "it creates a PasswordResetToken record for user", %{user: user} do
1966 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
1967 assert token_record
1968 end
1969
1970 test "it sends an email to user", %{user: user} do
1971 ObanHelpers.perform_all()
1972 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
1973
1974 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
1975 notify_email = Config.get([:instance, :notify_email])
1976 instance_name = Config.get([:instance, :name])
1977
1978 assert_email_sent(
1979 from: {instance_name, notify_email},
1980 to: {user.name, user.email},
1981 html_body: email.html_body
1982 )
1983 end
1984 end
1985
1986 describe "POST /auth/password, with invalid parameters" do
1987 setup do
1988 user = insert(:user)
1989 {:ok, user: user}
1990 end
1991
1992 test "it returns 404 when user is not found", %{conn: conn, user: user} do
1993 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
1994 assert conn.status == 404
1995 assert conn.resp_body == ""
1996 end
1997
1998 test "it returns 400 when user is not local", %{conn: conn, user: user} do
1999 {:ok, user} = Repo.update(Changeset.change(user, local: false))
2000 conn = post(conn, "/auth/password?email=#{user.email}")
2001 assert conn.status == 400
2002 assert conn.resp_body == ""
2003 end
2004 end
2005
2006 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
2007 setup do
2008 {:ok, user} =
2009 insert(:user)
2010 |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
2011 |> Repo.update()
2012
2013 assert user.info.confirmation_pending
2014
2015 [user: user]
2016 end
2017
2018 clear_config([:instance, :account_activation_required]) do
2019 Config.put([:instance, :account_activation_required], true)
2020 end
2021
2022 test "resend account confirmation email", %{conn: conn, user: user} do
2023 conn
2024 |> assign(:user, user)
2025 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
2026 |> json_response(:no_content)
2027
2028 ObanHelpers.perform_all()
2029
2030 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
2031 notify_email = Config.get([:instance, :notify_email])
2032 instance_name = Config.get([:instance, :name])
2033
2034 assert_email_sent(
2035 from: {instance_name, notify_email},
2036 to: {user.name, user.email},
2037 html_body: email.html_body
2038 )
2039 end
2040 end
2041
2042 describe "GET /api/v1/suggestions" do
2043 setup do
2044 user = insert(:user)
2045 other_user = insert(:user)
2046 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
2047 url500 = "http://test500?#{host}&#{user.nickname}"
2048 url200 = "http://test200?#{host}&#{user.nickname}"
2049
2050 mock(fn
2051 %{method: :get, url: ^url500} ->
2052 %Tesla.Env{status: 500, body: "bad request"}
2053
2054 %{method: :get, url: ^url200} ->
2055 %Tesla.Env{
2056 status: 200,
2057 body:
2058 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
2059 other_user.ap_id
2060 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
2061 }
2062 end)
2063
2064 [user: user, other_user: other_user]
2065 end
2066
2067 clear_config(:suggestions)
2068
2069 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
2070 Config.put([:suggestions, :enabled], false)
2071
2072 res =
2073 conn
2074 |> assign(:user, user)
2075 |> get("/api/v1/suggestions")
2076 |> json_response(200)
2077
2078 assert res == []
2079 end
2080
2081 test "returns error", %{conn: conn, user: user} do
2082 Config.put([:suggestions, :enabled], true)
2083 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
2084
2085 assert capture_log(fn ->
2086 res =
2087 conn
2088 |> assign(:user, user)
2089 |> get("/api/v1/suggestions")
2090 |> json_response(500)
2091
2092 assert res == "Something went wrong"
2093 end) =~ "Could not retrieve suggestions"
2094 end
2095
2096 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
2097 Config.put([:suggestions, :enabled], true)
2098 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
2099
2100 res =
2101 conn
2102 |> assign(:user, user)
2103 |> get("/api/v1/suggestions")
2104 |> json_response(200)
2105
2106 assert res == [
2107 %{
2108 "acct" => "yj455",
2109 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
2110 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
2111 "id" => 0
2112 },
2113 %{
2114 "acct" => other_user.ap_id,
2115 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
2116 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
2117 "id" => other_user.id
2118 }
2119 ]
2120 end
2121 end
2122
2123 describe "PUT /api/v1/media/:id" do
2124 setup do
2125 actor = insert(:user)
2126
2127 file = %Plug.Upload{
2128 content_type: "image/jpg",
2129 path: Path.absname("test/fixtures/image.jpg"),
2130 filename: "an_image.jpg"
2131 }
2132
2133 {:ok, %Object{} = object} =
2134 ActivityPub.upload(
2135 file,
2136 actor: User.ap_id(actor),
2137 description: "test-m"
2138 )
2139
2140 [actor: actor, object: object]
2141 end
2142
2143 test "updates name of media", %{conn: conn, actor: actor, object: object} do
2144 media =
2145 conn
2146 |> assign(:user, actor)
2147 |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"})
2148 |> json_response(:ok)
2149
2150 assert media["description"] == "test-media"
2151 assert refresh_record(object).data["name"] == "test-media"
2152 end
2153
2154 test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do
2155 media =
2156 conn
2157 |> assign(:user, actor)
2158 |> put("/api/v1/media/#{object.id}", %{})
2159 |> json_response(400)
2160
2161 assert media == %{"error" => "bad_request"}
2162 end
2163 end
2164
2165 describe "DELETE /auth/sign_out" do
2166 test "redirect to root page", %{conn: conn} do
2167 user = insert(:user)
2168
2169 conn =
2170 conn
2171 |> assign(:user, user)
2172 |> delete("/auth/sign_out")
2173
2174 assert conn.status == 302
2175 assert redirected_to(conn) == "/"
2176 end
2177 end
2178
2179 describe "GET /api/v1/accounts/:id/lists - account_lists" do
2180 test "returns lists to which the account belongs", %{conn: conn} do
2181 user = insert(:user)
2182 other_user = insert(:user)
2183 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
2184 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
2185
2186 res =
2187 conn
2188 |> assign(:user, user)
2189 |> get("/api/v1/accounts/#{other_user.id}/lists")
2190 |> json_response(200)
2191
2192 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
2193 end
2194 end
2195
2196 describe "empty_array, stubs for mastodon api" do
2197 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
2198 user = insert(:user)
2199
2200 res =
2201 conn
2202 |> assign(:user, user)
2203 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
2204 |> json_response(200)
2205
2206 assert res == []
2207 end
2208
2209 test "GET /api/v1/endorsements", %{conn: conn} do
2210 user = insert(:user)
2211
2212 res =
2213 conn
2214 |> assign(:user, user)
2215 |> get("/api/v1/endorsements")
2216 |> json_response(200)
2217
2218 assert res == []
2219 end
2220
2221 test "GET /api/v1/trends", %{conn: conn} do
2222 user = insert(:user)
2223
2224 res =
2225 conn
2226 |> assign(:user, user)
2227 |> get("/api/v1/trends")
2228 |> json_response(200)
2229
2230 assert res == []
2231 end
2232 end
2233 end