Extract conversation actions from `MastodonAPIController` to ConversationController
[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 "reports" do
1321 setup do
1322 reporter = insert(:user)
1323 target_user = insert(:user)
1324
1325 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
1326
1327 [reporter: reporter, target_user: target_user, activity: activity]
1328 end
1329
1330 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
1331 assert %{"action_taken" => false, "id" => _} =
1332 conn
1333 |> assign(:user, reporter)
1334 |> post("/api/v1/reports", %{"account_id" => target_user.id})
1335 |> json_response(200)
1336 end
1337
1338 test "submit a report with statuses and comment", %{
1339 conn: conn,
1340 reporter: reporter,
1341 target_user: target_user,
1342 activity: activity
1343 } do
1344 assert %{"action_taken" => false, "id" => _} =
1345 conn
1346 |> assign(:user, reporter)
1347 |> post("/api/v1/reports", %{
1348 "account_id" => target_user.id,
1349 "status_ids" => [activity.id],
1350 "comment" => "bad status!",
1351 "forward" => "false"
1352 })
1353 |> json_response(200)
1354 end
1355
1356 test "account_id is required", %{
1357 conn: conn,
1358 reporter: reporter,
1359 activity: activity
1360 } do
1361 assert %{"error" => "Valid `account_id` required"} =
1362 conn
1363 |> assign(:user, reporter)
1364 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
1365 |> json_response(400)
1366 end
1367
1368 test "comment must be up to the size specified in the config", %{
1369 conn: conn,
1370 reporter: reporter,
1371 target_user: target_user
1372 } do
1373 max_size = Config.get([:instance, :max_report_comment_size], 1000)
1374 comment = String.pad_trailing("a", max_size + 1, "a")
1375
1376 error = %{"error" => "Comment must be up to #{max_size} characters"}
1377
1378 assert ^error =
1379 conn
1380 |> assign(:user, reporter)
1381 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
1382 |> json_response(400)
1383 end
1384
1385 test "returns error when account is not exist", %{
1386 conn: conn,
1387 reporter: reporter,
1388 activity: activity
1389 } do
1390 conn =
1391 conn
1392 |> assign(:user, reporter)
1393 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
1394
1395 assert json_response(conn, 400) == %{"error" => "Account not found"}
1396 end
1397 end
1398
1399 describe "link headers" do
1400 test "preserves parameters in link headers", %{conn: conn} do
1401 user = insert(:user)
1402 other_user = insert(:user)
1403
1404 {:ok, activity1} =
1405 CommonAPI.post(other_user, %{
1406 "status" => "hi @#{user.nickname}",
1407 "visibility" => "public"
1408 })
1409
1410 {:ok, activity2} =
1411 CommonAPI.post(other_user, %{
1412 "status" => "hi @#{user.nickname}",
1413 "visibility" => "public"
1414 })
1415
1416 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
1417 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
1418
1419 conn =
1420 conn
1421 |> assign(:user, user)
1422 |> get("/api/v1/notifications", %{media_only: true})
1423
1424 assert [link_header] = get_resp_header(conn, "link")
1425 assert link_header =~ ~r/media_only=true/
1426 assert link_header =~ ~r/min_id=#{notification2.id}/
1427 assert link_header =~ ~r/max_id=#{notification1.id}/
1428 end
1429 end
1430
1431 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
1432 # Need to set an old-style integer ID to reproduce the problem
1433 # (these are no longer assigned to new accounts but were preserved
1434 # for existing accounts during the migration to flakeIDs)
1435 user_one = insert(:user, %{id: 1212})
1436 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
1437
1438 resp_one =
1439 conn
1440 |> get("/api/v1/accounts/#{user_one.id}")
1441
1442 resp_two =
1443 conn
1444 |> get("/api/v1/accounts/#{user_two.nickname}")
1445
1446 resp_three =
1447 conn
1448 |> get("/api/v1/accounts/#{user_two.id}")
1449
1450 acc_one = json_response(resp_one, 200)
1451 acc_two = json_response(resp_two, 200)
1452 acc_three = json_response(resp_three, 200)
1453 refute acc_one == acc_two
1454 assert acc_two == acc_three
1455 end
1456
1457 describe "custom emoji" do
1458 test "with tags", %{conn: conn} do
1459 [emoji | _body] =
1460 conn
1461 |> get("/api/v1/custom_emojis")
1462 |> json_response(200)
1463
1464 assert Map.has_key?(emoji, "shortcode")
1465 assert Map.has_key?(emoji, "static_url")
1466 assert Map.has_key?(emoji, "tags")
1467 assert is_list(emoji["tags"])
1468 assert Map.has_key?(emoji, "category")
1469 assert Map.has_key?(emoji, "url")
1470 assert Map.has_key?(emoji, "visible_in_picker")
1471 end
1472 end
1473
1474 describe "index/2 redirections" do
1475 setup %{conn: conn} do
1476 session_opts = [
1477 store: :cookie,
1478 key: "_test",
1479 signing_salt: "cooldude"
1480 ]
1481
1482 conn =
1483 conn
1484 |> Plug.Session.call(Plug.Session.init(session_opts))
1485 |> fetch_session()
1486
1487 test_path = "/web/statuses/test"
1488 %{conn: conn, path: test_path}
1489 end
1490
1491 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
1492 conn = get(conn, path)
1493
1494 assert conn.status == 302
1495 assert redirected_to(conn) == "/web/login"
1496 end
1497
1498 test "redirects not logged-in users to the login page on private instances", %{
1499 conn: conn,
1500 path: path
1501 } do
1502 Config.put([:instance, :public], false)
1503
1504 conn = get(conn, path)
1505
1506 assert conn.status == 302
1507 assert redirected_to(conn) == "/web/login"
1508 end
1509
1510 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
1511 token = insert(:oauth_token)
1512
1513 conn =
1514 conn
1515 |> assign(:user, token.user)
1516 |> put_session(:oauth_token, token.token)
1517 |> get(path)
1518
1519 assert conn.status == 200
1520 end
1521
1522 test "saves referer path to session", %{conn: conn, path: path} do
1523 conn = get(conn, path)
1524 return_to = Plug.Conn.get_session(conn, :return_to)
1525
1526 assert return_to == path
1527 end
1528
1529 test "redirects to the saved path after log in", %{conn: conn, path: path} do
1530 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1531 auth = insert(:oauth_authorization, app: app)
1532
1533 conn =
1534 conn
1535 |> put_session(:return_to, path)
1536 |> get("/web/login", %{code: auth.token})
1537
1538 assert conn.status == 302
1539 assert redirected_to(conn) == path
1540 end
1541
1542 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
1543 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
1544 auth = insert(:oauth_authorization, app: app)
1545
1546 conn = get(conn, "/web/login", %{code: auth.token})
1547
1548 assert conn.status == 302
1549 assert redirected_to(conn) == "/web/getting-started"
1550 end
1551 end
1552
1553 describe "create account by app" do
1554 setup do
1555 valid_params = %{
1556 username: "lain",
1557 email: "lain@example.org",
1558 password: "PlzDontHackLain",
1559 agreement: true
1560 }
1561
1562 [valid_params: valid_params]
1563 end
1564
1565 test "Account registration via Application", %{conn: conn} do
1566 conn =
1567 conn
1568 |> post("/api/v1/apps", %{
1569 client_name: "client_name",
1570 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
1571 scopes: "read, write, follow"
1572 })
1573
1574 %{
1575 "client_id" => client_id,
1576 "client_secret" => client_secret,
1577 "id" => _,
1578 "name" => "client_name",
1579 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
1580 "vapid_key" => _,
1581 "website" => nil
1582 } = json_response(conn, 200)
1583
1584 conn =
1585 conn
1586 |> post("/oauth/token", %{
1587 grant_type: "client_credentials",
1588 client_id: client_id,
1589 client_secret: client_secret
1590 })
1591
1592 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
1593 json_response(conn, 200)
1594
1595 assert token
1596 token_from_db = Repo.get_by(Token, token: token)
1597 assert token_from_db
1598 assert refresh
1599 assert scope == "read write follow"
1600
1601 conn =
1602 build_conn()
1603 |> put_req_header("authorization", "Bearer " <> token)
1604 |> post("/api/v1/accounts", %{
1605 username: "lain",
1606 email: "lain@example.org",
1607 password: "PlzDontHackLain",
1608 bio: "Test Bio",
1609 agreement: true
1610 })
1611
1612 %{
1613 "access_token" => token,
1614 "created_at" => _created_at,
1615 "scope" => _scope,
1616 "token_type" => "Bearer"
1617 } = json_response(conn, 200)
1618
1619 token_from_db = Repo.get_by(Token, token: token)
1620 assert token_from_db
1621 token_from_db = Repo.preload(token_from_db, :user)
1622 assert token_from_db.user
1623
1624 assert token_from_db.user.info.confirmation_pending
1625 end
1626
1627 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
1628 _user = insert(:user, email: "lain@example.org")
1629 app_token = insert(:oauth_token, user: nil)
1630
1631 conn =
1632 conn
1633 |> put_req_header("authorization", "Bearer " <> app_token.token)
1634
1635 res = post(conn, "/api/v1/accounts", valid_params)
1636 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
1637 end
1638
1639 test "rate limit", %{conn: conn} do
1640 app_token = insert(:oauth_token, user: nil)
1641
1642 conn =
1643 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
1644 |> Map.put(:remote_ip, {15, 15, 15, 15})
1645
1646 for i <- 1..5 do
1647 conn =
1648 conn
1649 |> post("/api/v1/accounts", %{
1650 username: "#{i}lain",
1651 email: "#{i}lain@example.org",
1652 password: "PlzDontHackLain",
1653 agreement: true
1654 })
1655
1656 %{
1657 "access_token" => token,
1658 "created_at" => _created_at,
1659 "scope" => _scope,
1660 "token_type" => "Bearer"
1661 } = json_response(conn, 200)
1662
1663 token_from_db = Repo.get_by(Token, token: token)
1664 assert token_from_db
1665 token_from_db = Repo.preload(token_from_db, :user)
1666 assert token_from_db.user
1667
1668 assert token_from_db.user.info.confirmation_pending
1669 end
1670
1671 conn =
1672 conn
1673 |> post("/api/v1/accounts", %{
1674 username: "6lain",
1675 email: "6lain@example.org",
1676 password: "PlzDontHackLain",
1677 agreement: true
1678 })
1679
1680 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
1681 end
1682
1683 test "returns bad_request if missing required params", %{
1684 conn: conn,
1685 valid_params: valid_params
1686 } do
1687 app_token = insert(:oauth_token, user: nil)
1688
1689 conn =
1690 conn
1691 |> put_req_header("authorization", "Bearer " <> app_token.token)
1692
1693 res = post(conn, "/api/v1/accounts", valid_params)
1694 assert json_response(res, 200)
1695
1696 [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
1697 |> Stream.zip(valid_params)
1698 |> Enum.each(fn {ip, {attr, _}} ->
1699 res =
1700 conn
1701 |> Map.put(:remote_ip, ip)
1702 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
1703 |> json_response(400)
1704
1705 assert res == %{"error" => "Missing parameters"}
1706 end)
1707 end
1708
1709 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
1710 conn =
1711 conn
1712 |> put_req_header("authorization", "Bearer " <> "invalid-token")
1713
1714 res = post(conn, "/api/v1/accounts", valid_params)
1715 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
1716 end
1717 end
1718
1719 describe "GET /api/v1/polls/:id" do
1720 test "returns poll entity for object id", %{conn: conn} do
1721 user = insert(:user)
1722
1723 {:ok, activity} =
1724 CommonAPI.post(user, %{
1725 "status" => "Pleroma does",
1726 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
1727 })
1728
1729 object = Object.normalize(activity)
1730
1731 conn =
1732 conn
1733 |> assign(:user, user)
1734 |> get("/api/v1/polls/#{object.id}")
1735
1736 response = json_response(conn, 200)
1737 id = to_string(object.id)
1738 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
1739 end
1740
1741 test "does not expose polls for private statuses", %{conn: conn} do
1742 user = insert(:user)
1743 other_user = insert(:user)
1744
1745 {:ok, activity} =
1746 CommonAPI.post(user, %{
1747 "status" => "Pleroma does",
1748 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
1749 "visibility" => "private"
1750 })
1751
1752 object = Object.normalize(activity)
1753
1754 conn =
1755 conn
1756 |> assign(:user, other_user)
1757 |> get("/api/v1/polls/#{object.id}")
1758
1759 assert json_response(conn, 404)
1760 end
1761 end
1762
1763 describe "POST /api/v1/polls/:id/votes" do
1764 test "votes are added to the poll", %{conn: conn} do
1765 user = insert(:user)
1766 other_user = insert(:user)
1767
1768 {:ok, activity} =
1769 CommonAPI.post(user, %{
1770 "status" => "A very delicious sandwich",
1771 "poll" => %{
1772 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
1773 "expires_in" => 20,
1774 "multiple" => true
1775 }
1776 })
1777
1778 object = Object.normalize(activity)
1779
1780 conn =
1781 conn
1782 |> assign(:user, other_user)
1783 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
1784
1785 assert json_response(conn, 200)
1786 object = Object.get_by_id(object.id)
1787
1788 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
1789 total_items == 1
1790 end)
1791 end
1792
1793 test "author can't vote", %{conn: conn} do
1794 user = insert(:user)
1795
1796 {:ok, activity} =
1797 CommonAPI.post(user, %{
1798 "status" => "Am I cute?",
1799 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
1800 })
1801
1802 object = Object.normalize(activity)
1803
1804 assert conn
1805 |> assign(:user, user)
1806 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
1807 |> json_response(422) == %{"error" => "Poll's author can't vote"}
1808
1809 object = Object.get_by_id(object.id)
1810
1811 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
1812 end
1813
1814 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
1815 user = insert(:user)
1816 other_user = insert(:user)
1817
1818 {:ok, activity} =
1819 CommonAPI.post(user, %{
1820 "status" => "The glass is",
1821 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
1822 })
1823
1824 object = Object.normalize(activity)
1825
1826 assert conn
1827 |> assign(:user, other_user)
1828 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
1829 |> json_response(422) == %{"error" => "Too many choices"}
1830
1831 object = Object.get_by_id(object.id)
1832
1833 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
1834 total_items == 1
1835 end)
1836 end
1837
1838 test "does not allow choice index to be greater than options count", %{conn: conn} do
1839 user = insert(:user)
1840 other_user = insert(:user)
1841
1842 {:ok, activity} =
1843 CommonAPI.post(user, %{
1844 "status" => "Am I cute?",
1845 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
1846 })
1847
1848 object = Object.normalize(activity)
1849
1850 conn =
1851 conn
1852 |> assign(:user, other_user)
1853 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
1854
1855 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
1856 end
1857
1858 test "returns 404 error when object is not exist", %{conn: conn} do
1859 user = insert(:user)
1860
1861 conn =
1862 conn
1863 |> assign(:user, user)
1864 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
1865
1866 assert json_response(conn, 404) == %{"error" => "Record not found"}
1867 end
1868
1869 test "returns 404 when poll is private and not available for user", %{conn: conn} do
1870 user = insert(:user)
1871 other_user = insert(:user)
1872
1873 {:ok, activity} =
1874 CommonAPI.post(user, %{
1875 "status" => "Am I cute?",
1876 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
1877 "visibility" => "private"
1878 })
1879
1880 object = Object.normalize(activity)
1881
1882 conn =
1883 conn
1884 |> assign(:user, other_user)
1885 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
1886
1887 assert json_response(conn, 404) == %{"error" => "Record not found"}
1888 end
1889 end
1890
1891 describe "POST /auth/password, with valid parameters" do
1892 setup %{conn: conn} do
1893 user = insert(:user)
1894 conn = post(conn, "/auth/password?email=#{user.email}")
1895 %{conn: conn, user: user}
1896 end
1897
1898 test "it returns 204", %{conn: conn} do
1899 assert json_response(conn, :no_content)
1900 end
1901
1902 test "it creates a PasswordResetToken record for user", %{user: user} do
1903 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
1904 assert token_record
1905 end
1906
1907 test "it sends an email to user", %{user: user} do
1908 ObanHelpers.perform_all()
1909 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
1910
1911 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
1912 notify_email = Config.get([:instance, :notify_email])
1913 instance_name = Config.get([:instance, :name])
1914
1915 assert_email_sent(
1916 from: {instance_name, notify_email},
1917 to: {user.name, user.email},
1918 html_body: email.html_body
1919 )
1920 end
1921 end
1922
1923 describe "POST /auth/password, with invalid parameters" do
1924 setup do
1925 user = insert(:user)
1926 {:ok, user: user}
1927 end
1928
1929 test "it returns 404 when user is not found", %{conn: conn, user: user} do
1930 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
1931 assert conn.status == 404
1932 assert conn.resp_body == ""
1933 end
1934
1935 test "it returns 400 when user is not local", %{conn: conn, user: user} do
1936 {:ok, user} = Repo.update(Changeset.change(user, local: false))
1937 conn = post(conn, "/auth/password?email=#{user.email}")
1938 assert conn.status == 400
1939 assert conn.resp_body == ""
1940 end
1941 end
1942
1943 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
1944 setup do
1945 {:ok, user} =
1946 insert(:user)
1947 |> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
1948 |> Repo.update()
1949
1950 assert user.info.confirmation_pending
1951
1952 [user: user]
1953 end
1954
1955 clear_config([:instance, :account_activation_required]) do
1956 Config.put([:instance, :account_activation_required], true)
1957 end
1958
1959 test "resend account confirmation email", %{conn: conn, user: user} do
1960 conn
1961 |> assign(:user, user)
1962 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
1963 |> json_response(:no_content)
1964
1965 ObanHelpers.perform_all()
1966
1967 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
1968 notify_email = Config.get([:instance, :notify_email])
1969 instance_name = Config.get([:instance, :name])
1970
1971 assert_email_sent(
1972 from: {instance_name, notify_email},
1973 to: {user.name, user.email},
1974 html_body: email.html_body
1975 )
1976 end
1977 end
1978
1979 describe "GET /api/v1/suggestions" do
1980 setup do
1981 user = insert(:user)
1982 other_user = insert(:user)
1983 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
1984 url500 = "http://test500?#{host}&#{user.nickname}"
1985 url200 = "http://test200?#{host}&#{user.nickname}"
1986
1987 mock(fn
1988 %{method: :get, url: ^url500} ->
1989 %Tesla.Env{status: 500, body: "bad request"}
1990
1991 %{method: :get, url: ^url200} ->
1992 %Tesla.Env{
1993 status: 200,
1994 body:
1995 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
1996 other_user.ap_id
1997 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
1998 }
1999 end)
2000
2001 [user: user, other_user: other_user]
2002 end
2003
2004 clear_config(:suggestions)
2005
2006 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
2007 Config.put([:suggestions, :enabled], false)
2008
2009 res =
2010 conn
2011 |> assign(:user, user)
2012 |> get("/api/v1/suggestions")
2013 |> json_response(200)
2014
2015 assert res == []
2016 end
2017
2018 test "returns error", %{conn: conn, user: user} do
2019 Config.put([:suggestions, :enabled], true)
2020 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
2021
2022 assert capture_log(fn ->
2023 res =
2024 conn
2025 |> assign(:user, user)
2026 |> get("/api/v1/suggestions")
2027 |> json_response(500)
2028
2029 assert res == "Something went wrong"
2030 end) =~ "Could not retrieve suggestions"
2031 end
2032
2033 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
2034 Config.put([:suggestions, :enabled], true)
2035 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
2036
2037 res =
2038 conn
2039 |> assign(:user, user)
2040 |> get("/api/v1/suggestions")
2041 |> json_response(200)
2042
2043 assert res == [
2044 %{
2045 "acct" => "yj455",
2046 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
2047 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
2048 "id" => 0
2049 },
2050 %{
2051 "acct" => other_user.ap_id,
2052 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
2053 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
2054 "id" => other_user.id
2055 }
2056 ]
2057 end
2058 end
2059
2060 describe "PUT /api/v1/media/:id" do
2061 setup do
2062 actor = insert(:user)
2063
2064 file = %Plug.Upload{
2065 content_type: "image/jpg",
2066 path: Path.absname("test/fixtures/image.jpg"),
2067 filename: "an_image.jpg"
2068 }
2069
2070 {:ok, %Object{} = object} =
2071 ActivityPub.upload(
2072 file,
2073 actor: User.ap_id(actor),
2074 description: "test-m"
2075 )
2076
2077 [actor: actor, object: object]
2078 end
2079
2080 test "updates name of media", %{conn: conn, actor: actor, object: object} do
2081 media =
2082 conn
2083 |> assign(:user, actor)
2084 |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"})
2085 |> json_response(:ok)
2086
2087 assert media["description"] == "test-media"
2088 assert refresh_record(object).data["name"] == "test-media"
2089 end
2090
2091 test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do
2092 media =
2093 conn
2094 |> assign(:user, actor)
2095 |> put("/api/v1/media/#{object.id}", %{})
2096 |> json_response(400)
2097
2098 assert media == %{"error" => "bad_request"}
2099 end
2100 end
2101
2102 describe "DELETE /auth/sign_out" do
2103 test "redirect to root page", %{conn: conn} do
2104 user = insert(:user)
2105
2106 conn =
2107 conn
2108 |> assign(:user, user)
2109 |> delete("/auth/sign_out")
2110
2111 assert conn.status == 302
2112 assert redirected_to(conn) == "/"
2113 end
2114 end
2115
2116 describe "GET /api/v1/accounts/:id/lists - account_lists" do
2117 test "returns lists to which the account belongs", %{conn: conn} do
2118 user = insert(:user)
2119 other_user = insert(:user)
2120 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
2121 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
2122
2123 res =
2124 conn
2125 |> assign(:user, user)
2126 |> get("/api/v1/accounts/#{other_user.id}/lists")
2127 |> json_response(200)
2128
2129 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
2130 end
2131 end
2132
2133 describe "empty_array, stubs for mastodon api" do
2134 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
2135 user = insert(:user)
2136
2137 res =
2138 conn
2139 |> assign(:user, user)
2140 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
2141 |> json_response(200)
2142
2143 assert res == []
2144 end
2145
2146 test "GET /api/v1/endorsements", %{conn: conn} do
2147 user = insert(:user)
2148
2149 res =
2150 conn
2151 |> assign(:user, user)
2152 |> get("/api/v1/endorsements")
2153 |> json_response(200)
2154
2155 assert res == []
2156 end
2157
2158 test "GET /api/v1/trends", %{conn: conn} do
2159 user = insert(:user)
2160
2161 res =
2162 conn
2163 |> assign(:user, user)
2164 |> get("/api/v1/trends")
2165 |> json_response(200)
2166
2167 assert res == []
2168 end
2169 end
2170 end