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