Merge branch 'develop' into 'remove-avatar-header'
[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.Activity
10 alias Pleroma.Notification
11 alias Pleroma.Object
12 alias Pleroma.Repo
13 alias Pleroma.ScheduledActivity
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.MastodonAPI.FilterView
18 alias Pleroma.Web.OAuth.App
19 alias Pleroma.Web.OAuth.Token
20 alias Pleroma.Web.OStatus
21 alias Pleroma.Web.Push
22 alias Pleroma.Web.TwitterAPI.TwitterAPI
23 import Pleroma.Factory
24 import ExUnit.CaptureLog
25 import Tesla.Mock
26
27 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
28
29 setup do
30 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
31 :ok
32 end
33
34 test "the home timeline", %{conn: conn} do
35 user = insert(:user)
36 following = insert(:user)
37
38 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
39
40 conn =
41 conn
42 |> assign(:user, user)
43 |> get("/api/v1/timelines/home")
44
45 assert Enum.empty?(json_response(conn, 200))
46
47 {:ok, user} = User.follow(user, following)
48
49 conn =
50 build_conn()
51 |> assign(:user, user)
52 |> get("/api/v1/timelines/home")
53
54 assert [%{"content" => "test"}] = json_response(conn, 200)
55 end
56
57 test "the public timeline", %{conn: conn} do
58 following = insert(:user)
59
60 capture_log(fn ->
61 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
62
63 {:ok, [_activity]} =
64 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
65
66 conn =
67 conn
68 |> get("/api/v1/timelines/public", %{"local" => "False"})
69
70 assert length(json_response(conn, 200)) == 2
71
72 conn =
73 build_conn()
74 |> get("/api/v1/timelines/public", %{"local" => "True"})
75
76 assert [%{"content" => "test"}] = json_response(conn, 200)
77
78 conn =
79 build_conn()
80 |> get("/api/v1/timelines/public", %{"local" => "1"})
81
82 assert [%{"content" => "test"}] = json_response(conn, 200)
83 end)
84 end
85
86 test "the public timeline when public is set to false", %{conn: conn} do
87 public = Pleroma.Config.get([:instance, :public])
88 Pleroma.Config.put([:instance, :public], false)
89
90 on_exit(fn ->
91 Pleroma.Config.put([:instance, :public], public)
92 end)
93
94 assert conn
95 |> get("/api/v1/timelines/public", %{"local" => "False"})
96 |> json_response(403) == %{"error" => "This resource requires authentication."}
97 end
98
99 test "posting a status", %{conn: conn} do
100 user = insert(:user)
101
102 idempotency_key = "Pikachu rocks!"
103
104 conn_one =
105 conn
106 |> assign(:user, user)
107 |> put_req_header("idempotency-key", idempotency_key)
108 |> post("/api/v1/statuses", %{
109 "status" => "cofe",
110 "spoiler_text" => "2hu",
111 "sensitive" => "false"
112 })
113
114 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
115 # Six hours
116 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
117
118 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
119 json_response(conn_one, 200)
120
121 assert Activity.get_by_id(id)
122
123 conn_two =
124 conn
125 |> assign(:user, user)
126 |> put_req_header("idempotency-key", idempotency_key)
127 |> post("/api/v1/statuses", %{
128 "status" => "cofe",
129 "spoiler_text" => "2hu",
130 "sensitive" => "false"
131 })
132
133 assert %{"id" => second_id} = json_response(conn_two, 200)
134
135 assert id == second_id
136
137 conn_three =
138 conn
139 |> assign(:user, user)
140 |> post("/api/v1/statuses", %{
141 "status" => "cofe",
142 "spoiler_text" => "2hu",
143 "sensitive" => "false"
144 })
145
146 assert %{"id" => third_id} = json_response(conn_three, 200)
147
148 refute id == third_id
149 end
150
151 describe "posting polls" do
152 test "posting a poll", %{conn: conn} do
153 user = insert(:user)
154 time = NaiveDateTime.utc_now()
155
156 conn =
157 conn
158 |> assign(:user, user)
159 |> post("/api/v1/statuses", %{
160 "status" => "Who is the #bestgrill?",
161 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
162 })
163
164 response = json_response(conn, 200)
165
166 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
167 title in ["Rei", "Asuka", "Misato"]
168 end)
169
170 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
171 refute response["poll"]["expred"]
172 end
173
174 test "option limit is enforced", %{conn: conn} do
175 user = insert(:user)
176 limit = Pleroma.Config.get([:instance, :poll_limits, :max_options])
177
178 conn =
179 conn
180 |> assign(:user, user)
181 |> post("/api/v1/statuses", %{
182 "status" => "desu~",
183 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
184 })
185
186 %{"error" => error} = json_response(conn, 422)
187 assert error == "Poll can't contain more than #{limit} options"
188 end
189
190 test "option character limit is enforced", %{conn: conn} do
191 user = insert(:user)
192 limit = Pleroma.Config.get([:instance, :poll_limits, :max_option_chars])
193
194 conn =
195 conn
196 |> assign(:user, user)
197 |> post("/api/v1/statuses", %{
198 "status" => "...",
199 "poll" => %{
200 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
201 "expires_in" => 1
202 }
203 })
204
205 %{"error" => error} = json_response(conn, 422)
206 assert error == "Poll options cannot be longer than #{limit} characters each"
207 end
208
209 test "minimal date limit is enforced", %{conn: conn} do
210 user = insert(:user)
211 limit = Pleroma.Config.get([:instance, :poll_limits, :min_expiration])
212
213 conn =
214 conn
215 |> assign(:user, user)
216 |> post("/api/v1/statuses", %{
217 "status" => "imagine arbitrary limits",
218 "poll" => %{
219 "options" => ["this post was made by pleroma gang"],
220 "expires_in" => limit - 1
221 }
222 })
223
224 %{"error" => error} = json_response(conn, 422)
225 assert error == "Expiration date is too soon"
226 end
227
228 test "maximum date limit is enforced", %{conn: conn} do
229 user = insert(:user)
230 limit = Pleroma.Config.get([:instance, :poll_limits, :max_expiration])
231
232 conn =
233 conn
234 |> assign(:user, user)
235 |> post("/api/v1/statuses", %{
236 "status" => "imagine arbitrary limits",
237 "poll" => %{
238 "options" => ["this post was made by pleroma gang"],
239 "expires_in" => limit + 1
240 }
241 })
242
243 %{"error" => error} = json_response(conn, 422)
244 assert error == "Expiration date is too far in the future"
245 end
246 end
247
248 test "posting a sensitive status", %{conn: conn} do
249 user = insert(:user)
250
251 conn =
252 conn
253 |> assign(:user, user)
254 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
255
256 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
257 assert Activity.get_by_id(id)
258 end
259
260 test "posting a fake status", %{conn: conn} do
261 user = insert(:user)
262
263 real_conn =
264 conn
265 |> assign(:user, user)
266 |> post("/api/v1/statuses", %{
267 "status" =>
268 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
269 })
270
271 real_status = json_response(real_conn, 200)
272
273 assert real_status
274 assert Object.get_by_ap_id(real_status["uri"])
275
276 real_status =
277 real_status
278 |> Map.put("id", nil)
279 |> Map.put("url", nil)
280 |> Map.put("uri", nil)
281 |> Map.put("created_at", nil)
282 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
283
284 fake_conn =
285 conn
286 |> assign(:user, user)
287 |> post("/api/v1/statuses", %{
288 "status" =>
289 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
290 "preview" => true
291 })
292
293 fake_status = json_response(fake_conn, 200)
294
295 assert fake_status
296 refute Object.get_by_ap_id(fake_status["uri"])
297
298 fake_status =
299 fake_status
300 |> Map.put("id", nil)
301 |> Map.put("url", nil)
302 |> Map.put("uri", nil)
303 |> Map.put("created_at", nil)
304 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
305
306 assert real_status == fake_status
307 end
308
309 test "posting a status with OGP link preview", %{conn: conn} do
310 Pleroma.Config.put([:rich_media, :enabled], true)
311 user = insert(:user)
312
313 conn =
314 conn
315 |> assign(:user, user)
316 |> post("/api/v1/statuses", %{
317 "status" => "http://example.com/ogp"
318 })
319
320 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
321 assert Activity.get_by_id(id)
322 Pleroma.Config.put([:rich_media, :enabled], false)
323 end
324
325 test "posting a direct status", %{conn: conn} do
326 user1 = insert(:user)
327 user2 = insert(:user)
328 content = "direct cofe @#{user2.nickname}"
329
330 conn =
331 conn
332 |> assign(:user, user1)
333 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
334
335 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
336 assert activity = Activity.get_by_id(id)
337 assert activity.recipients == [user2.ap_id, user1.ap_id]
338 assert activity.data["to"] == [user2.ap_id]
339 assert activity.data["cc"] == []
340 end
341
342 test "direct timeline", %{conn: conn} do
343 user_one = insert(:user)
344 user_two = insert(:user)
345
346 {:ok, user_two} = User.follow(user_two, user_one)
347
348 {:ok, direct} =
349 CommonAPI.post(user_one, %{
350 "status" => "Hi @#{user_two.nickname}!",
351 "visibility" => "direct"
352 })
353
354 {:ok, _follower_only} =
355 CommonAPI.post(user_one, %{
356 "status" => "Hi @#{user_two.nickname}!",
357 "visibility" => "private"
358 })
359
360 # Only direct should be visible here
361 res_conn =
362 conn
363 |> assign(:user, user_two)
364 |> get("api/v1/timelines/direct")
365
366 [status] = json_response(res_conn, 200)
367
368 assert %{"visibility" => "direct"} = status
369 assert status["url"] != direct.data["id"]
370
371 # User should be able to see his own direct message
372 res_conn =
373 build_conn()
374 |> assign(:user, user_one)
375 |> get("api/v1/timelines/direct")
376
377 [status] = json_response(res_conn, 200)
378
379 assert %{"visibility" => "direct"} = status
380
381 # Both should be visible here
382 res_conn =
383 conn
384 |> assign(:user, user_two)
385 |> get("api/v1/timelines/home")
386
387 [_s1, _s2] = json_response(res_conn, 200)
388
389 # Test pagination
390 Enum.each(1..20, fn _ ->
391 {:ok, _} =
392 CommonAPI.post(user_one, %{
393 "status" => "Hi @#{user_two.nickname}!",
394 "visibility" => "direct"
395 })
396 end)
397
398 res_conn =
399 conn
400 |> assign(:user, user_two)
401 |> get("api/v1/timelines/direct")
402
403 statuses = json_response(res_conn, 200)
404 assert length(statuses) == 20
405
406 res_conn =
407 conn
408 |> assign(:user, user_two)
409 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
410
411 [status] = json_response(res_conn, 200)
412
413 assert status["url"] != direct.data["id"]
414 end
415
416 test "Conversations", %{conn: conn} do
417 user_one = insert(:user)
418 user_two = insert(:user)
419 user_three = insert(:user)
420
421 {:ok, user_two} = User.follow(user_two, user_one)
422
423 {:ok, direct} =
424 CommonAPI.post(user_one, %{
425 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
426 "visibility" => "direct"
427 })
428
429 {:ok, _follower_only} =
430 CommonAPI.post(user_one, %{
431 "status" => "Hi @#{user_two.nickname}!",
432 "visibility" => "private"
433 })
434
435 res_conn =
436 conn
437 |> assign(:user, user_one)
438 |> get("/api/v1/conversations")
439
440 assert response = json_response(res_conn, 200)
441
442 assert [
443 %{
444 "id" => res_id,
445 "accounts" => res_accounts,
446 "last_status" => res_last_status,
447 "unread" => unread
448 }
449 ] = response
450
451 account_ids = Enum.map(res_accounts, & &1["id"])
452 assert length(res_accounts) == 2
453 assert user_two.id in account_ids
454 assert user_three.id in account_ids
455 assert is_binary(res_id)
456 assert unread == true
457 assert res_last_status["id"] == direct.id
458
459 # Apparently undocumented API endpoint
460 res_conn =
461 conn
462 |> assign(:user, user_one)
463 |> post("/api/v1/conversations/#{res_id}/read")
464
465 assert response = json_response(res_conn, 200)
466 assert length(response["accounts"]) == 2
467 assert response["last_status"]["id"] == direct.id
468 assert response["unread"] == false
469
470 # (vanilla) Mastodon frontend behaviour
471 res_conn =
472 conn
473 |> assign(:user, user_one)
474 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
475
476 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
477 end
478
479 test "doesn't include DMs from blocked users", %{conn: conn} do
480 blocker = insert(:user)
481 blocked = insert(:user)
482 user = insert(:user)
483 {:ok, blocker} = User.block(blocker, blocked)
484
485 {:ok, _blocked_direct} =
486 CommonAPI.post(blocked, %{
487 "status" => "Hi @#{blocker.nickname}!",
488 "visibility" => "direct"
489 })
490
491 {:ok, direct} =
492 CommonAPI.post(user, %{
493 "status" => "Hi @#{blocker.nickname}!",
494 "visibility" => "direct"
495 })
496
497 res_conn =
498 conn
499 |> assign(:user, user)
500 |> get("api/v1/timelines/direct")
501
502 [status] = json_response(res_conn, 200)
503 assert status["id"] == direct.id
504 end
505
506 test "replying to a status", %{conn: conn} do
507 user = insert(:user)
508
509 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
510
511 conn =
512 conn
513 |> assign(:user, user)
514 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
515
516 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
517
518 activity = Activity.get_by_id(id)
519
520 assert activity.data["context"] == replied_to.data["context"]
521 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
522 end
523
524 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
525 user = insert(:user)
526
527 conn =
528 conn
529 |> assign(:user, user)
530 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
531
532 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
533
534 activity = Activity.get_by_id(id)
535
536 assert activity
537 end
538
539 test "verify_credentials", %{conn: conn} do
540 user = insert(:user)
541
542 conn =
543 conn
544 |> assign(:user, user)
545 |> get("/api/v1/accounts/verify_credentials")
546
547 response = json_response(conn, 200)
548
549 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
550 assert response["pleroma"]["chat_token"]
551 assert id == to_string(user.id)
552 end
553
554 test "verify_credentials default scope unlisted", %{conn: conn} do
555 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
556
557 conn =
558 conn
559 |> assign(:user, user)
560 |> get("/api/v1/accounts/verify_credentials")
561
562 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
563 assert id == to_string(user.id)
564 end
565
566 test "apps/verify_credentials", %{conn: conn} do
567 token = insert(:oauth_token)
568
569 conn =
570 conn
571 |> assign(:user, token.user)
572 |> assign(:token, token)
573 |> get("/api/v1/apps/verify_credentials")
574
575 app = Repo.preload(token, :app).app
576
577 expected = %{
578 "name" => app.client_name,
579 "website" => app.website,
580 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
581 }
582
583 assert expected == json_response(conn, 200)
584 end
585
586 test "user avatar can be set", %{conn: conn} do
587 user = insert(:user)
588 avatar_image = File.read!("test/fixtures/avatar_data_uri")
589
590 conn =
591 conn
592 |> assign(:user, user)
593 |> patch("/api/v1/accounts/update_avatar", %{img: avatar_image})
594
595 user = refresh_record(user)
596
597 assert %{
598 "name" => _,
599 "type" => _,
600 "url" => [
601 %{
602 "href" => _,
603 "mediaType" => _,
604 "type" => _
605 }
606 ]
607 } = user.avatar
608
609 assert %{"url" => _} = json_response(conn, 200)
610 end
611
612 test "user avatar can be reset", %{conn: conn} do
613 user = insert(:user)
614
615 conn =
616 conn
617 |> assign(:user, user)
618 |> patch("/api/v1/accounts/update_avatar", %{img: ""})
619
620 user = User.get_cached_by_id(user.id)
621
622 assert user.avatar == nil
623
624 assert %{"url" => nil} = json_response(conn, 200)
625 end
626
627 test "can set profile banner", %{conn: conn} do
628 user = insert(:user)
629
630 conn =
631 conn
632 |> assign(:user, user)
633 |> patch("/api/v1/accounts/update_banner", %{"banner" => @image})
634
635 user = refresh_record(user)
636 assert user.info.banner["type"] == "Image"
637
638 assert %{"url" => _} = json_response(conn, 200)
639 end
640
641 test "can reset profile banner", %{conn: conn} do
642 user = insert(:user)
643
644 conn =
645 conn
646 |> assign(:user, user)
647 |> patch("/api/v1/accounts/update_banner", %{"banner" => ""})
648
649 user = refresh_record(user)
650 assert user.info.banner == %{}
651
652 assert %{"url" => nil} = json_response(conn, 200)
653 end
654
655 test "background image can be set", %{conn: conn} do
656 user = insert(:user)
657
658 conn =
659 conn
660 |> assign(:user, user)
661 |> patch("/api/v1/accounts/update_background", %{"img" => @image})
662
663 user = refresh_record(user)
664 assert user.info.background["type"] == "Image"
665 assert %{"url" => _} = json_response(conn, 200)
666 end
667
668 test "background image can be reset", %{conn: conn} do
669 user = insert(:user)
670
671 conn =
672 conn
673 |> assign(:user, user)
674 |> patch("/api/v1/accounts/update_background", %{"img" => ""})
675
676 user = refresh_record(user)
677 assert user.info.background == %{}
678 assert %{"url" => nil} = json_response(conn, 200)
679 end
680
681 test "creates an oauth app", %{conn: conn} do
682 user = insert(:user)
683 app_attrs = build(:oauth_app)
684
685 conn =
686 conn
687 |> assign(:user, user)
688 |> post("/api/v1/apps", %{
689 client_name: app_attrs.client_name,
690 redirect_uris: app_attrs.redirect_uris
691 })
692
693 [app] = Repo.all(App)
694
695 expected = %{
696 "name" => app.client_name,
697 "website" => app.website,
698 "client_id" => app.client_id,
699 "client_secret" => app.client_secret,
700 "id" => app.id |> to_string(),
701 "redirect_uri" => app.redirect_uris,
702 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
703 }
704
705 assert expected == json_response(conn, 200)
706 end
707
708 test "get a status", %{conn: conn} do
709 activity = insert(:note_activity)
710
711 conn =
712 conn
713 |> get("/api/v1/statuses/#{activity.id}")
714
715 assert %{"id" => id} = json_response(conn, 200)
716 assert id == to_string(activity.id)
717 end
718
719 describe "deleting a status" do
720 test "when you created it", %{conn: conn} do
721 activity = insert(:note_activity)
722 author = User.get_cached_by_ap_id(activity.data["actor"])
723
724 conn =
725 conn
726 |> assign(:user, author)
727 |> delete("/api/v1/statuses/#{activity.id}")
728
729 assert %{} = json_response(conn, 200)
730
731 refute Activity.get_by_id(activity.id)
732 end
733
734 test "when you didn't create it", %{conn: conn} do
735 activity = insert(:note_activity)
736 user = insert(:user)
737
738 conn =
739 conn
740 |> assign(:user, user)
741 |> delete("/api/v1/statuses/#{activity.id}")
742
743 assert %{"error" => _} = json_response(conn, 403)
744
745 assert Activity.get_by_id(activity.id) == activity
746 end
747
748 test "when you're an admin or moderator", %{conn: conn} do
749 activity1 = insert(:note_activity)
750 activity2 = insert(:note_activity)
751 admin = insert(:user, info: %{is_admin: true})
752 moderator = insert(:user, info: %{is_moderator: true})
753
754 res_conn =
755 conn
756 |> assign(:user, admin)
757 |> delete("/api/v1/statuses/#{activity1.id}")
758
759 assert %{} = json_response(res_conn, 200)
760
761 res_conn =
762 conn
763 |> assign(:user, moderator)
764 |> delete("/api/v1/statuses/#{activity2.id}")
765
766 assert %{} = json_response(res_conn, 200)
767
768 refute Activity.get_by_id(activity1.id)
769 refute Activity.get_by_id(activity2.id)
770 end
771 end
772
773 describe "filters" do
774 test "creating a filter", %{conn: conn} do
775 user = insert(:user)
776
777 filter = %Pleroma.Filter{
778 phrase: "knights",
779 context: ["home"]
780 }
781
782 conn =
783 conn
784 |> assign(:user, user)
785 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
786
787 assert response = json_response(conn, 200)
788 assert response["phrase"] == filter.phrase
789 assert response["context"] == filter.context
790 assert response["irreversible"] == false
791 assert response["id"] != nil
792 assert response["id"] != ""
793 end
794
795 test "fetching a list of filters", %{conn: conn} do
796 user = insert(:user)
797
798 query_one = %Pleroma.Filter{
799 user_id: user.id,
800 filter_id: 1,
801 phrase: "knights",
802 context: ["home"]
803 }
804
805 query_two = %Pleroma.Filter{
806 user_id: user.id,
807 filter_id: 2,
808 phrase: "who",
809 context: ["home"]
810 }
811
812 {:ok, filter_one} = Pleroma.Filter.create(query_one)
813 {:ok, filter_two} = Pleroma.Filter.create(query_two)
814
815 response =
816 conn
817 |> assign(:user, user)
818 |> get("/api/v1/filters")
819 |> json_response(200)
820
821 assert response ==
822 render_json(
823 FilterView,
824 "filters.json",
825 filters: [filter_two, filter_one]
826 )
827 end
828
829 test "get a filter", %{conn: conn} do
830 user = insert(:user)
831
832 query = %Pleroma.Filter{
833 user_id: user.id,
834 filter_id: 2,
835 phrase: "knight",
836 context: ["home"]
837 }
838
839 {:ok, filter} = Pleroma.Filter.create(query)
840
841 conn =
842 conn
843 |> assign(:user, user)
844 |> get("/api/v1/filters/#{filter.filter_id}")
845
846 assert _response = json_response(conn, 200)
847 end
848
849 test "update a filter", %{conn: conn} do
850 user = insert(:user)
851
852 query = %Pleroma.Filter{
853 user_id: user.id,
854 filter_id: 2,
855 phrase: "knight",
856 context: ["home"]
857 }
858
859 {:ok, _filter} = Pleroma.Filter.create(query)
860
861 new = %Pleroma.Filter{
862 phrase: "nii",
863 context: ["home"]
864 }
865
866 conn =
867 conn
868 |> assign(:user, user)
869 |> put("/api/v1/filters/#{query.filter_id}", %{
870 phrase: new.phrase,
871 context: new.context
872 })
873
874 assert response = json_response(conn, 200)
875 assert response["phrase"] == new.phrase
876 assert response["context"] == new.context
877 end
878
879 test "delete a filter", %{conn: conn} do
880 user = insert(:user)
881
882 query = %Pleroma.Filter{
883 user_id: user.id,
884 filter_id: 2,
885 phrase: "knight",
886 context: ["home"]
887 }
888
889 {:ok, filter} = Pleroma.Filter.create(query)
890
891 conn =
892 conn
893 |> assign(:user, user)
894 |> delete("/api/v1/filters/#{filter.filter_id}")
895
896 assert response = json_response(conn, 200)
897 assert response == %{}
898 end
899 end
900
901 describe "lists" do
902 test "creating a list", %{conn: conn} do
903 user = insert(:user)
904
905 conn =
906 conn
907 |> assign(:user, user)
908 |> post("/api/v1/lists", %{"title" => "cuties"})
909
910 assert %{"title" => title} = json_response(conn, 200)
911 assert title == "cuties"
912 end
913
914 test "adding users to a list", %{conn: conn} do
915 user = insert(:user)
916 other_user = insert(:user)
917 {:ok, list} = Pleroma.List.create("name", user)
918
919 conn =
920 conn
921 |> assign(:user, user)
922 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
923
924 assert %{} == json_response(conn, 200)
925 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
926 assert following == [other_user.follower_address]
927 end
928
929 test "removing users from a list", %{conn: conn} do
930 user = insert(:user)
931 other_user = insert(:user)
932 third_user = insert(:user)
933 {:ok, list} = Pleroma.List.create("name", user)
934 {:ok, list} = Pleroma.List.follow(list, other_user)
935 {:ok, list} = Pleroma.List.follow(list, third_user)
936
937 conn =
938 conn
939 |> assign(:user, user)
940 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
941
942 assert %{} == json_response(conn, 200)
943 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
944 assert following == [third_user.follower_address]
945 end
946
947 test "listing users in a list", %{conn: conn} do
948 user = insert(:user)
949 other_user = insert(:user)
950 {:ok, list} = Pleroma.List.create("name", user)
951 {:ok, list} = Pleroma.List.follow(list, other_user)
952
953 conn =
954 conn
955 |> assign(:user, user)
956 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
957
958 assert [%{"id" => id}] = json_response(conn, 200)
959 assert id == to_string(other_user.id)
960 end
961
962 test "retrieving a list", %{conn: conn} do
963 user = insert(:user)
964 {:ok, list} = Pleroma.List.create("name", user)
965
966 conn =
967 conn
968 |> assign(:user, user)
969 |> get("/api/v1/lists/#{list.id}")
970
971 assert %{"id" => id} = json_response(conn, 200)
972 assert id == to_string(list.id)
973 end
974
975 test "renaming a list", %{conn: conn} do
976 user = insert(:user)
977 {:ok, list} = Pleroma.List.create("name", user)
978
979 conn =
980 conn
981 |> assign(:user, user)
982 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
983
984 assert %{"title" => name} = json_response(conn, 200)
985 assert name == "newname"
986 end
987
988 test "deleting a list", %{conn: conn} do
989 user = insert(:user)
990 {:ok, list} = Pleroma.List.create("name", user)
991
992 conn =
993 conn
994 |> assign(:user, user)
995 |> delete("/api/v1/lists/#{list.id}")
996
997 assert %{} = json_response(conn, 200)
998 assert is_nil(Repo.get(Pleroma.List, list.id))
999 end
1000
1001 test "list timeline", %{conn: conn} do
1002 user = insert(:user)
1003 other_user = insert(:user)
1004 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
1005 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
1006 {:ok, list} = Pleroma.List.create("name", user)
1007 {:ok, list} = Pleroma.List.follow(list, other_user)
1008
1009 conn =
1010 conn
1011 |> assign(:user, user)
1012 |> get("/api/v1/timelines/list/#{list.id}")
1013
1014 assert [%{"id" => id}] = json_response(conn, 200)
1015
1016 assert id == to_string(activity_two.id)
1017 end
1018
1019 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
1020 user = insert(:user)
1021 other_user = insert(:user)
1022 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
1023
1024 {:ok, _activity_two} =
1025 TwitterAPI.create_status(other_user, %{
1026 "status" => "Marisa is cute.",
1027 "visibility" => "private"
1028 })
1029
1030 {:ok, list} = Pleroma.List.create("name", user)
1031 {:ok, list} = Pleroma.List.follow(list, other_user)
1032
1033 conn =
1034 conn
1035 |> assign(:user, user)
1036 |> get("/api/v1/timelines/list/#{list.id}")
1037
1038 assert [%{"id" => id}] = json_response(conn, 200)
1039
1040 assert id == to_string(activity_one.id)
1041 end
1042 end
1043
1044 describe "notifications" do
1045 test "list of notifications", %{conn: conn} do
1046 user = insert(:user)
1047 other_user = insert(:user)
1048
1049 {:ok, activity} =
1050 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
1051
1052 {:ok, [_notification]} = Notification.create_notifications(activity)
1053
1054 conn =
1055 conn
1056 |> assign(:user, user)
1057 |> get("/api/v1/notifications")
1058
1059 expected_response =
1060 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1061 user.ap_id
1062 }\">@<span>#{user.nickname}</span></a></span>"
1063
1064 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1065 assert response == expected_response
1066 end
1067
1068 test "getting a single notification", %{conn: conn} do
1069 user = insert(:user)
1070 other_user = insert(:user)
1071
1072 {:ok, activity} =
1073 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
1074
1075 {:ok, [notification]} = Notification.create_notifications(activity)
1076
1077 conn =
1078 conn
1079 |> assign(:user, user)
1080 |> get("/api/v1/notifications/#{notification.id}")
1081
1082 expected_response =
1083 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1084 user.ap_id
1085 }\">@<span>#{user.nickname}</span></a></span>"
1086
1087 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1088 assert response == expected_response
1089 end
1090
1091 test "dismissing a single notification", %{conn: conn} do
1092 user = insert(:user)
1093 other_user = insert(:user)
1094
1095 {:ok, activity} =
1096 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
1097
1098 {:ok, [notification]} = Notification.create_notifications(activity)
1099
1100 conn =
1101 conn
1102 |> assign(:user, user)
1103 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1104
1105 assert %{} = json_response(conn, 200)
1106 end
1107
1108 test "clearing all notifications", %{conn: conn} do
1109 user = insert(:user)
1110 other_user = insert(:user)
1111
1112 {:ok, activity} =
1113 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
1114
1115 {:ok, [_notification]} = Notification.create_notifications(activity)
1116
1117 conn =
1118 conn
1119 |> assign(:user, user)
1120 |> post("/api/v1/notifications/clear")
1121
1122 assert %{} = json_response(conn, 200)
1123
1124 conn =
1125 build_conn()
1126 |> assign(:user, user)
1127 |> get("/api/v1/notifications")
1128
1129 assert all = json_response(conn, 200)
1130 assert all == []
1131 end
1132
1133 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1134 user = insert(:user)
1135 other_user = insert(:user)
1136
1137 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1138 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1139 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1140 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1141
1142 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1143 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1144 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1145 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1146
1147 conn =
1148 conn
1149 |> assign(:user, user)
1150
1151 # min_id
1152 conn_res =
1153 conn
1154 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1155
1156 result = json_response(conn_res, 200)
1157 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1158
1159 # since_id
1160 conn_res =
1161 conn
1162 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1163
1164 result = json_response(conn_res, 200)
1165 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1166
1167 # max_id
1168 conn_res =
1169 conn
1170 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1171
1172 result = json_response(conn_res, 200)
1173 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1174 end
1175
1176 test "filters notifications using exclude_types", %{conn: conn} do
1177 user = insert(:user)
1178 other_user = insert(:user)
1179
1180 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1181 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1182 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1183 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1184 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1185
1186 mention_notification_id =
1187 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1188
1189 favorite_notification_id =
1190 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1191
1192 reblog_notification_id =
1193 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1194
1195 follow_notification_id =
1196 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1197
1198 conn =
1199 conn
1200 |> assign(:user, user)
1201
1202 conn_res =
1203 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1204
1205 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1206
1207 conn_res =
1208 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1209
1210 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1211
1212 conn_res =
1213 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1214
1215 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1216
1217 conn_res =
1218 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1219
1220 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1221 end
1222
1223 test "destroy multiple", %{conn: conn} do
1224 user = insert(:user)
1225 other_user = insert(:user)
1226
1227 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1228 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1229 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1230 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1231
1232 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1233 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1234 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1235 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1236
1237 conn =
1238 conn
1239 |> assign(:user, user)
1240
1241 conn_res =
1242 conn
1243 |> get("/api/v1/notifications")
1244
1245 result = json_response(conn_res, 200)
1246 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1247
1248 conn2 =
1249 conn
1250 |> assign(:user, other_user)
1251
1252 conn_res =
1253 conn2
1254 |> get("/api/v1/notifications")
1255
1256 result = json_response(conn_res, 200)
1257 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1258
1259 conn_destroy =
1260 conn
1261 |> delete("/api/v1/notifications/destroy_multiple", %{
1262 "ids" => [notification1_id, notification2_id]
1263 })
1264
1265 assert json_response(conn_destroy, 200) == %{}
1266
1267 conn_res =
1268 conn2
1269 |> get("/api/v1/notifications")
1270
1271 result = json_response(conn_res, 200)
1272 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1273 end
1274 end
1275
1276 describe "reblogging" do
1277 test "reblogs and returns the reblogged status", %{conn: conn} do
1278 activity = insert(:note_activity)
1279 user = insert(:user)
1280
1281 conn =
1282 conn
1283 |> assign(:user, user)
1284 |> post("/api/v1/statuses/#{activity.id}/reblog")
1285
1286 assert %{
1287 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1288 "reblogged" => true
1289 } = json_response(conn, 200)
1290
1291 assert to_string(activity.id) == id
1292 end
1293
1294 test "reblogged status for another user", %{conn: conn} do
1295 activity = insert(:note_activity)
1296 user1 = insert(:user)
1297 user2 = insert(:user)
1298 user3 = insert(:user)
1299 CommonAPI.favorite(activity.id, user2)
1300 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1301 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1302 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1303
1304 conn_res =
1305 conn
1306 |> assign(:user, user3)
1307 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1308
1309 assert %{
1310 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1311 "reblogged" => false,
1312 "favourited" => false,
1313 "bookmarked" => false
1314 } = json_response(conn_res, 200)
1315
1316 conn_res =
1317 conn
1318 |> assign(:user, user2)
1319 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1320
1321 assert %{
1322 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1323 "reblogged" => true,
1324 "favourited" => true,
1325 "bookmarked" => true
1326 } = json_response(conn_res, 200)
1327
1328 assert to_string(activity.id) == id
1329 end
1330 end
1331
1332 describe "unreblogging" do
1333 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1334 activity = insert(:note_activity)
1335 user = insert(:user)
1336
1337 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1338
1339 conn =
1340 conn
1341 |> assign(:user, user)
1342 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1343
1344 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1345
1346 assert to_string(activity.id) == id
1347 end
1348 end
1349
1350 describe "favoriting" do
1351 test "favs a status and returns it", %{conn: conn} do
1352 activity = insert(:note_activity)
1353 user = insert(:user)
1354
1355 conn =
1356 conn
1357 |> assign(:user, user)
1358 |> post("/api/v1/statuses/#{activity.id}/favourite")
1359
1360 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1361 json_response(conn, 200)
1362
1363 assert to_string(activity.id) == id
1364 end
1365
1366 test "returns 500 for a wrong id", %{conn: conn} do
1367 user = insert(:user)
1368
1369 resp =
1370 conn
1371 |> assign(:user, user)
1372 |> post("/api/v1/statuses/1/favourite")
1373 |> json_response(500)
1374
1375 assert resp == "Something went wrong"
1376 end
1377 end
1378
1379 describe "unfavoriting" do
1380 test "unfavorites a status and returns it", %{conn: conn} do
1381 activity = insert(:note_activity)
1382 user = insert(:user)
1383
1384 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1385
1386 conn =
1387 conn
1388 |> assign(:user, user)
1389 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1390
1391 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1392 json_response(conn, 200)
1393
1394 assert to_string(activity.id) == id
1395 end
1396 end
1397
1398 describe "user timelines" do
1399 test "gets a users statuses", %{conn: conn} do
1400 user_one = insert(:user)
1401 user_two = insert(:user)
1402 user_three = insert(:user)
1403
1404 {:ok, user_three} = User.follow(user_three, user_one)
1405
1406 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1407
1408 {:ok, direct_activity} =
1409 CommonAPI.post(user_one, %{
1410 "status" => "Hi, @#{user_two.nickname}.",
1411 "visibility" => "direct"
1412 })
1413
1414 {:ok, private_activity} =
1415 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1416
1417 resp =
1418 conn
1419 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1420
1421 assert [%{"id" => id}] = json_response(resp, 200)
1422 assert id == to_string(activity.id)
1423
1424 resp =
1425 conn
1426 |> assign(:user, user_two)
1427 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1428
1429 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1430 assert id_one == to_string(direct_activity.id)
1431 assert id_two == to_string(activity.id)
1432
1433 resp =
1434 conn
1435 |> assign(:user, user_three)
1436 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1437
1438 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1439 assert id_one == to_string(private_activity.id)
1440 assert id_two == to_string(activity.id)
1441 end
1442
1443 test "unimplemented pinned statuses feature", %{conn: conn} do
1444 note = insert(:note_activity)
1445 user = User.get_cached_by_ap_id(note.data["actor"])
1446
1447 conn =
1448 conn
1449 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1450
1451 assert json_response(conn, 200) == []
1452 end
1453
1454 test "gets an users media", %{conn: conn} do
1455 note = insert(:note_activity)
1456 user = User.get_cached_by_ap_id(note.data["actor"])
1457
1458 file = %Plug.Upload{
1459 content_type: "image/jpg",
1460 path: Path.absname("test/fixtures/image.jpg"),
1461 filename: "an_image.jpg"
1462 }
1463
1464 media =
1465 TwitterAPI.upload(file, user, "json")
1466 |> Poison.decode!()
1467
1468 {:ok, image_post} =
1469 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1470
1471 conn =
1472 conn
1473 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1474
1475 assert [%{"id" => id}] = json_response(conn, 200)
1476 assert id == to_string(image_post.id)
1477
1478 conn =
1479 build_conn()
1480 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1481
1482 assert [%{"id" => id}] = json_response(conn, 200)
1483 assert id == to_string(image_post.id)
1484 end
1485
1486 test "gets a user's statuses without reblogs", %{conn: conn} do
1487 user = insert(:user)
1488 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1489 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1490
1491 conn =
1492 conn
1493 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1494
1495 assert [%{"id" => id}] = json_response(conn, 200)
1496 assert id == to_string(post.id)
1497
1498 conn =
1499 conn
1500 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1501
1502 assert [%{"id" => id}] = json_response(conn, 200)
1503 assert id == to_string(post.id)
1504 end
1505 end
1506
1507 describe "user relationships" do
1508 test "returns the relationships for the current user", %{conn: conn} do
1509 user = insert(:user)
1510 other_user = insert(:user)
1511 {:ok, user} = User.follow(user, other_user)
1512
1513 conn =
1514 conn
1515 |> assign(:user, user)
1516 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1517
1518 assert [relationship] = json_response(conn, 200)
1519
1520 assert to_string(other_user.id) == relationship["id"]
1521 end
1522 end
1523
1524 describe "media upload" do
1525 setup do
1526 upload_config = Pleroma.Config.get([Pleroma.Upload])
1527 proxy_config = Pleroma.Config.get([:media_proxy])
1528
1529 on_exit(fn ->
1530 Pleroma.Config.put([Pleroma.Upload], upload_config)
1531 Pleroma.Config.put([:media_proxy], proxy_config)
1532 end)
1533
1534 user = insert(:user)
1535
1536 conn =
1537 build_conn()
1538 |> assign(:user, user)
1539
1540 image = %Plug.Upload{
1541 content_type: "image/jpg",
1542 path: Path.absname("test/fixtures/image.jpg"),
1543 filename: "an_image.jpg"
1544 }
1545
1546 [conn: conn, image: image]
1547 end
1548
1549 test "returns uploaded image", %{conn: conn, image: image} do
1550 desc = "Description of the image"
1551
1552 media =
1553 conn
1554 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1555 |> json_response(:ok)
1556
1557 assert media["type"] == "image"
1558 assert media["description"] == desc
1559 assert media["id"]
1560
1561 object = Repo.get(Object, media["id"])
1562 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1563 end
1564
1565 test "returns proxied url when media proxy is enabled", %{conn: conn, image: image} do
1566 Pleroma.Config.put([Pleroma.Upload, :base_url], "https://media.pleroma.social")
1567
1568 proxy_url = "https://cache.pleroma.social"
1569 Pleroma.Config.put([:media_proxy, :enabled], true)
1570 Pleroma.Config.put([:media_proxy, :base_url], proxy_url)
1571
1572 media =
1573 conn
1574 |> post("/api/v1/media", %{"file" => image})
1575 |> json_response(:ok)
1576
1577 assert String.starts_with?(media["url"], proxy_url)
1578 end
1579
1580 test "returns media url when proxy is enabled but media url is whitelisted", %{
1581 conn: conn,
1582 image: image
1583 } do
1584 media_url = "https://media.pleroma.social"
1585 Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
1586
1587 Pleroma.Config.put([:media_proxy, :enabled], true)
1588 Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
1589 Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
1590
1591 media =
1592 conn
1593 |> post("/api/v1/media", %{"file" => image})
1594 |> json_response(:ok)
1595
1596 assert String.starts_with?(media["url"], media_url)
1597 end
1598 end
1599
1600 describe "locked accounts" do
1601 test "/api/v1/follow_requests works" do
1602 user = insert(:user, %{info: %User.Info{locked: true}})
1603 other_user = insert(:user)
1604
1605 {:ok, _activity} = ActivityPub.follow(other_user, user)
1606
1607 user = User.get_cached_by_id(user.id)
1608 other_user = User.get_cached_by_id(other_user.id)
1609
1610 assert User.following?(other_user, user) == false
1611
1612 conn =
1613 build_conn()
1614 |> assign(:user, user)
1615 |> get("/api/v1/follow_requests")
1616
1617 assert [relationship] = json_response(conn, 200)
1618 assert to_string(other_user.id) == relationship["id"]
1619 end
1620
1621 test "/api/v1/follow_requests/:id/authorize works" do
1622 user = insert(:user, %{info: %User.Info{locked: true}})
1623 other_user = insert(:user)
1624
1625 {:ok, _activity} = ActivityPub.follow(other_user, user)
1626
1627 user = User.get_cached_by_id(user.id)
1628 other_user = User.get_cached_by_id(other_user.id)
1629
1630 assert User.following?(other_user, user) == false
1631
1632 conn =
1633 build_conn()
1634 |> assign(:user, user)
1635 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1636
1637 assert relationship = json_response(conn, 200)
1638 assert to_string(other_user.id) == relationship["id"]
1639
1640 user = User.get_cached_by_id(user.id)
1641 other_user = User.get_cached_by_id(other_user.id)
1642
1643 assert User.following?(other_user, user) == true
1644 end
1645
1646 test "verify_credentials", %{conn: conn} do
1647 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1648
1649 conn =
1650 conn
1651 |> assign(:user, user)
1652 |> get("/api/v1/accounts/verify_credentials")
1653
1654 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1655 assert id == to_string(user.id)
1656 end
1657
1658 test "/api/v1/follow_requests/:id/reject works" do
1659 user = insert(:user, %{info: %User.Info{locked: true}})
1660 other_user = insert(:user)
1661
1662 {:ok, _activity} = ActivityPub.follow(other_user, user)
1663
1664 user = User.get_cached_by_id(user.id)
1665
1666 conn =
1667 build_conn()
1668 |> assign(:user, user)
1669 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1670
1671 assert relationship = json_response(conn, 200)
1672 assert to_string(other_user.id) == relationship["id"]
1673
1674 user = User.get_cached_by_id(user.id)
1675 other_user = User.get_cached_by_id(other_user.id)
1676
1677 assert User.following?(other_user, user) == false
1678 end
1679 end
1680
1681 test "account fetching", %{conn: conn} do
1682 user = insert(:user)
1683
1684 conn =
1685 conn
1686 |> get("/api/v1/accounts/#{user.id}")
1687
1688 assert %{"id" => id} = json_response(conn, 200)
1689 assert id == to_string(user.id)
1690
1691 conn =
1692 build_conn()
1693 |> get("/api/v1/accounts/-1")
1694
1695 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1696 end
1697
1698 test "account fetching also works nickname", %{conn: conn} do
1699 user = insert(:user)
1700
1701 conn =
1702 conn
1703 |> get("/api/v1/accounts/#{user.nickname}")
1704
1705 assert %{"id" => id} = json_response(conn, 200)
1706 assert id == user.id
1707 end
1708
1709 test "mascot upload", %{conn: conn} do
1710 user = insert(:user)
1711
1712 non_image_file = %Plug.Upload{
1713 content_type: "audio/mpeg",
1714 path: Path.absname("test/fixtures/sound.mp3"),
1715 filename: "sound.mp3"
1716 }
1717
1718 conn =
1719 conn
1720 |> assign(:user, user)
1721 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1722
1723 assert json_response(conn, 415)
1724
1725 file = %Plug.Upload{
1726 content_type: "image/jpg",
1727 path: Path.absname("test/fixtures/image.jpg"),
1728 filename: "an_image.jpg"
1729 }
1730
1731 conn =
1732 build_conn()
1733 |> assign(:user, user)
1734 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1735
1736 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1737 end
1738
1739 test "mascot retrieving", %{conn: conn} do
1740 user = insert(:user)
1741 # When user hasn't set a mascot, we should just get pleroma tan back
1742 conn =
1743 conn
1744 |> assign(:user, user)
1745 |> get("/api/v1/pleroma/mascot")
1746
1747 assert %{"url" => url} = json_response(conn, 200)
1748 assert url =~ "pleroma-fox-tan-smol"
1749
1750 # When a user sets their mascot, we should get that back
1751 file = %Plug.Upload{
1752 content_type: "image/jpg",
1753 path: Path.absname("test/fixtures/image.jpg"),
1754 filename: "an_image.jpg"
1755 }
1756
1757 conn =
1758 build_conn()
1759 |> assign(:user, user)
1760 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1761
1762 assert json_response(conn, 200)
1763
1764 user = User.get_cached_by_id(user.id)
1765
1766 conn =
1767 build_conn()
1768 |> assign(:user, user)
1769 |> get("/api/v1/pleroma/mascot")
1770
1771 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1772 assert url =~ "an_image"
1773 end
1774
1775 test "hashtag timeline", %{conn: conn} do
1776 following = insert(:user)
1777
1778 capture_log(fn ->
1779 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1780
1781 {:ok, [_activity]} =
1782 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1783
1784 nconn =
1785 conn
1786 |> get("/api/v1/timelines/tag/2hu")
1787
1788 assert [%{"id" => id}] = json_response(nconn, 200)
1789
1790 assert id == to_string(activity.id)
1791
1792 # works for different capitalization too
1793 nconn =
1794 conn
1795 |> get("/api/v1/timelines/tag/2HU")
1796
1797 assert [%{"id" => id}] = json_response(nconn, 200)
1798
1799 assert id == to_string(activity.id)
1800 end)
1801 end
1802
1803 test "multi-hashtag timeline", %{conn: conn} do
1804 user = insert(:user)
1805
1806 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1807 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1808 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1809
1810 any_test =
1811 conn
1812 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1813
1814 [status_none, status_test1, status_test] = json_response(any_test, 200)
1815
1816 assert to_string(activity_test.id) == status_test["id"]
1817 assert to_string(activity_test1.id) == status_test1["id"]
1818 assert to_string(activity_none.id) == status_none["id"]
1819
1820 restricted_test =
1821 conn
1822 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1823
1824 assert [status_test1] == json_response(restricted_test, 200)
1825
1826 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1827
1828 assert [status_none] == json_response(all_test, 200)
1829 end
1830
1831 test "getting followers", %{conn: conn} do
1832 user = insert(:user)
1833 other_user = insert(:user)
1834 {:ok, user} = User.follow(user, other_user)
1835
1836 conn =
1837 conn
1838 |> get("/api/v1/accounts/#{other_user.id}/followers")
1839
1840 assert [%{"id" => id}] = json_response(conn, 200)
1841 assert id == to_string(user.id)
1842 end
1843
1844 test "getting followers, hide_followers", %{conn: conn} do
1845 user = insert(:user)
1846 other_user = insert(:user, %{info: %{hide_followers: true}})
1847 {:ok, _user} = User.follow(user, other_user)
1848
1849 conn =
1850 conn
1851 |> get("/api/v1/accounts/#{other_user.id}/followers")
1852
1853 assert [] == json_response(conn, 200)
1854 end
1855
1856 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1857 user = insert(:user)
1858 other_user = insert(:user, %{info: %{hide_followers: true}})
1859 {:ok, _user} = User.follow(user, other_user)
1860
1861 conn =
1862 conn
1863 |> assign(:user, other_user)
1864 |> get("/api/v1/accounts/#{other_user.id}/followers")
1865
1866 refute [] == json_response(conn, 200)
1867 end
1868
1869 test "getting followers, pagination", %{conn: conn} do
1870 user = insert(:user)
1871 follower1 = insert(:user)
1872 follower2 = insert(:user)
1873 follower3 = insert(:user)
1874 {:ok, _} = User.follow(follower1, user)
1875 {:ok, _} = User.follow(follower2, user)
1876 {:ok, _} = User.follow(follower3, user)
1877
1878 conn =
1879 conn
1880 |> assign(:user, user)
1881
1882 res_conn =
1883 conn
1884 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1885
1886 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1887 assert id3 == follower3.id
1888 assert id2 == follower2.id
1889
1890 res_conn =
1891 conn
1892 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1893
1894 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1895 assert id2 == follower2.id
1896 assert id1 == follower1.id
1897
1898 res_conn =
1899 conn
1900 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1901
1902 assert [%{"id" => id2}] = json_response(res_conn, 200)
1903 assert id2 == follower2.id
1904
1905 assert [link_header] = get_resp_header(res_conn, "link")
1906 assert link_header =~ ~r/min_id=#{follower2.id}/
1907 assert link_header =~ ~r/max_id=#{follower2.id}/
1908 end
1909
1910 test "getting following", %{conn: conn} do
1911 user = insert(:user)
1912 other_user = insert(:user)
1913 {:ok, user} = User.follow(user, other_user)
1914
1915 conn =
1916 conn
1917 |> get("/api/v1/accounts/#{user.id}/following")
1918
1919 assert [%{"id" => id}] = json_response(conn, 200)
1920 assert id == to_string(other_user.id)
1921 end
1922
1923 test "getting following, hide_follows", %{conn: conn} do
1924 user = insert(:user, %{info: %{hide_follows: true}})
1925 other_user = insert(:user)
1926 {:ok, user} = User.follow(user, other_user)
1927
1928 conn =
1929 conn
1930 |> get("/api/v1/accounts/#{user.id}/following")
1931
1932 assert [] == json_response(conn, 200)
1933 end
1934
1935 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1936 user = insert(:user, %{info: %{hide_follows: true}})
1937 other_user = insert(:user)
1938 {:ok, user} = User.follow(user, other_user)
1939
1940 conn =
1941 conn
1942 |> assign(:user, user)
1943 |> get("/api/v1/accounts/#{user.id}/following")
1944
1945 refute [] == json_response(conn, 200)
1946 end
1947
1948 test "getting following, pagination", %{conn: conn} do
1949 user = insert(:user)
1950 following1 = insert(:user)
1951 following2 = insert(:user)
1952 following3 = insert(:user)
1953 {:ok, _} = User.follow(user, following1)
1954 {:ok, _} = User.follow(user, following2)
1955 {:ok, _} = User.follow(user, following3)
1956
1957 conn =
1958 conn
1959 |> assign(:user, user)
1960
1961 res_conn =
1962 conn
1963 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1964
1965 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1966 assert id3 == following3.id
1967 assert id2 == following2.id
1968
1969 res_conn =
1970 conn
1971 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1972
1973 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1974 assert id2 == following2.id
1975 assert id1 == following1.id
1976
1977 res_conn =
1978 conn
1979 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1980
1981 assert [%{"id" => id2}] = json_response(res_conn, 200)
1982 assert id2 == following2.id
1983
1984 assert [link_header] = get_resp_header(res_conn, "link")
1985 assert link_header =~ ~r/min_id=#{following2.id}/
1986 assert link_header =~ ~r/max_id=#{following2.id}/
1987 end
1988
1989 test "following / unfollowing a user", %{conn: conn} do
1990 user = insert(:user)
1991 other_user = insert(:user)
1992
1993 conn =
1994 conn
1995 |> assign(:user, user)
1996 |> post("/api/v1/accounts/#{other_user.id}/follow")
1997
1998 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1999
2000 user = User.get_cached_by_id(user.id)
2001
2002 conn =
2003 build_conn()
2004 |> assign(:user, user)
2005 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2006
2007 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2008
2009 user = User.get_cached_by_id(user.id)
2010
2011 conn =
2012 build_conn()
2013 |> assign(:user, user)
2014 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2015
2016 assert %{"id" => id} = json_response(conn, 200)
2017 assert id == to_string(other_user.id)
2018 end
2019
2020 test "following without reblogs" do
2021 follower = insert(:user)
2022 followed = insert(:user)
2023 other_user = insert(:user)
2024
2025 conn =
2026 build_conn()
2027 |> assign(:user, follower)
2028 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2029
2030 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2031
2032 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2033 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2034
2035 conn =
2036 build_conn()
2037 |> assign(:user, User.get_cached_by_id(follower.id))
2038 |> get("/api/v1/timelines/home")
2039
2040 assert [] == json_response(conn, 200)
2041
2042 conn =
2043 build_conn()
2044 |> assign(:user, follower)
2045 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2046
2047 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2048
2049 conn =
2050 build_conn()
2051 |> assign(:user, User.get_cached_by_id(follower.id))
2052 |> get("/api/v1/timelines/home")
2053
2054 expected_activity_id = reblog.id
2055 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2056 end
2057
2058 test "following / unfollowing errors" do
2059 user = insert(:user)
2060
2061 conn =
2062 build_conn()
2063 |> assign(:user, user)
2064
2065 # self follow
2066 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2067 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2068
2069 # self unfollow
2070 user = User.get_cached_by_id(user.id)
2071 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2072 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2073
2074 # self follow via uri
2075 user = User.get_cached_by_id(user.id)
2076 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2077 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2078
2079 # follow non existing user
2080 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2081 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2082
2083 # follow non existing user via uri
2084 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2085 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2086
2087 # unfollow non existing user
2088 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2089 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2090 end
2091
2092 test "muting / unmuting a user", %{conn: conn} do
2093 user = insert(:user)
2094 other_user = insert(:user)
2095
2096 conn =
2097 conn
2098 |> assign(:user, user)
2099 |> post("/api/v1/accounts/#{other_user.id}/mute")
2100
2101 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
2102
2103 user = User.get_cached_by_id(user.id)
2104
2105 conn =
2106 build_conn()
2107 |> assign(:user, user)
2108 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2109
2110 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
2111 end
2112
2113 test "subscribing / unsubscribing to a user", %{conn: conn} do
2114 user = insert(:user)
2115 subscription_target = insert(:user)
2116
2117 conn =
2118 conn
2119 |> assign(:user, user)
2120 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2121
2122 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2123
2124 conn =
2125 build_conn()
2126 |> assign(:user, user)
2127 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2128
2129 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2130 end
2131
2132 test "getting a list of mutes", %{conn: conn} do
2133 user = insert(:user)
2134 other_user = insert(:user)
2135
2136 {:ok, user} = User.mute(user, other_user)
2137
2138 conn =
2139 conn
2140 |> assign(:user, user)
2141 |> get("/api/v1/mutes")
2142
2143 other_user_id = to_string(other_user.id)
2144 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2145 end
2146
2147 test "blocking / unblocking a user", %{conn: conn} do
2148 user = insert(:user)
2149 other_user = insert(:user)
2150
2151 conn =
2152 conn
2153 |> assign(:user, user)
2154 |> post("/api/v1/accounts/#{other_user.id}/block")
2155
2156 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2157
2158 user = User.get_cached_by_id(user.id)
2159
2160 conn =
2161 build_conn()
2162 |> assign(:user, user)
2163 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2164
2165 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2166 end
2167
2168 test "getting a list of blocks", %{conn: conn} do
2169 user = insert(:user)
2170 other_user = insert(:user)
2171
2172 {:ok, user} = User.block(user, other_user)
2173
2174 conn =
2175 conn
2176 |> assign(:user, user)
2177 |> get("/api/v1/blocks")
2178
2179 other_user_id = to_string(other_user.id)
2180 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2181 end
2182
2183 test "blocking / unblocking a domain", %{conn: conn} do
2184 user = insert(:user)
2185 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2186
2187 conn =
2188 conn
2189 |> assign(:user, user)
2190 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2191
2192 assert %{} = json_response(conn, 200)
2193 user = User.get_cached_by_ap_id(user.ap_id)
2194 assert User.blocks?(user, other_user)
2195
2196 conn =
2197 build_conn()
2198 |> assign(:user, user)
2199 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2200
2201 assert %{} = json_response(conn, 200)
2202 user = User.get_cached_by_ap_id(user.ap_id)
2203 refute User.blocks?(user, other_user)
2204 end
2205
2206 test "getting a list of domain blocks", %{conn: conn} do
2207 user = insert(:user)
2208
2209 {:ok, user} = User.block_domain(user, "bad.site")
2210 {:ok, user} = User.block_domain(user, "even.worse.site")
2211
2212 conn =
2213 conn
2214 |> assign(:user, user)
2215 |> get("/api/v1/domain_blocks")
2216
2217 domain_blocks = json_response(conn, 200)
2218
2219 assert "bad.site" in domain_blocks
2220 assert "even.worse.site" in domain_blocks
2221 end
2222
2223 test "unimplemented follow_requests, blocks, domain blocks" do
2224 user = insert(:user)
2225
2226 ["blocks", "domain_blocks", "follow_requests"]
2227 |> Enum.each(fn endpoint ->
2228 conn =
2229 build_conn()
2230 |> assign(:user, user)
2231 |> get("/api/v1/#{endpoint}")
2232
2233 assert [] = json_response(conn, 200)
2234 end)
2235 end
2236
2237 test "returns the favorites of a user", %{conn: conn} do
2238 user = insert(:user)
2239 other_user = insert(:user)
2240
2241 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2242 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2243
2244 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2245
2246 first_conn =
2247 conn
2248 |> assign(:user, user)
2249 |> get("/api/v1/favourites")
2250
2251 assert [status] = json_response(first_conn, 200)
2252 assert status["id"] == to_string(activity.id)
2253
2254 assert [{"link", _link_header}] =
2255 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2256
2257 # Honours query params
2258 {:ok, second_activity} =
2259 CommonAPI.post(other_user, %{
2260 "status" =>
2261 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2262 })
2263
2264 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2265
2266 last_like = status["id"]
2267
2268 second_conn =
2269 conn
2270 |> assign(:user, user)
2271 |> get("/api/v1/favourites?since_id=#{last_like}")
2272
2273 assert [second_status] = json_response(second_conn, 200)
2274 assert second_status["id"] == to_string(second_activity.id)
2275
2276 third_conn =
2277 conn
2278 |> assign(:user, user)
2279 |> get("/api/v1/favourites?limit=0")
2280
2281 assert [] = json_response(third_conn, 200)
2282 end
2283
2284 describe "getting favorites timeline of specified user" do
2285 setup do
2286 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2287 [current_user: current_user, user: user]
2288 end
2289
2290 test "returns list of statuses favorited by specified user", %{
2291 conn: conn,
2292 current_user: current_user,
2293 user: user
2294 } do
2295 [activity | _] = insert_pair(:note_activity)
2296 CommonAPI.favorite(activity.id, user)
2297
2298 response =
2299 conn
2300 |> assign(:user, current_user)
2301 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2302 |> json_response(:ok)
2303
2304 [like] = response
2305
2306 assert length(response) == 1
2307 assert like["id"] == activity.id
2308 end
2309
2310 test "returns favorites for specified user_id when user is not logged in", %{
2311 conn: conn,
2312 user: user
2313 } do
2314 activity = insert(:note_activity)
2315 CommonAPI.favorite(activity.id, user)
2316
2317 response =
2318 conn
2319 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2320 |> json_response(:ok)
2321
2322 assert length(response) == 1
2323 end
2324
2325 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2326 conn: conn,
2327 current_user: current_user,
2328 user: user
2329 } do
2330 {:ok, direct} =
2331 CommonAPI.post(current_user, %{
2332 "status" => "Hi @#{user.nickname}!",
2333 "visibility" => "direct"
2334 })
2335
2336 CommonAPI.favorite(direct.id, user)
2337
2338 response =
2339 conn
2340 |> assign(:user, current_user)
2341 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2342 |> json_response(:ok)
2343
2344 assert length(response) == 1
2345
2346 anonymous_response =
2347 conn
2348 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2349 |> json_response(:ok)
2350
2351 assert Enum.empty?(anonymous_response)
2352 end
2353
2354 test "does not return others' favorited DM when user is not one of recipients", %{
2355 conn: conn,
2356 current_user: current_user,
2357 user: user
2358 } do
2359 user_two = insert(:user)
2360
2361 {:ok, direct} =
2362 CommonAPI.post(user_two, %{
2363 "status" => "Hi @#{user.nickname}!",
2364 "visibility" => "direct"
2365 })
2366
2367 CommonAPI.favorite(direct.id, user)
2368
2369 response =
2370 conn
2371 |> assign(:user, current_user)
2372 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2373 |> json_response(:ok)
2374
2375 assert Enum.empty?(response)
2376 end
2377
2378 test "paginates favorites using since_id and max_id", %{
2379 conn: conn,
2380 current_user: current_user,
2381 user: user
2382 } do
2383 activities = insert_list(10, :note_activity)
2384
2385 Enum.each(activities, fn activity ->
2386 CommonAPI.favorite(activity.id, user)
2387 end)
2388
2389 third_activity = Enum.at(activities, 2)
2390 seventh_activity = Enum.at(activities, 6)
2391
2392 response =
2393 conn
2394 |> assign(:user, current_user)
2395 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2396 since_id: third_activity.id,
2397 max_id: seventh_activity.id
2398 })
2399 |> json_response(:ok)
2400
2401 assert length(response) == 3
2402 refute third_activity in response
2403 refute seventh_activity in response
2404 end
2405
2406 test "limits favorites using limit parameter", %{
2407 conn: conn,
2408 current_user: current_user,
2409 user: user
2410 } do
2411 7
2412 |> insert_list(:note_activity)
2413 |> Enum.each(fn activity ->
2414 CommonAPI.favorite(activity.id, user)
2415 end)
2416
2417 response =
2418 conn
2419 |> assign(:user, current_user)
2420 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2421 |> json_response(:ok)
2422
2423 assert length(response) == 3
2424 end
2425
2426 test "returns empty response when user does not have any favorited statuses", %{
2427 conn: conn,
2428 current_user: current_user,
2429 user: user
2430 } do
2431 response =
2432 conn
2433 |> assign(:user, current_user)
2434 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2435 |> json_response(:ok)
2436
2437 assert Enum.empty?(response)
2438 end
2439
2440 test "returns 404 error when specified user is not exist", %{conn: conn} do
2441 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2442
2443 assert json_response(conn, 404) == %{"error" => "Record not found"}
2444 end
2445
2446 test "returns 403 error when user has hidden own favorites", %{
2447 conn: conn,
2448 current_user: current_user
2449 } do
2450 user = insert(:user, %{info: %{hide_favorites: true}})
2451 activity = insert(:note_activity)
2452 CommonAPI.favorite(activity.id, user)
2453
2454 conn =
2455 conn
2456 |> assign(:user, current_user)
2457 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2458
2459 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2460 end
2461
2462 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2463 user = insert(:user)
2464 activity = insert(:note_activity)
2465 CommonAPI.favorite(activity.id, user)
2466
2467 conn =
2468 conn
2469 |> assign(:user, current_user)
2470 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2471
2472 assert user.info.hide_favorites
2473 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2474 end
2475 end
2476
2477 test "get instance information", %{conn: conn} do
2478 conn = get(conn, "/api/v1/instance")
2479 assert result = json_response(conn, 200)
2480
2481 email = Pleroma.Config.get([:instance, :email])
2482 # Note: not checking for "max_toot_chars" since it's optional
2483 assert %{
2484 "uri" => _,
2485 "title" => _,
2486 "description" => _,
2487 "version" => _,
2488 "email" => from_config_email,
2489 "urls" => %{
2490 "streaming_api" => _
2491 },
2492 "stats" => _,
2493 "thumbnail" => _,
2494 "languages" => _,
2495 "registrations" => _,
2496 "poll_limits" => _
2497 } = result
2498
2499 assert email == from_config_email
2500 end
2501
2502 test "get instance stats", %{conn: conn} do
2503 user = insert(:user, %{local: true})
2504
2505 user2 = insert(:user, %{local: true})
2506 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2507
2508 insert(:user, %{local: false, nickname: "u@peer1.com"})
2509 insert(:user, %{local: false, nickname: "u@peer2.com"})
2510
2511 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2512
2513 # Stats should count users with missing or nil `info.deactivated` value
2514 user = User.get_cached_by_id(user.id)
2515 info_change = Changeset.change(user.info, %{deactivated: nil})
2516
2517 {:ok, _user} =
2518 user
2519 |> Changeset.change()
2520 |> Changeset.put_embed(:info, info_change)
2521 |> User.update_and_set_cache()
2522
2523 Pleroma.Stats.update_stats()
2524
2525 conn = get(conn, "/api/v1/instance")
2526
2527 assert result = json_response(conn, 200)
2528
2529 stats = result["stats"]
2530
2531 assert stats
2532 assert stats["user_count"] == 1
2533 assert stats["status_count"] == 1
2534 assert stats["domain_count"] == 2
2535 end
2536
2537 test "get peers", %{conn: conn} do
2538 insert(:user, %{local: false, nickname: "u@peer1.com"})
2539 insert(:user, %{local: false, nickname: "u@peer2.com"})
2540
2541 Pleroma.Stats.update_stats()
2542
2543 conn = get(conn, "/api/v1/instance/peers")
2544
2545 assert result = json_response(conn, 200)
2546
2547 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2548 end
2549
2550 test "put settings", %{conn: conn} do
2551 user = insert(:user)
2552
2553 conn =
2554 conn
2555 |> assign(:user, user)
2556 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2557
2558 assert _result = json_response(conn, 200)
2559
2560 user = User.get_cached_by_ap_id(user.ap_id)
2561 assert user.info.settings == %{"programming" => "socks"}
2562 end
2563
2564 describe "pinned statuses" do
2565 setup do
2566 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2567
2568 user = insert(:user)
2569 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2570
2571 [user: user, activity: activity]
2572 end
2573
2574 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2575 {:ok, _} = CommonAPI.pin(activity.id, user)
2576
2577 result =
2578 conn
2579 |> assign(:user, user)
2580 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2581 |> json_response(200)
2582
2583 id_str = to_string(activity.id)
2584
2585 assert [%{"id" => ^id_str, "pinned" => true}] = result
2586 end
2587
2588 test "pin status", %{conn: conn, user: user, activity: activity} do
2589 id_str = to_string(activity.id)
2590
2591 assert %{"id" => ^id_str, "pinned" => true} =
2592 conn
2593 |> assign(:user, user)
2594 |> post("/api/v1/statuses/#{activity.id}/pin")
2595 |> json_response(200)
2596
2597 assert [%{"id" => ^id_str, "pinned" => true}] =
2598 conn
2599 |> assign(:user, user)
2600 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2601 |> json_response(200)
2602 end
2603
2604 test "unpin status", %{conn: conn, user: user, activity: activity} do
2605 {:ok, _} = CommonAPI.pin(activity.id, user)
2606
2607 id_str = to_string(activity.id)
2608 user = refresh_record(user)
2609
2610 assert %{"id" => ^id_str, "pinned" => false} =
2611 conn
2612 |> assign(:user, user)
2613 |> post("/api/v1/statuses/#{activity.id}/unpin")
2614 |> json_response(200)
2615
2616 assert [] =
2617 conn
2618 |> assign(:user, user)
2619 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2620 |> json_response(200)
2621 end
2622
2623 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2624 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2625
2626 id_str_one = to_string(activity_one.id)
2627
2628 assert %{"id" => ^id_str_one, "pinned" => true} =
2629 conn
2630 |> assign(:user, user)
2631 |> post("/api/v1/statuses/#{id_str_one}/pin")
2632 |> json_response(200)
2633
2634 user = refresh_record(user)
2635
2636 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2637 conn
2638 |> assign(:user, user)
2639 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2640 |> json_response(400)
2641 end
2642 end
2643
2644 describe "cards" do
2645 setup do
2646 Pleroma.Config.put([:rich_media, :enabled], true)
2647
2648 on_exit(fn ->
2649 Pleroma.Config.put([:rich_media, :enabled], false)
2650 end)
2651
2652 user = insert(:user)
2653 %{user: user}
2654 end
2655
2656 test "returns rich-media card", %{conn: conn, user: user} do
2657 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2658
2659 card_data = %{
2660 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2661 "provider_name" => "www.imdb.com",
2662 "provider_url" => "http://www.imdb.com",
2663 "title" => "The Rock",
2664 "type" => "link",
2665 "url" => "http://www.imdb.com/title/tt0117500/",
2666 "description" =>
2667 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2668 "pleroma" => %{
2669 "opengraph" => %{
2670 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2671 "title" => "The Rock",
2672 "type" => "video.movie",
2673 "url" => "http://www.imdb.com/title/tt0117500/",
2674 "description" =>
2675 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2676 }
2677 }
2678 }
2679
2680 response =
2681 conn
2682 |> get("/api/v1/statuses/#{activity.id}/card")
2683 |> json_response(200)
2684
2685 assert response == card_data
2686
2687 # works with private posts
2688 {:ok, activity} =
2689 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2690
2691 response_two =
2692 conn
2693 |> assign(:user, user)
2694 |> get("/api/v1/statuses/#{activity.id}/card")
2695 |> json_response(200)
2696
2697 assert response_two == card_data
2698 end
2699
2700 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2701 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp-missing-data"})
2702
2703 response =
2704 conn
2705 |> get("/api/v1/statuses/#{activity.id}/card")
2706 |> json_response(:ok)
2707
2708 assert response == %{
2709 "type" => "link",
2710 "title" => "Pleroma",
2711 "description" => "",
2712 "image" => nil,
2713 "provider_name" => "pleroma.social",
2714 "provider_url" => "https://pleroma.social",
2715 "url" => "https://pleroma.social/",
2716 "pleroma" => %{
2717 "opengraph" => %{
2718 "title" => "Pleroma",
2719 "type" => "website",
2720 "url" => "https://pleroma.social/"
2721 }
2722 }
2723 }
2724 end
2725 end
2726
2727 test "bookmarks" do
2728 user = insert(:user)
2729 for_user = insert(:user)
2730
2731 {:ok, activity1} =
2732 CommonAPI.post(user, %{
2733 "status" => "heweoo?"
2734 })
2735
2736 {:ok, activity2} =
2737 CommonAPI.post(user, %{
2738 "status" => "heweoo!"
2739 })
2740
2741 response1 =
2742 build_conn()
2743 |> assign(:user, for_user)
2744 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2745
2746 assert json_response(response1, 200)["bookmarked"] == true
2747
2748 response2 =
2749 build_conn()
2750 |> assign(:user, for_user)
2751 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2752
2753 assert json_response(response2, 200)["bookmarked"] == true
2754
2755 bookmarks =
2756 build_conn()
2757 |> assign(:user, for_user)
2758 |> get("/api/v1/bookmarks")
2759
2760 assert [json_response(response2, 200), json_response(response1, 200)] ==
2761 json_response(bookmarks, 200)
2762
2763 response1 =
2764 build_conn()
2765 |> assign(:user, for_user)
2766 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2767
2768 assert json_response(response1, 200)["bookmarked"] == false
2769
2770 bookmarks =
2771 build_conn()
2772 |> assign(:user, for_user)
2773 |> get("/api/v1/bookmarks")
2774
2775 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2776 end
2777
2778 describe "conversation muting" do
2779 setup do
2780 user = insert(:user)
2781 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2782
2783 [user: user, activity: activity]
2784 end
2785
2786 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2787 id_str = to_string(activity.id)
2788
2789 assert %{"id" => ^id_str, "muted" => true} =
2790 conn
2791 |> assign(:user, user)
2792 |> post("/api/v1/statuses/#{activity.id}/mute")
2793 |> json_response(200)
2794 end
2795
2796 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2797 {:ok, _} = CommonAPI.add_mute(user, activity)
2798
2799 id_str = to_string(activity.id)
2800 user = refresh_record(user)
2801
2802 assert %{"id" => ^id_str, "muted" => false} =
2803 conn
2804 |> assign(:user, user)
2805 |> post("/api/v1/statuses/#{activity.id}/unmute")
2806 |> json_response(200)
2807 end
2808 end
2809
2810 describe "reports" do
2811 setup do
2812 reporter = insert(:user)
2813 target_user = insert(:user)
2814
2815 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2816
2817 [reporter: reporter, target_user: target_user, activity: activity]
2818 end
2819
2820 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2821 assert %{"action_taken" => false, "id" => _} =
2822 conn
2823 |> assign(:user, reporter)
2824 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2825 |> json_response(200)
2826 end
2827
2828 test "submit a report with statuses and comment", %{
2829 conn: conn,
2830 reporter: reporter,
2831 target_user: target_user,
2832 activity: activity
2833 } do
2834 assert %{"action_taken" => false, "id" => _} =
2835 conn
2836 |> assign(:user, reporter)
2837 |> post("/api/v1/reports", %{
2838 "account_id" => target_user.id,
2839 "status_ids" => [activity.id],
2840 "comment" => "bad status!"
2841 })
2842 |> json_response(200)
2843 end
2844
2845 test "account_id is required", %{
2846 conn: conn,
2847 reporter: reporter,
2848 activity: activity
2849 } do
2850 assert %{"error" => "Valid `account_id` required"} =
2851 conn
2852 |> assign(:user, reporter)
2853 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2854 |> json_response(400)
2855 end
2856
2857 test "comment must be up to the size specified in the config", %{
2858 conn: conn,
2859 reporter: reporter,
2860 target_user: target_user
2861 } do
2862 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2863 comment = String.pad_trailing("a", max_size + 1, "a")
2864
2865 error = %{"error" => "Comment must be up to #{max_size} characters"}
2866
2867 assert ^error =
2868 conn
2869 |> assign(:user, reporter)
2870 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2871 |> json_response(400)
2872 end
2873 end
2874
2875 describe "link headers" do
2876 test "preserves parameters in link headers", %{conn: conn} do
2877 user = insert(:user)
2878 other_user = insert(:user)
2879
2880 {:ok, activity1} =
2881 CommonAPI.post(other_user, %{
2882 "status" => "hi @#{user.nickname}",
2883 "visibility" => "public"
2884 })
2885
2886 {:ok, activity2} =
2887 CommonAPI.post(other_user, %{
2888 "status" => "hi @#{user.nickname}",
2889 "visibility" => "public"
2890 })
2891
2892 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2893 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2894
2895 conn =
2896 conn
2897 |> assign(:user, user)
2898 |> get("/api/v1/notifications", %{media_only: true})
2899
2900 assert [link_header] = get_resp_header(conn, "link")
2901 assert link_header =~ ~r/media_only=true/
2902 assert link_header =~ ~r/min_id=#{notification2.id}/
2903 assert link_header =~ ~r/max_id=#{notification1.id}/
2904 end
2905 end
2906
2907 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2908 # Need to set an old-style integer ID to reproduce the problem
2909 # (these are no longer assigned to new accounts but were preserved
2910 # for existing accounts during the migration to flakeIDs)
2911 user_one = insert(:user, %{id: 1212})
2912 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2913
2914 resp_one =
2915 conn
2916 |> get("/api/v1/accounts/#{user_one.id}")
2917
2918 resp_two =
2919 conn
2920 |> get("/api/v1/accounts/#{user_two.nickname}")
2921
2922 resp_three =
2923 conn
2924 |> get("/api/v1/accounts/#{user_two.id}")
2925
2926 acc_one = json_response(resp_one, 200)
2927 acc_two = json_response(resp_two, 200)
2928 acc_three = json_response(resp_three, 200)
2929 refute acc_one == acc_two
2930 assert acc_two == acc_three
2931 end
2932
2933 describe "custom emoji" do
2934 test "with tags", %{conn: conn} do
2935 [emoji | _body] =
2936 conn
2937 |> get("/api/v1/custom_emojis")
2938 |> json_response(200)
2939
2940 assert Map.has_key?(emoji, "shortcode")
2941 assert Map.has_key?(emoji, "static_url")
2942 assert Map.has_key?(emoji, "tags")
2943 assert is_list(emoji["tags"])
2944 assert Map.has_key?(emoji, "url")
2945 assert Map.has_key?(emoji, "visible_in_picker")
2946 end
2947 end
2948
2949 describe "index/2 redirections" do
2950 setup %{conn: conn} do
2951 session_opts = [
2952 store: :cookie,
2953 key: "_test",
2954 signing_salt: "cooldude"
2955 ]
2956
2957 conn =
2958 conn
2959 |> Plug.Session.call(Plug.Session.init(session_opts))
2960 |> fetch_session()
2961
2962 test_path = "/web/statuses/test"
2963 %{conn: conn, path: test_path}
2964 end
2965
2966 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2967 conn = get(conn, path)
2968
2969 assert conn.status == 302
2970 assert redirected_to(conn) == "/web/login"
2971 end
2972
2973 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2974 token = insert(:oauth_token)
2975
2976 conn =
2977 conn
2978 |> assign(:user, token.user)
2979 |> put_session(:oauth_token, token.token)
2980 |> get(path)
2981
2982 assert conn.status == 200
2983 end
2984
2985 test "saves referer path to session", %{conn: conn, path: path} do
2986 conn = get(conn, path)
2987 return_to = Plug.Conn.get_session(conn, :return_to)
2988
2989 assert return_to == path
2990 end
2991
2992 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2993 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2994 auth = insert(:oauth_authorization, app: app)
2995
2996 conn =
2997 conn
2998 |> put_session(:return_to, path)
2999 |> get("/web/login", %{code: auth.token})
3000
3001 assert conn.status == 302
3002 assert redirected_to(conn) == path
3003 end
3004
3005 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3006 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3007 auth = insert(:oauth_authorization, app: app)
3008
3009 conn = get(conn, "/web/login", %{code: auth.token})
3010
3011 assert conn.status == 302
3012 assert redirected_to(conn) == "/web/getting-started"
3013 end
3014 end
3015
3016 describe "scheduled activities" do
3017 test "creates a scheduled activity", %{conn: conn} do
3018 user = insert(:user)
3019 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3020
3021 conn =
3022 conn
3023 |> assign(:user, user)
3024 |> post("/api/v1/statuses", %{
3025 "status" => "scheduled",
3026 "scheduled_at" => scheduled_at
3027 })
3028
3029 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3030 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3031 assert [] == Repo.all(Activity)
3032 end
3033
3034 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3035 user = insert(:user)
3036 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3037
3038 file = %Plug.Upload{
3039 content_type: "image/jpg",
3040 path: Path.absname("test/fixtures/image.jpg"),
3041 filename: "an_image.jpg"
3042 }
3043
3044 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3045
3046 conn =
3047 conn
3048 |> assign(:user, user)
3049 |> post("/api/v1/statuses", %{
3050 "media_ids" => [to_string(upload.id)],
3051 "status" => "scheduled",
3052 "scheduled_at" => scheduled_at
3053 })
3054
3055 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3056 assert %{"type" => "image"} = media_attachment
3057 end
3058
3059 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3060 %{conn: conn} do
3061 user = insert(:user)
3062
3063 scheduled_at =
3064 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3065
3066 conn =
3067 conn
3068 |> assign(:user, user)
3069 |> post("/api/v1/statuses", %{
3070 "status" => "not scheduled",
3071 "scheduled_at" => scheduled_at
3072 })
3073
3074 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3075 assert [] == Repo.all(ScheduledActivity)
3076 end
3077
3078 test "returns error when daily user limit is exceeded", %{conn: conn} do
3079 user = insert(:user)
3080
3081 today =
3082 NaiveDateTime.utc_now()
3083 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3084 |> NaiveDateTime.to_iso8601()
3085
3086 attrs = %{params: %{}, scheduled_at: today}
3087 {:ok, _} = ScheduledActivity.create(user, attrs)
3088 {:ok, _} = ScheduledActivity.create(user, attrs)
3089
3090 conn =
3091 conn
3092 |> assign(:user, user)
3093 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3094
3095 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3096 end
3097
3098 test "returns error when total user limit is exceeded", %{conn: conn} do
3099 user = insert(:user)
3100
3101 today =
3102 NaiveDateTime.utc_now()
3103 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3104 |> NaiveDateTime.to_iso8601()
3105
3106 tomorrow =
3107 NaiveDateTime.utc_now()
3108 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3109 |> NaiveDateTime.to_iso8601()
3110
3111 attrs = %{params: %{}, scheduled_at: today}
3112 {:ok, _} = ScheduledActivity.create(user, attrs)
3113 {:ok, _} = ScheduledActivity.create(user, attrs)
3114 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3115
3116 conn =
3117 conn
3118 |> assign(:user, user)
3119 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3120
3121 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3122 end
3123
3124 test "shows scheduled activities", %{conn: conn} do
3125 user = insert(:user)
3126 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3127 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3128 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3129 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3130
3131 conn =
3132 conn
3133 |> assign(:user, user)
3134
3135 # min_id
3136 conn_res =
3137 conn
3138 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3139
3140 result = json_response(conn_res, 200)
3141 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3142
3143 # since_id
3144 conn_res =
3145 conn
3146 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3147
3148 result = json_response(conn_res, 200)
3149 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3150
3151 # max_id
3152 conn_res =
3153 conn
3154 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3155
3156 result = json_response(conn_res, 200)
3157 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3158 end
3159
3160 test "shows a scheduled activity", %{conn: conn} do
3161 user = insert(:user)
3162 scheduled_activity = insert(:scheduled_activity, user: user)
3163
3164 res_conn =
3165 conn
3166 |> assign(:user, user)
3167 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3168
3169 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3170 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3171
3172 res_conn =
3173 conn
3174 |> assign(:user, user)
3175 |> get("/api/v1/scheduled_statuses/404")
3176
3177 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3178 end
3179
3180 test "updates a scheduled activity", %{conn: conn} do
3181 user = insert(:user)
3182 scheduled_activity = insert(:scheduled_activity, user: user)
3183
3184 new_scheduled_at =
3185 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3186
3187 res_conn =
3188 conn
3189 |> assign(:user, user)
3190 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3191 scheduled_at: new_scheduled_at
3192 })
3193
3194 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3195 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3196
3197 res_conn =
3198 conn
3199 |> assign(:user, user)
3200 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3201
3202 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3203 end
3204
3205 test "deletes a scheduled activity", %{conn: conn} do
3206 user = insert(:user)
3207 scheduled_activity = insert(:scheduled_activity, user: user)
3208
3209 res_conn =
3210 conn
3211 |> assign(:user, user)
3212 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3213
3214 assert %{} = json_response(res_conn, 200)
3215 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3216
3217 res_conn =
3218 conn
3219 |> assign(:user, user)
3220 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3221
3222 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3223 end
3224 end
3225
3226 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3227 user1 = insert(:user)
3228 user2 = insert(:user)
3229 user3 = insert(:user)
3230
3231 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3232
3233 # Reply to status from another user
3234 conn1 =
3235 conn
3236 |> assign(:user, user2)
3237 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3238
3239 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3240
3241 activity = Activity.get_by_id_with_object(id)
3242
3243 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3244 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3245
3246 # Reblog from the third user
3247 conn2 =
3248 conn
3249 |> assign(:user, user3)
3250 |> post("/api/v1/statuses/#{activity.id}/reblog")
3251
3252 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3253 json_response(conn2, 200)
3254
3255 assert to_string(activity.id) == id
3256
3257 # Getting third user status
3258 conn3 =
3259 conn
3260 |> assign(:user, user3)
3261 |> get("api/v1/timelines/home")
3262
3263 [reblogged_activity] = json_response(conn3, 200)
3264
3265 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3266
3267 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3268 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3269 end
3270
3271 describe "create account by app" do
3272 test "Account registration via Application", %{conn: conn} do
3273 conn =
3274 conn
3275 |> post("/api/v1/apps", %{
3276 client_name: "client_name",
3277 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3278 scopes: "read, write, follow"
3279 })
3280
3281 %{
3282 "client_id" => client_id,
3283 "client_secret" => client_secret,
3284 "id" => _,
3285 "name" => "client_name",
3286 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3287 "vapid_key" => _,
3288 "website" => nil
3289 } = json_response(conn, 200)
3290
3291 conn =
3292 conn
3293 |> post("/oauth/token", %{
3294 grant_type: "client_credentials",
3295 client_id: client_id,
3296 client_secret: client_secret
3297 })
3298
3299 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3300 json_response(conn, 200)
3301
3302 assert token
3303 token_from_db = Repo.get_by(Token, token: token)
3304 assert token_from_db
3305 assert refresh
3306 assert scope == "read write follow"
3307
3308 conn =
3309 build_conn()
3310 |> put_req_header("authorization", "Bearer " <> token)
3311 |> post("/api/v1/accounts", %{
3312 username: "lain",
3313 email: "lain@example.org",
3314 password: "PlzDontHackLain",
3315 agreement: true
3316 })
3317
3318 %{
3319 "access_token" => token,
3320 "created_at" => _created_at,
3321 "scope" => _scope,
3322 "token_type" => "Bearer"
3323 } = json_response(conn, 200)
3324
3325 token_from_db = Repo.get_by(Token, token: token)
3326 assert token_from_db
3327 token_from_db = Repo.preload(token_from_db, :user)
3328 assert token_from_db.user
3329
3330 assert token_from_db.user.info.confirmation_pending
3331 end
3332
3333 test "rate limit", %{conn: conn} do
3334 app_token = insert(:oauth_token, user: nil)
3335
3336 conn =
3337 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3338 |> Map.put(:remote_ip, {15, 15, 15, 15})
3339
3340 for i <- 1..5 do
3341 conn =
3342 conn
3343 |> post("/api/v1/accounts", %{
3344 username: "#{i}lain",
3345 email: "#{i}lain@example.org",
3346 password: "PlzDontHackLain",
3347 agreement: true
3348 })
3349
3350 %{
3351 "access_token" => token,
3352 "created_at" => _created_at,
3353 "scope" => _scope,
3354 "token_type" => "Bearer"
3355 } = json_response(conn, 200)
3356
3357 token_from_db = Repo.get_by(Token, token: token)
3358 assert token_from_db
3359 token_from_db = Repo.preload(token_from_db, :user)
3360 assert token_from_db.user
3361
3362 assert token_from_db.user.info.confirmation_pending
3363 end
3364
3365 conn =
3366 conn
3367 |> post("/api/v1/accounts", %{
3368 username: "6lain",
3369 email: "6lain@example.org",
3370 password: "PlzDontHackLain",
3371 agreement: true
3372 })
3373
3374 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3375 end
3376 end
3377
3378 describe "GET /api/v1/polls/:id" do
3379 test "returns poll entity for object id", %{conn: conn} do
3380 user = insert(:user)
3381
3382 {:ok, activity} =
3383 CommonAPI.post(user, %{
3384 "status" => "Pleroma does",
3385 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3386 })
3387
3388 object = Object.normalize(activity)
3389
3390 conn =
3391 conn
3392 |> assign(:user, user)
3393 |> get("/api/v1/polls/#{object.id}")
3394
3395 response = json_response(conn, 200)
3396 id = object.id
3397 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3398 end
3399
3400 test "does not expose polls for private statuses", %{conn: conn} do
3401 user = insert(:user)
3402 other_user = insert(:user)
3403
3404 {:ok, activity} =
3405 CommonAPI.post(user, %{
3406 "status" => "Pleroma does",
3407 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3408 "visibility" => "private"
3409 })
3410
3411 object = Object.normalize(activity)
3412
3413 conn =
3414 conn
3415 |> assign(:user, other_user)
3416 |> get("/api/v1/polls/#{object.id}")
3417
3418 assert json_response(conn, 404)
3419 end
3420 end
3421
3422 describe "POST /api/v1/polls/:id/votes" do
3423 test "votes are added to the poll", %{conn: conn} do
3424 user = insert(:user)
3425 other_user = insert(:user)
3426
3427 {:ok, activity} =
3428 CommonAPI.post(user, %{
3429 "status" => "A very delicious sandwich",
3430 "poll" => %{
3431 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3432 "expires_in" => 20,
3433 "multiple" => true
3434 }
3435 })
3436
3437 object = Object.normalize(activity)
3438
3439 conn =
3440 conn
3441 |> assign(:user, other_user)
3442 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3443
3444 assert json_response(conn, 200)
3445 object = Object.get_by_id(object.id)
3446
3447 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3448 total_items == 1
3449 end)
3450 end
3451
3452 test "author can't vote", %{conn: conn} do
3453 user = insert(:user)
3454
3455 {:ok, activity} =
3456 CommonAPI.post(user, %{
3457 "status" => "Am I cute?",
3458 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3459 })
3460
3461 object = Object.normalize(activity)
3462
3463 assert conn
3464 |> assign(:user, user)
3465 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3466 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3467
3468 object = Object.get_by_id(object.id)
3469
3470 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3471 end
3472
3473 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3474 user = insert(:user)
3475 other_user = insert(:user)
3476
3477 {:ok, activity} =
3478 CommonAPI.post(user, %{
3479 "status" => "The glass is",
3480 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3481 })
3482
3483 object = Object.normalize(activity)
3484
3485 assert conn
3486 |> assign(:user, other_user)
3487 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3488 |> json_response(422) == %{"error" => "Too many choices"}
3489
3490 object = Object.get_by_id(object.id)
3491
3492 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3493 total_items == 1
3494 end)
3495 end
3496 end
3497 end