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