Merge branch 'outbox_pagination' into 'develop'
[akkoma] / test / web / mastodon_api / mastodon_api_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
7
8 alias Ecto.Changeset
9 alias Pleroma.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 end
1568
1569 describe "media upload" do
1570 setup do
1571 user = insert(:user)
1572
1573 conn =
1574 build_conn()
1575 |> assign(:user, user)
1576
1577 image = %Plug.Upload{
1578 content_type: "image/jpg",
1579 path: Path.absname("test/fixtures/image.jpg"),
1580 filename: "an_image.jpg"
1581 }
1582
1583 [conn: conn, image: image]
1584 end
1585
1586 clear_config([:media_proxy])
1587 clear_config([Pleroma.Upload])
1588
1589 test "returns uploaded image", %{conn: conn, image: image} do
1590 desc = "Description of the image"
1591
1592 media =
1593 conn
1594 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1595 |> json_response(:ok)
1596
1597 assert media["type"] == "image"
1598 assert media["description"] == desc
1599 assert media["id"]
1600
1601 object = Repo.get(Object, media["id"])
1602 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1603 end
1604 end
1605
1606 describe "locked accounts" do
1607 test "/api/v1/follow_requests works" do
1608 user = insert(:user, %{info: %User.Info{locked: true}})
1609 other_user = insert(:user)
1610
1611 {:ok, _activity} = ActivityPub.follow(other_user, user)
1612
1613 user = User.get_cached_by_id(user.id)
1614 other_user = User.get_cached_by_id(other_user.id)
1615
1616 assert User.following?(other_user, user) == false
1617
1618 conn =
1619 build_conn()
1620 |> assign(:user, user)
1621 |> get("/api/v1/follow_requests")
1622
1623 assert [relationship] = json_response(conn, 200)
1624 assert to_string(other_user.id) == relationship["id"]
1625 end
1626
1627 test "/api/v1/follow_requests/:id/authorize works" do
1628 user = insert(:user, %{info: %User.Info{locked: true}})
1629 other_user = insert(:user)
1630
1631 {:ok, _activity} = ActivityPub.follow(other_user, user)
1632
1633 user = User.get_cached_by_id(user.id)
1634 other_user = User.get_cached_by_id(other_user.id)
1635
1636 assert User.following?(other_user, user) == false
1637
1638 conn =
1639 build_conn()
1640 |> assign(:user, user)
1641 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1642
1643 assert relationship = json_response(conn, 200)
1644 assert to_string(other_user.id) == relationship["id"]
1645
1646 user = User.get_cached_by_id(user.id)
1647 other_user = User.get_cached_by_id(other_user.id)
1648
1649 assert User.following?(other_user, user) == true
1650 end
1651
1652 test "verify_credentials", %{conn: conn} do
1653 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1654
1655 conn =
1656 conn
1657 |> assign(:user, user)
1658 |> get("/api/v1/accounts/verify_credentials")
1659
1660 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1661 assert id == to_string(user.id)
1662 end
1663
1664 test "/api/v1/follow_requests/:id/reject works" do
1665 user = insert(:user, %{info: %User.Info{locked: true}})
1666 other_user = insert(:user)
1667
1668 {:ok, _activity} = ActivityPub.follow(other_user, user)
1669
1670 user = User.get_cached_by_id(user.id)
1671
1672 conn =
1673 build_conn()
1674 |> assign(:user, user)
1675 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1676
1677 assert relationship = json_response(conn, 200)
1678 assert to_string(other_user.id) == relationship["id"]
1679
1680 user = User.get_cached_by_id(user.id)
1681 other_user = User.get_cached_by_id(other_user.id)
1682
1683 assert User.following?(other_user, user) == false
1684 end
1685 end
1686
1687 describe "account fetching" do
1688 test "works by id" do
1689 user = insert(:user)
1690
1691 conn =
1692 build_conn()
1693 |> get("/api/v1/accounts/#{user.id}")
1694
1695 assert %{"id" => id} = json_response(conn, 200)
1696 assert id == to_string(user.id)
1697
1698 conn =
1699 build_conn()
1700 |> get("/api/v1/accounts/-1")
1701
1702 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1703 end
1704
1705 test "works by nickname" do
1706 user = insert(:user)
1707
1708 conn =
1709 build_conn()
1710 |> get("/api/v1/accounts/#{user.nickname}")
1711
1712 assert %{"id" => id} = json_response(conn, 200)
1713 assert id == user.id
1714 end
1715
1716 test "works by nickname for remote users" do
1717 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1718 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1719 user = insert(:user, nickname: "user@example.com", local: false)
1720
1721 conn =
1722 build_conn()
1723 |> get("/api/v1/accounts/#{user.nickname}")
1724
1725 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1726 assert %{"id" => id} = json_response(conn, 200)
1727 assert id == user.id
1728 end
1729
1730 test "respects limit_to_local_content == :all for remote user nicknames" do
1731 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1732 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1733
1734 user = insert(:user, nickname: "user@example.com", local: false)
1735
1736 conn =
1737 build_conn()
1738 |> get("/api/v1/accounts/#{user.nickname}")
1739
1740 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1741 assert json_response(conn, 404)
1742 end
1743
1744 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1745 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1746 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1747
1748 user = insert(:user, nickname: "user@example.com", local: false)
1749 reading_user = insert(:user)
1750
1751 conn =
1752 build_conn()
1753 |> get("/api/v1/accounts/#{user.nickname}")
1754
1755 assert json_response(conn, 404)
1756
1757 conn =
1758 build_conn()
1759 |> assign(:user, reading_user)
1760 |> get("/api/v1/accounts/#{user.nickname}")
1761
1762 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1763 assert %{"id" => id} = json_response(conn, 200)
1764 assert id == user.id
1765 end
1766 end
1767
1768 test "mascot upload", %{conn: conn} do
1769 user = insert(:user)
1770
1771 non_image_file = %Plug.Upload{
1772 content_type: "audio/mpeg",
1773 path: Path.absname("test/fixtures/sound.mp3"),
1774 filename: "sound.mp3"
1775 }
1776
1777 conn =
1778 conn
1779 |> assign(:user, user)
1780 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1781
1782 assert json_response(conn, 415)
1783
1784 file = %Plug.Upload{
1785 content_type: "image/jpg",
1786 path: Path.absname("test/fixtures/image.jpg"),
1787 filename: "an_image.jpg"
1788 }
1789
1790 conn =
1791 build_conn()
1792 |> assign(:user, user)
1793 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1794
1795 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1796 end
1797
1798 test "mascot retrieving", %{conn: conn} do
1799 user = insert(:user)
1800 # When user hasn't set a mascot, we should just get pleroma tan back
1801 conn =
1802 conn
1803 |> assign(:user, user)
1804 |> get("/api/v1/pleroma/mascot")
1805
1806 assert %{"url" => url} = json_response(conn, 200)
1807 assert url =~ "pleroma-fox-tan-smol"
1808
1809 # When a user sets their mascot, we should get that back
1810 file = %Plug.Upload{
1811 content_type: "image/jpg",
1812 path: Path.absname("test/fixtures/image.jpg"),
1813 filename: "an_image.jpg"
1814 }
1815
1816 conn =
1817 build_conn()
1818 |> assign(:user, user)
1819 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1820
1821 assert json_response(conn, 200)
1822
1823 user = User.get_cached_by_id(user.id)
1824
1825 conn =
1826 build_conn()
1827 |> assign(:user, user)
1828 |> get("/api/v1/pleroma/mascot")
1829
1830 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1831 assert url =~ "an_image"
1832 end
1833
1834 test "hashtag timeline", %{conn: conn} do
1835 following = insert(:user)
1836
1837 capture_log(fn ->
1838 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1839
1840 {:ok, [_activity]} =
1841 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1842
1843 nconn =
1844 conn
1845 |> get("/api/v1/timelines/tag/2hu")
1846
1847 assert [%{"id" => id}] = json_response(nconn, 200)
1848
1849 assert id == to_string(activity.id)
1850
1851 # works for different capitalization too
1852 nconn =
1853 conn
1854 |> get("/api/v1/timelines/tag/2HU")
1855
1856 assert [%{"id" => id}] = json_response(nconn, 200)
1857
1858 assert id == to_string(activity.id)
1859 end)
1860 end
1861
1862 test "multi-hashtag timeline", %{conn: conn} do
1863 user = insert(:user)
1864
1865 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1866 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1867 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1868
1869 any_test =
1870 conn
1871 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1872
1873 [status_none, status_test1, status_test] = json_response(any_test, 200)
1874
1875 assert to_string(activity_test.id) == status_test["id"]
1876 assert to_string(activity_test1.id) == status_test1["id"]
1877 assert to_string(activity_none.id) == status_none["id"]
1878
1879 restricted_test =
1880 conn
1881 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1882
1883 assert [status_test1] == json_response(restricted_test, 200)
1884
1885 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1886
1887 assert [status_none] == json_response(all_test, 200)
1888 end
1889
1890 test "getting followers", %{conn: conn} do
1891 user = insert(:user)
1892 other_user = insert(:user)
1893 {:ok, user} = User.follow(user, other_user)
1894
1895 conn =
1896 conn
1897 |> get("/api/v1/accounts/#{other_user.id}/followers")
1898
1899 assert [%{"id" => id}] = json_response(conn, 200)
1900 assert id == to_string(user.id)
1901 end
1902
1903 test "getting followers, hide_followers", %{conn: conn} do
1904 user = insert(:user)
1905 other_user = insert(:user, %{info: %{hide_followers: true}})
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 [] == json_response(conn, 200)
1913 end
1914
1915 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1916 user = insert(:user)
1917 other_user = insert(:user, %{info: %{hide_followers: true}})
1918 {:ok, _user} = User.follow(user, other_user)
1919
1920 conn =
1921 conn
1922 |> assign(:user, other_user)
1923 |> get("/api/v1/accounts/#{other_user.id}/followers")
1924
1925 refute [] == json_response(conn, 200)
1926 end
1927
1928 test "getting followers, pagination", %{conn: conn} do
1929 user = insert(:user)
1930 follower1 = insert(:user)
1931 follower2 = insert(:user)
1932 follower3 = insert(:user)
1933 {:ok, _} = User.follow(follower1, user)
1934 {:ok, _} = User.follow(follower2, user)
1935 {:ok, _} = User.follow(follower3, user)
1936
1937 conn =
1938 conn
1939 |> assign(:user, user)
1940
1941 res_conn =
1942 conn
1943 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1944
1945 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1946 assert id3 == follower3.id
1947 assert id2 == follower2.id
1948
1949 res_conn =
1950 conn
1951 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1952
1953 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1954 assert id2 == follower2.id
1955 assert id1 == follower1.id
1956
1957 res_conn =
1958 conn
1959 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1960
1961 assert [%{"id" => id2}] = json_response(res_conn, 200)
1962 assert id2 == follower2.id
1963
1964 assert [link_header] = get_resp_header(res_conn, "link")
1965 assert link_header =~ ~r/min_id=#{follower2.id}/
1966 assert link_header =~ ~r/max_id=#{follower2.id}/
1967 end
1968
1969 test "getting following", %{conn: conn} do
1970 user = insert(:user)
1971 other_user = insert(:user)
1972 {:ok, user} = User.follow(user, other_user)
1973
1974 conn =
1975 conn
1976 |> get("/api/v1/accounts/#{user.id}/following")
1977
1978 assert [%{"id" => id}] = json_response(conn, 200)
1979 assert id == to_string(other_user.id)
1980 end
1981
1982 test "getting following, hide_follows", %{conn: conn} do
1983 user = insert(:user, %{info: %{hide_follows: true}})
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 [] == json_response(conn, 200)
1992 end
1993
1994 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1995 user = insert(:user, %{info: %{hide_follows: true}})
1996 other_user = insert(:user)
1997 {:ok, user} = User.follow(user, other_user)
1998
1999 conn =
2000 conn
2001 |> assign(:user, user)
2002 |> get("/api/v1/accounts/#{user.id}/following")
2003
2004 refute [] == json_response(conn, 200)
2005 end
2006
2007 test "getting following, pagination", %{conn: conn} do
2008 user = insert(:user)
2009 following1 = insert(:user)
2010 following2 = insert(:user)
2011 following3 = insert(:user)
2012 {:ok, _} = User.follow(user, following1)
2013 {:ok, _} = User.follow(user, following2)
2014 {:ok, _} = User.follow(user, following3)
2015
2016 conn =
2017 conn
2018 |> assign(:user, user)
2019
2020 res_conn =
2021 conn
2022 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2023
2024 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2025 assert id3 == following3.id
2026 assert id2 == following2.id
2027
2028 res_conn =
2029 conn
2030 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2031
2032 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2033 assert id2 == following2.id
2034 assert id1 == following1.id
2035
2036 res_conn =
2037 conn
2038 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2039
2040 assert [%{"id" => id2}] = json_response(res_conn, 200)
2041 assert id2 == following2.id
2042
2043 assert [link_header] = get_resp_header(res_conn, "link")
2044 assert link_header =~ ~r/min_id=#{following2.id}/
2045 assert link_header =~ ~r/max_id=#{following2.id}/
2046 end
2047
2048 test "following / unfollowing a user", %{conn: conn} do
2049 user = insert(:user)
2050 other_user = insert(:user)
2051
2052 conn =
2053 conn
2054 |> assign(:user, user)
2055 |> post("/api/v1/accounts/#{other_user.id}/follow")
2056
2057 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2058
2059 user = User.get_cached_by_id(user.id)
2060
2061 conn =
2062 build_conn()
2063 |> assign(:user, user)
2064 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2065
2066 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2067
2068 user = User.get_cached_by_id(user.id)
2069
2070 conn =
2071 build_conn()
2072 |> assign(:user, user)
2073 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2074
2075 assert %{"id" => id} = json_response(conn, 200)
2076 assert id == to_string(other_user.id)
2077 end
2078
2079 test "following without reblogs" do
2080 follower = insert(:user)
2081 followed = insert(:user)
2082 other_user = insert(:user)
2083
2084 conn =
2085 build_conn()
2086 |> assign(:user, follower)
2087 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2088
2089 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2090
2091 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2092 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2093
2094 conn =
2095 build_conn()
2096 |> assign(:user, User.get_cached_by_id(follower.id))
2097 |> get("/api/v1/timelines/home")
2098
2099 assert [] == json_response(conn, 200)
2100
2101 conn =
2102 build_conn()
2103 |> assign(:user, follower)
2104 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2105
2106 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2107
2108 conn =
2109 build_conn()
2110 |> assign(:user, User.get_cached_by_id(follower.id))
2111 |> get("/api/v1/timelines/home")
2112
2113 expected_activity_id = reblog.id
2114 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2115 end
2116
2117 test "following / unfollowing errors" do
2118 user = insert(:user)
2119
2120 conn =
2121 build_conn()
2122 |> assign(:user, user)
2123
2124 # self follow
2125 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2126 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2127
2128 # self unfollow
2129 user = User.get_cached_by_id(user.id)
2130 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2131 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2132
2133 # self follow via uri
2134 user = User.get_cached_by_id(user.id)
2135 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2136 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2137
2138 # follow non existing user
2139 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2140 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2141
2142 # follow non existing user via uri
2143 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2144 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2145
2146 # unfollow non existing user
2147 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2148 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2149 end
2150
2151 describe "mute/unmute" do
2152 test "with notifications", %{conn: conn} do
2153 user = insert(:user)
2154 other_user = insert(:user)
2155
2156 conn =
2157 conn
2158 |> assign(:user, user)
2159 |> post("/api/v1/accounts/#{other_user.id}/mute")
2160
2161 response = json_response(conn, 200)
2162
2163 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2164 user = User.get_cached_by_id(user.id)
2165
2166 conn =
2167 build_conn()
2168 |> assign(:user, user)
2169 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2170
2171 response = json_response(conn, 200)
2172 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2173 end
2174
2175 test "without notifications", %{conn: conn} do
2176 user = insert(:user)
2177 other_user = insert(:user)
2178
2179 conn =
2180 conn
2181 |> assign(:user, user)
2182 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2183
2184 response = json_response(conn, 200)
2185
2186 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2187 user = User.get_cached_by_id(user.id)
2188
2189 conn =
2190 build_conn()
2191 |> assign(:user, user)
2192 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2193
2194 response = json_response(conn, 200)
2195 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2196 end
2197 end
2198
2199 test "subscribing / unsubscribing to a user", %{conn: conn} do
2200 user = insert(:user)
2201 subscription_target = insert(:user)
2202
2203 conn =
2204 conn
2205 |> assign(:user, user)
2206 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2207
2208 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2209
2210 conn =
2211 build_conn()
2212 |> assign(:user, user)
2213 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2214
2215 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2216 end
2217
2218 test "getting a list of mutes", %{conn: conn} do
2219 user = insert(:user)
2220 other_user = insert(:user)
2221
2222 {:ok, user} = User.mute(user, other_user)
2223
2224 conn =
2225 conn
2226 |> assign(:user, user)
2227 |> get("/api/v1/mutes")
2228
2229 other_user_id = to_string(other_user.id)
2230 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2231 end
2232
2233 test "blocking / unblocking a user", %{conn: conn} do
2234 user = insert(:user)
2235 other_user = insert(:user)
2236
2237 conn =
2238 conn
2239 |> assign(:user, user)
2240 |> post("/api/v1/accounts/#{other_user.id}/block")
2241
2242 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2243
2244 user = User.get_cached_by_id(user.id)
2245
2246 conn =
2247 build_conn()
2248 |> assign(:user, user)
2249 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2250
2251 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2252 end
2253
2254 test "getting a list of blocks", %{conn: conn} do
2255 user = insert(:user)
2256 other_user = insert(:user)
2257
2258 {:ok, user} = User.block(user, other_user)
2259
2260 conn =
2261 conn
2262 |> assign(:user, user)
2263 |> get("/api/v1/blocks")
2264
2265 other_user_id = to_string(other_user.id)
2266 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2267 end
2268
2269 test "blocking / unblocking a domain", %{conn: conn} do
2270 user = insert(:user)
2271 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2272
2273 conn =
2274 conn
2275 |> assign(:user, user)
2276 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2277
2278 assert %{} = json_response(conn, 200)
2279 user = User.get_cached_by_ap_id(user.ap_id)
2280 assert User.blocks?(user, other_user)
2281
2282 conn =
2283 build_conn()
2284 |> assign(:user, user)
2285 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2286
2287 assert %{} = json_response(conn, 200)
2288 user = User.get_cached_by_ap_id(user.ap_id)
2289 refute User.blocks?(user, other_user)
2290 end
2291
2292 test "getting a list of domain blocks", %{conn: conn} do
2293 user = insert(:user)
2294
2295 {:ok, user} = User.block_domain(user, "bad.site")
2296 {:ok, user} = User.block_domain(user, "even.worse.site")
2297
2298 conn =
2299 conn
2300 |> assign(:user, user)
2301 |> get("/api/v1/domain_blocks")
2302
2303 domain_blocks = json_response(conn, 200)
2304
2305 assert "bad.site" in domain_blocks
2306 assert "even.worse.site" in domain_blocks
2307 end
2308
2309 test "unimplemented follow_requests, blocks, domain blocks" do
2310 user = insert(:user)
2311
2312 ["blocks", "domain_blocks", "follow_requests"]
2313 |> Enum.each(fn endpoint ->
2314 conn =
2315 build_conn()
2316 |> assign(:user, user)
2317 |> get("/api/v1/#{endpoint}")
2318
2319 assert [] = json_response(conn, 200)
2320 end)
2321 end
2322
2323 test "returns the favorites of a user", %{conn: conn} do
2324 user = insert(:user)
2325 other_user = insert(:user)
2326
2327 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2328 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2329
2330 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2331
2332 first_conn =
2333 conn
2334 |> assign(:user, user)
2335 |> get("/api/v1/favourites")
2336
2337 assert [status] = json_response(first_conn, 200)
2338 assert status["id"] == to_string(activity.id)
2339
2340 assert [{"link", _link_header}] =
2341 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2342
2343 # Honours query params
2344 {:ok, second_activity} =
2345 CommonAPI.post(other_user, %{
2346 "status" =>
2347 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2348 })
2349
2350 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2351
2352 last_like = status["id"]
2353
2354 second_conn =
2355 conn
2356 |> assign(:user, user)
2357 |> get("/api/v1/favourites?since_id=#{last_like}")
2358
2359 assert [second_status] = json_response(second_conn, 200)
2360 assert second_status["id"] == to_string(second_activity.id)
2361
2362 third_conn =
2363 conn
2364 |> assign(:user, user)
2365 |> get("/api/v1/favourites?limit=0")
2366
2367 assert [] = json_response(third_conn, 200)
2368 end
2369
2370 describe "getting favorites timeline of specified user" do
2371 setup do
2372 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2373 [current_user: current_user, user: user]
2374 end
2375
2376 test "returns list of statuses favorited by specified user", %{
2377 conn: conn,
2378 current_user: current_user,
2379 user: user
2380 } do
2381 [activity | _] = insert_pair(:note_activity)
2382 CommonAPI.favorite(activity.id, user)
2383
2384 response =
2385 conn
2386 |> assign(:user, current_user)
2387 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2388 |> json_response(:ok)
2389
2390 [like] = response
2391
2392 assert length(response) == 1
2393 assert like["id"] == activity.id
2394 end
2395
2396 test "returns favorites for specified user_id when user is not logged in", %{
2397 conn: conn,
2398 user: user
2399 } do
2400 activity = insert(:note_activity)
2401 CommonAPI.favorite(activity.id, user)
2402
2403 response =
2404 conn
2405 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2406 |> json_response(:ok)
2407
2408 assert length(response) == 1
2409 end
2410
2411 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2412 conn: conn,
2413 current_user: current_user,
2414 user: user
2415 } do
2416 {:ok, direct} =
2417 CommonAPI.post(current_user, %{
2418 "status" => "Hi @#{user.nickname}!",
2419 "visibility" => "direct"
2420 })
2421
2422 CommonAPI.favorite(direct.id, user)
2423
2424 response =
2425 conn
2426 |> assign(:user, current_user)
2427 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2428 |> json_response(:ok)
2429
2430 assert length(response) == 1
2431
2432 anonymous_response =
2433 conn
2434 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2435 |> json_response(:ok)
2436
2437 assert Enum.empty?(anonymous_response)
2438 end
2439
2440 test "does not return others' favorited DM when user is not one of recipients", %{
2441 conn: conn,
2442 current_user: current_user,
2443 user: user
2444 } do
2445 user_two = insert(:user)
2446
2447 {:ok, direct} =
2448 CommonAPI.post(user_two, %{
2449 "status" => "Hi @#{user.nickname}!",
2450 "visibility" => "direct"
2451 })
2452
2453 CommonAPI.favorite(direct.id, user)
2454
2455 response =
2456 conn
2457 |> assign(:user, current_user)
2458 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2459 |> json_response(:ok)
2460
2461 assert Enum.empty?(response)
2462 end
2463
2464 test "paginates favorites using since_id and max_id", %{
2465 conn: conn,
2466 current_user: current_user,
2467 user: user
2468 } do
2469 activities = insert_list(10, :note_activity)
2470
2471 Enum.each(activities, fn activity ->
2472 CommonAPI.favorite(activity.id, user)
2473 end)
2474
2475 third_activity = Enum.at(activities, 2)
2476 seventh_activity = Enum.at(activities, 6)
2477
2478 response =
2479 conn
2480 |> assign(:user, current_user)
2481 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2482 since_id: third_activity.id,
2483 max_id: seventh_activity.id
2484 })
2485 |> json_response(:ok)
2486
2487 assert length(response) == 3
2488 refute third_activity in response
2489 refute seventh_activity in response
2490 end
2491
2492 test "limits favorites using limit parameter", %{
2493 conn: conn,
2494 current_user: current_user,
2495 user: user
2496 } do
2497 7
2498 |> insert_list(:note_activity)
2499 |> Enum.each(fn activity ->
2500 CommonAPI.favorite(activity.id, user)
2501 end)
2502
2503 response =
2504 conn
2505 |> assign(:user, current_user)
2506 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2507 |> json_response(:ok)
2508
2509 assert length(response) == 3
2510 end
2511
2512 test "returns empty response when user does not have any favorited statuses", %{
2513 conn: conn,
2514 current_user: current_user,
2515 user: user
2516 } do
2517 response =
2518 conn
2519 |> assign(:user, current_user)
2520 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2521 |> json_response(:ok)
2522
2523 assert Enum.empty?(response)
2524 end
2525
2526 test "returns 404 error when specified user is not exist", %{conn: conn} do
2527 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2528
2529 assert json_response(conn, 404) == %{"error" => "Record not found"}
2530 end
2531
2532 test "returns 403 error when user has hidden own favorites", %{
2533 conn: conn,
2534 current_user: current_user
2535 } do
2536 user = insert(:user, %{info: %{hide_favorites: true}})
2537 activity = insert(:note_activity)
2538 CommonAPI.favorite(activity.id, user)
2539
2540 conn =
2541 conn
2542 |> assign(:user, current_user)
2543 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2544
2545 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2546 end
2547
2548 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2549 user = insert(:user)
2550 activity = insert(:note_activity)
2551 CommonAPI.favorite(activity.id, user)
2552
2553 conn =
2554 conn
2555 |> assign(:user, current_user)
2556 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2557
2558 assert user.info.hide_favorites
2559 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2560 end
2561 end
2562
2563 test "get instance information", %{conn: conn} do
2564 conn = get(conn, "/api/v1/instance")
2565 assert result = json_response(conn, 200)
2566
2567 email = Config.get([:instance, :email])
2568 # Note: not checking for "max_toot_chars" since it's optional
2569 assert %{
2570 "uri" => _,
2571 "title" => _,
2572 "description" => _,
2573 "version" => _,
2574 "email" => from_config_email,
2575 "urls" => %{
2576 "streaming_api" => _
2577 },
2578 "stats" => _,
2579 "thumbnail" => _,
2580 "languages" => _,
2581 "registrations" => _,
2582 "poll_limits" => _
2583 } = result
2584
2585 assert email == from_config_email
2586 end
2587
2588 test "get instance stats", %{conn: conn} do
2589 user = insert(:user, %{local: true})
2590
2591 user2 = insert(:user, %{local: true})
2592 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2593
2594 insert(:user, %{local: false, nickname: "u@peer1.com"})
2595 insert(:user, %{local: false, nickname: "u@peer2.com"})
2596
2597 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2598
2599 # Stats should count users with missing or nil `info.deactivated` value
2600 user = User.get_cached_by_id(user.id)
2601 info_change = Changeset.change(user.info, %{deactivated: nil})
2602
2603 {:ok, _user} =
2604 user
2605 |> Changeset.change()
2606 |> Changeset.put_embed(:info, info_change)
2607 |> User.update_and_set_cache()
2608
2609 Pleroma.Stats.force_update()
2610
2611 conn = get(conn, "/api/v1/instance")
2612
2613 assert result = json_response(conn, 200)
2614
2615 stats = result["stats"]
2616
2617 assert stats
2618 assert stats["user_count"] == 1
2619 assert stats["status_count"] == 1
2620 assert stats["domain_count"] == 2
2621 end
2622
2623 test "get peers", %{conn: conn} do
2624 insert(:user, %{local: false, nickname: "u@peer1.com"})
2625 insert(:user, %{local: false, nickname: "u@peer2.com"})
2626
2627 Pleroma.Stats.force_update()
2628
2629 conn = get(conn, "/api/v1/instance/peers")
2630
2631 assert result = json_response(conn, 200)
2632
2633 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2634 end
2635
2636 test "put settings", %{conn: conn} do
2637 user = insert(:user)
2638
2639 conn =
2640 conn
2641 |> assign(:user, user)
2642 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2643
2644 assert _result = json_response(conn, 200)
2645
2646 user = User.get_cached_by_ap_id(user.ap_id)
2647 assert user.info.settings == %{"programming" => "socks"}
2648 end
2649
2650 describe "pinned statuses" do
2651 setup do
2652 user = insert(:user)
2653 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2654
2655 [user: user, activity: activity]
2656 end
2657
2658 clear_config([:instance, :max_pinned_statuses]) do
2659 Config.put([:instance, :max_pinned_statuses], 1)
2660 end
2661
2662 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2663 {:ok, _} = CommonAPI.pin(activity.id, user)
2664
2665 result =
2666 conn
2667 |> assign(:user, user)
2668 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2669 |> json_response(200)
2670
2671 id_str = to_string(activity.id)
2672
2673 assert [%{"id" => ^id_str, "pinned" => true}] = result
2674 end
2675
2676 test "pin status", %{conn: conn, user: user, activity: activity} do
2677 id_str = to_string(activity.id)
2678
2679 assert %{"id" => ^id_str, "pinned" => true} =
2680 conn
2681 |> assign(:user, user)
2682 |> post("/api/v1/statuses/#{activity.id}/pin")
2683 |> json_response(200)
2684
2685 assert [%{"id" => ^id_str, "pinned" => true}] =
2686 conn
2687 |> assign(:user, user)
2688 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2689 |> json_response(200)
2690 end
2691
2692 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2693 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2694
2695 conn =
2696 conn
2697 |> assign(:user, user)
2698 |> post("/api/v1/statuses/#{dm.id}/pin")
2699
2700 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2701 end
2702
2703 test "unpin status", %{conn: conn, user: user, activity: activity} do
2704 {:ok, _} = CommonAPI.pin(activity.id, user)
2705
2706 id_str = to_string(activity.id)
2707 user = refresh_record(user)
2708
2709 assert %{"id" => ^id_str, "pinned" => false} =
2710 conn
2711 |> assign(:user, user)
2712 |> post("/api/v1/statuses/#{activity.id}/unpin")
2713 |> json_response(200)
2714
2715 assert [] =
2716 conn
2717 |> assign(:user, user)
2718 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2719 |> json_response(200)
2720 end
2721
2722 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2723 conn =
2724 conn
2725 |> assign(:user, user)
2726 |> post("/api/v1/statuses/1/unpin")
2727
2728 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2729 end
2730
2731 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2732 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2733
2734 id_str_one = to_string(activity_one.id)
2735
2736 assert %{"id" => ^id_str_one, "pinned" => true} =
2737 conn
2738 |> assign(:user, user)
2739 |> post("/api/v1/statuses/#{id_str_one}/pin")
2740 |> json_response(200)
2741
2742 user = refresh_record(user)
2743
2744 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2745 conn
2746 |> assign(:user, user)
2747 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2748 |> json_response(400)
2749 end
2750 end
2751
2752 describe "cards" do
2753 setup do
2754 Config.put([:rich_media, :enabled], true)
2755
2756 user = insert(:user)
2757 %{user: user}
2758 end
2759
2760 test "returns rich-media card", %{conn: conn, user: user} do
2761 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2762
2763 card_data = %{
2764 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2765 "provider_name" => "example.com",
2766 "provider_url" => "https://example.com",
2767 "title" => "The Rock",
2768 "type" => "link",
2769 "url" => "https://example.com/ogp",
2770 "description" =>
2771 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2772 "pleroma" => %{
2773 "opengraph" => %{
2774 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2775 "title" => "The Rock",
2776 "type" => "video.movie",
2777 "url" => "https://example.com/ogp",
2778 "description" =>
2779 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2780 }
2781 }
2782 }
2783
2784 response =
2785 conn
2786 |> get("/api/v1/statuses/#{activity.id}/card")
2787 |> json_response(200)
2788
2789 assert response == card_data
2790
2791 # works with private posts
2792 {:ok, activity} =
2793 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2794
2795 response_two =
2796 conn
2797 |> assign(:user, user)
2798 |> get("/api/v1/statuses/#{activity.id}/card")
2799 |> json_response(200)
2800
2801 assert response_two == card_data
2802 end
2803
2804 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2805 {:ok, activity} =
2806 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2807
2808 response =
2809 conn
2810 |> get("/api/v1/statuses/#{activity.id}/card")
2811 |> json_response(:ok)
2812
2813 assert response == %{
2814 "type" => "link",
2815 "title" => "Pleroma",
2816 "description" => "",
2817 "image" => nil,
2818 "provider_name" => "example.com",
2819 "provider_url" => "https://example.com",
2820 "url" => "https://example.com/ogp-missing-data",
2821 "pleroma" => %{
2822 "opengraph" => %{
2823 "title" => "Pleroma",
2824 "type" => "website",
2825 "url" => "https://example.com/ogp-missing-data"
2826 }
2827 }
2828 }
2829 end
2830 end
2831
2832 test "bookmarks" do
2833 user = insert(:user)
2834 for_user = insert(:user)
2835
2836 {:ok, activity1} =
2837 CommonAPI.post(user, %{
2838 "status" => "heweoo?"
2839 })
2840
2841 {:ok, activity2} =
2842 CommonAPI.post(user, %{
2843 "status" => "heweoo!"
2844 })
2845
2846 response1 =
2847 build_conn()
2848 |> assign(:user, for_user)
2849 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2850
2851 assert json_response(response1, 200)["bookmarked"] == true
2852
2853 response2 =
2854 build_conn()
2855 |> assign(:user, for_user)
2856 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2857
2858 assert json_response(response2, 200)["bookmarked"] == true
2859
2860 bookmarks =
2861 build_conn()
2862 |> assign(:user, for_user)
2863 |> get("/api/v1/bookmarks")
2864
2865 assert [json_response(response2, 200), json_response(response1, 200)] ==
2866 json_response(bookmarks, 200)
2867
2868 response1 =
2869 build_conn()
2870 |> assign(:user, for_user)
2871 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2872
2873 assert json_response(response1, 200)["bookmarked"] == false
2874
2875 bookmarks =
2876 build_conn()
2877 |> assign(:user, for_user)
2878 |> get("/api/v1/bookmarks")
2879
2880 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2881 end
2882
2883 describe "conversation muting" do
2884 setup do
2885 post_user = insert(:user)
2886 user = insert(:user)
2887
2888 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2889
2890 [user: user, activity: activity]
2891 end
2892
2893 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2894 id_str = to_string(activity.id)
2895
2896 assert %{"id" => ^id_str, "muted" => true} =
2897 conn
2898 |> assign(:user, user)
2899 |> post("/api/v1/statuses/#{activity.id}/mute")
2900 |> json_response(200)
2901 end
2902
2903 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2904 {:ok, _} = CommonAPI.add_mute(user, activity)
2905
2906 conn =
2907 conn
2908 |> assign(:user, user)
2909 |> post("/api/v1/statuses/#{activity.id}/mute")
2910
2911 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2912 end
2913
2914 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2915 {:ok, _} = CommonAPI.add_mute(user, activity)
2916
2917 id_str = to_string(activity.id)
2918 user = refresh_record(user)
2919
2920 assert %{"id" => ^id_str, "muted" => false} =
2921 conn
2922 |> assign(:user, user)
2923 |> post("/api/v1/statuses/#{activity.id}/unmute")
2924 |> json_response(200)
2925 end
2926 end
2927
2928 describe "reports" do
2929 setup do
2930 reporter = insert(:user)
2931 target_user = insert(:user)
2932
2933 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2934
2935 [reporter: reporter, target_user: target_user, activity: activity]
2936 end
2937
2938 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2939 assert %{"action_taken" => false, "id" => _} =
2940 conn
2941 |> assign(:user, reporter)
2942 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2943 |> json_response(200)
2944 end
2945
2946 test "submit a report with statuses and comment", %{
2947 conn: conn,
2948 reporter: reporter,
2949 target_user: target_user,
2950 activity: activity
2951 } do
2952 assert %{"action_taken" => false, "id" => _} =
2953 conn
2954 |> assign(:user, reporter)
2955 |> post("/api/v1/reports", %{
2956 "account_id" => target_user.id,
2957 "status_ids" => [activity.id],
2958 "comment" => "bad status!",
2959 "forward" => "false"
2960 })
2961 |> json_response(200)
2962 end
2963
2964 test "account_id is required", %{
2965 conn: conn,
2966 reporter: reporter,
2967 activity: activity
2968 } do
2969 assert %{"error" => "Valid `account_id` required"} =
2970 conn
2971 |> assign(:user, reporter)
2972 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2973 |> json_response(400)
2974 end
2975
2976 test "comment must be up to the size specified in the config", %{
2977 conn: conn,
2978 reporter: reporter,
2979 target_user: target_user
2980 } do
2981 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2982 comment = String.pad_trailing("a", max_size + 1, "a")
2983
2984 error = %{"error" => "Comment must be up to #{max_size} characters"}
2985
2986 assert ^error =
2987 conn
2988 |> assign(:user, reporter)
2989 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2990 |> json_response(400)
2991 end
2992
2993 test "returns error when account is not exist", %{
2994 conn: conn,
2995 reporter: reporter,
2996 activity: activity
2997 } do
2998 conn =
2999 conn
3000 |> assign(:user, reporter)
3001 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3002
3003 assert json_response(conn, 400) == %{"error" => "Account not found"}
3004 end
3005 end
3006
3007 describe "link headers" do
3008 test "preserves parameters in link headers", %{conn: conn} do
3009 user = insert(:user)
3010 other_user = insert(:user)
3011
3012 {:ok, activity1} =
3013 CommonAPI.post(other_user, %{
3014 "status" => "hi @#{user.nickname}",
3015 "visibility" => "public"
3016 })
3017
3018 {:ok, activity2} =
3019 CommonAPI.post(other_user, %{
3020 "status" => "hi @#{user.nickname}",
3021 "visibility" => "public"
3022 })
3023
3024 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3025 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3026
3027 conn =
3028 conn
3029 |> assign(:user, user)
3030 |> get("/api/v1/notifications", %{media_only: true})
3031
3032 assert [link_header] = get_resp_header(conn, "link")
3033 assert link_header =~ ~r/media_only=true/
3034 assert link_header =~ ~r/min_id=#{notification2.id}/
3035 assert link_header =~ ~r/max_id=#{notification1.id}/
3036 end
3037 end
3038
3039 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3040 # Need to set an old-style integer ID to reproduce the problem
3041 # (these are no longer assigned to new accounts but were preserved
3042 # for existing accounts during the migration to flakeIDs)
3043 user_one = insert(:user, %{id: 1212})
3044 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3045
3046 resp_one =
3047 conn
3048 |> get("/api/v1/accounts/#{user_one.id}")
3049
3050 resp_two =
3051 conn
3052 |> get("/api/v1/accounts/#{user_two.nickname}")
3053
3054 resp_three =
3055 conn
3056 |> get("/api/v1/accounts/#{user_two.id}")
3057
3058 acc_one = json_response(resp_one, 200)
3059 acc_two = json_response(resp_two, 200)
3060 acc_three = json_response(resp_three, 200)
3061 refute acc_one == acc_two
3062 assert acc_two == acc_three
3063 end
3064
3065 describe "custom emoji" do
3066 test "with tags", %{conn: conn} do
3067 [emoji | _body] =
3068 conn
3069 |> get("/api/v1/custom_emojis")
3070 |> json_response(200)
3071
3072 assert Map.has_key?(emoji, "shortcode")
3073 assert Map.has_key?(emoji, "static_url")
3074 assert Map.has_key?(emoji, "tags")
3075 assert is_list(emoji["tags"])
3076 assert Map.has_key?(emoji, "category")
3077 assert Map.has_key?(emoji, "url")
3078 assert Map.has_key?(emoji, "visible_in_picker")
3079 end
3080 end
3081
3082 describe "index/2 redirections" do
3083 setup %{conn: conn} do
3084 session_opts = [
3085 store: :cookie,
3086 key: "_test",
3087 signing_salt: "cooldude"
3088 ]
3089
3090 conn =
3091 conn
3092 |> Plug.Session.call(Plug.Session.init(session_opts))
3093 |> fetch_session()
3094
3095 test_path = "/web/statuses/test"
3096 %{conn: conn, path: test_path}
3097 end
3098
3099 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3100 conn = get(conn, path)
3101
3102 assert conn.status == 302
3103 assert redirected_to(conn) == "/web/login"
3104 end
3105
3106 test "redirects not logged-in users to the login page on private instances", %{
3107 conn: conn,
3108 path: path
3109 } do
3110 Config.put([:instance, :public], false)
3111
3112 conn = get(conn, path)
3113
3114 assert conn.status == 302
3115 assert redirected_to(conn) == "/web/login"
3116 end
3117
3118 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3119 token = insert(:oauth_token)
3120
3121 conn =
3122 conn
3123 |> assign(:user, token.user)
3124 |> put_session(:oauth_token, token.token)
3125 |> get(path)
3126
3127 assert conn.status == 200
3128 end
3129
3130 test "saves referer path to session", %{conn: conn, path: path} do
3131 conn = get(conn, path)
3132 return_to = Plug.Conn.get_session(conn, :return_to)
3133
3134 assert return_to == path
3135 end
3136
3137 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3138 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3139 auth = insert(:oauth_authorization, app: app)
3140
3141 conn =
3142 conn
3143 |> put_session(:return_to, path)
3144 |> get("/web/login", %{code: auth.token})
3145
3146 assert conn.status == 302
3147 assert redirected_to(conn) == path
3148 end
3149
3150 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3151 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3152 auth = insert(:oauth_authorization, app: app)
3153
3154 conn = get(conn, "/web/login", %{code: auth.token})
3155
3156 assert conn.status == 302
3157 assert redirected_to(conn) == "/web/getting-started"
3158 end
3159 end
3160
3161 describe "scheduled activities" do
3162 test "creates a scheduled activity", %{conn: conn} do
3163 user = insert(:user)
3164 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3165
3166 conn =
3167 conn
3168 |> assign(:user, user)
3169 |> post("/api/v1/statuses", %{
3170 "status" => "scheduled",
3171 "scheduled_at" => scheduled_at
3172 })
3173
3174 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3175 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3176 assert [] == Repo.all(Activity)
3177 end
3178
3179 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3180 user = insert(:user)
3181 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3182
3183 file = %Plug.Upload{
3184 content_type: "image/jpg",
3185 path: Path.absname("test/fixtures/image.jpg"),
3186 filename: "an_image.jpg"
3187 }
3188
3189 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3190
3191 conn =
3192 conn
3193 |> assign(:user, user)
3194 |> post("/api/v1/statuses", %{
3195 "media_ids" => [to_string(upload.id)],
3196 "status" => "scheduled",
3197 "scheduled_at" => scheduled_at
3198 })
3199
3200 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3201 assert %{"type" => "image"} = media_attachment
3202 end
3203
3204 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3205 %{conn: conn} do
3206 user = insert(:user)
3207
3208 scheduled_at =
3209 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3210
3211 conn =
3212 conn
3213 |> assign(:user, user)
3214 |> post("/api/v1/statuses", %{
3215 "status" => "not scheduled",
3216 "scheduled_at" => scheduled_at
3217 })
3218
3219 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3220 assert [] == Repo.all(ScheduledActivity)
3221 end
3222
3223 test "returns error when daily user limit is exceeded", %{conn: conn} do
3224 user = insert(:user)
3225
3226 today =
3227 NaiveDateTime.utc_now()
3228 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3229 |> NaiveDateTime.to_iso8601()
3230
3231 attrs = %{params: %{}, scheduled_at: today}
3232 {:ok, _} = ScheduledActivity.create(user, attrs)
3233 {:ok, _} = ScheduledActivity.create(user, attrs)
3234
3235 conn =
3236 conn
3237 |> assign(:user, user)
3238 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3239
3240 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3241 end
3242
3243 test "returns error when total user limit is exceeded", %{conn: conn} do
3244 user = insert(:user)
3245
3246 today =
3247 NaiveDateTime.utc_now()
3248 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3249 |> NaiveDateTime.to_iso8601()
3250
3251 tomorrow =
3252 NaiveDateTime.utc_now()
3253 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3254 |> NaiveDateTime.to_iso8601()
3255
3256 attrs = %{params: %{}, scheduled_at: today}
3257 {:ok, _} = ScheduledActivity.create(user, attrs)
3258 {:ok, _} = ScheduledActivity.create(user, attrs)
3259 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3260
3261 conn =
3262 conn
3263 |> assign(:user, user)
3264 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3265
3266 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3267 end
3268
3269 test "shows scheduled activities", %{conn: conn} do
3270 user = insert(:user)
3271 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3272 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3273 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3274 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3275
3276 conn =
3277 conn
3278 |> assign(:user, user)
3279
3280 # min_id
3281 conn_res =
3282 conn
3283 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3284
3285 result = json_response(conn_res, 200)
3286 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3287
3288 # since_id
3289 conn_res =
3290 conn
3291 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3292
3293 result = json_response(conn_res, 200)
3294 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3295
3296 # max_id
3297 conn_res =
3298 conn
3299 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3300
3301 result = json_response(conn_res, 200)
3302 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3303 end
3304
3305 test "shows a scheduled activity", %{conn: conn} do
3306 user = insert(:user)
3307 scheduled_activity = insert(:scheduled_activity, user: user)
3308
3309 res_conn =
3310 conn
3311 |> assign(:user, user)
3312 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3313
3314 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3315 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3316
3317 res_conn =
3318 conn
3319 |> assign(:user, user)
3320 |> get("/api/v1/scheduled_statuses/404")
3321
3322 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3323 end
3324
3325 test "updates a scheduled activity", %{conn: conn} do
3326 user = insert(:user)
3327 scheduled_activity = insert(:scheduled_activity, user: user)
3328
3329 new_scheduled_at =
3330 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3331
3332 res_conn =
3333 conn
3334 |> assign(:user, user)
3335 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3336 scheduled_at: new_scheduled_at
3337 })
3338
3339 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3340 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3341
3342 res_conn =
3343 conn
3344 |> assign(:user, user)
3345 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3346
3347 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3348 end
3349
3350 test "deletes a scheduled activity", %{conn: conn} do
3351 user = insert(:user)
3352 scheduled_activity = insert(:scheduled_activity, user: user)
3353
3354 res_conn =
3355 conn
3356 |> assign(:user, user)
3357 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3358
3359 assert %{} = json_response(res_conn, 200)
3360 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3361
3362 res_conn =
3363 conn
3364 |> assign(:user, user)
3365 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3366
3367 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3368 end
3369 end
3370
3371 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3372 user1 = insert(:user)
3373 user2 = insert(:user)
3374 user3 = insert(:user)
3375
3376 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3377
3378 # Reply to status from another user
3379 conn1 =
3380 conn
3381 |> assign(:user, user2)
3382 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3383
3384 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3385
3386 activity = Activity.get_by_id_with_object(id)
3387
3388 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3389 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3390
3391 # Reblog from the third user
3392 conn2 =
3393 conn
3394 |> assign(:user, user3)
3395 |> post("/api/v1/statuses/#{activity.id}/reblog")
3396
3397 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3398 json_response(conn2, 200)
3399
3400 assert to_string(activity.id) == id
3401
3402 # Getting third user status
3403 conn3 =
3404 conn
3405 |> assign(:user, user3)
3406 |> get("api/v1/timelines/home")
3407
3408 [reblogged_activity] = json_response(conn3, 200)
3409
3410 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3411
3412 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3413 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3414 end
3415
3416 describe "create account by app" do
3417 test "Account registration via Application", %{conn: conn} do
3418 conn =
3419 conn
3420 |> post("/api/v1/apps", %{
3421 client_name: "client_name",
3422 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3423 scopes: "read, write, follow"
3424 })
3425
3426 %{
3427 "client_id" => client_id,
3428 "client_secret" => client_secret,
3429 "id" => _,
3430 "name" => "client_name",
3431 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3432 "vapid_key" => _,
3433 "website" => nil
3434 } = json_response(conn, 200)
3435
3436 conn =
3437 conn
3438 |> post("/oauth/token", %{
3439 grant_type: "client_credentials",
3440 client_id: client_id,
3441 client_secret: client_secret
3442 })
3443
3444 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3445 json_response(conn, 200)
3446
3447 assert token
3448 token_from_db = Repo.get_by(Token, token: token)
3449 assert token_from_db
3450 assert refresh
3451 assert scope == "read write follow"
3452
3453 conn =
3454 build_conn()
3455 |> put_req_header("authorization", "Bearer " <> token)
3456 |> post("/api/v1/accounts", %{
3457 username: "lain",
3458 email: "lain@example.org",
3459 password: "PlzDontHackLain",
3460 agreement: true
3461 })
3462
3463 %{
3464 "access_token" => token,
3465 "created_at" => _created_at,
3466 "scope" => _scope,
3467 "token_type" => "Bearer"
3468 } = json_response(conn, 200)
3469
3470 token_from_db = Repo.get_by(Token, token: token)
3471 assert token_from_db
3472 token_from_db = Repo.preload(token_from_db, :user)
3473 assert token_from_db.user
3474
3475 assert token_from_db.user.info.confirmation_pending
3476 end
3477
3478 test "rate limit", %{conn: conn} do
3479 app_token = insert(:oauth_token, user: nil)
3480
3481 conn =
3482 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3483 |> Map.put(:remote_ip, {15, 15, 15, 15})
3484
3485 for i <- 1..5 do
3486 conn =
3487 conn
3488 |> post("/api/v1/accounts", %{
3489 username: "#{i}lain",
3490 email: "#{i}lain@example.org",
3491 password: "PlzDontHackLain",
3492 agreement: true
3493 })
3494
3495 %{
3496 "access_token" => token,
3497 "created_at" => _created_at,
3498 "scope" => _scope,
3499 "token_type" => "Bearer"
3500 } = json_response(conn, 200)
3501
3502 token_from_db = Repo.get_by(Token, token: token)
3503 assert token_from_db
3504 token_from_db = Repo.preload(token_from_db, :user)
3505 assert token_from_db.user
3506
3507 assert token_from_db.user.info.confirmation_pending
3508 end
3509
3510 conn =
3511 conn
3512 |> post("/api/v1/accounts", %{
3513 username: "6lain",
3514 email: "6lain@example.org",
3515 password: "PlzDontHackLain",
3516 agreement: true
3517 })
3518
3519 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3520 end
3521 end
3522
3523 describe "GET /api/v1/polls/:id" do
3524 test "returns poll entity for object id", %{conn: conn} do
3525 user = insert(:user)
3526
3527 {:ok, activity} =
3528 CommonAPI.post(user, %{
3529 "status" => "Pleroma does",
3530 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3531 })
3532
3533 object = Object.normalize(activity)
3534
3535 conn =
3536 conn
3537 |> assign(:user, user)
3538 |> get("/api/v1/polls/#{object.id}")
3539
3540 response = json_response(conn, 200)
3541 id = to_string(object.id)
3542 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3543 end
3544
3545 test "does not expose polls for private statuses", %{conn: conn} do
3546 user = insert(:user)
3547 other_user = insert(:user)
3548
3549 {:ok, activity} =
3550 CommonAPI.post(user, %{
3551 "status" => "Pleroma does",
3552 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3553 "visibility" => "private"
3554 })
3555
3556 object = Object.normalize(activity)
3557
3558 conn =
3559 conn
3560 |> assign(:user, other_user)
3561 |> get("/api/v1/polls/#{object.id}")
3562
3563 assert json_response(conn, 404)
3564 end
3565 end
3566
3567 describe "POST /api/v1/polls/:id/votes" do
3568 test "votes are added to the poll", %{conn: conn} do
3569 user = insert(:user)
3570 other_user = insert(:user)
3571
3572 {:ok, activity} =
3573 CommonAPI.post(user, %{
3574 "status" => "A very delicious sandwich",
3575 "poll" => %{
3576 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3577 "expires_in" => 20,
3578 "multiple" => true
3579 }
3580 })
3581
3582 object = Object.normalize(activity)
3583
3584 conn =
3585 conn
3586 |> assign(:user, other_user)
3587 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3588
3589 assert json_response(conn, 200)
3590 object = Object.get_by_id(object.id)
3591
3592 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3593 total_items == 1
3594 end)
3595 end
3596
3597 test "author can't vote", %{conn: conn} do
3598 user = insert(:user)
3599
3600 {:ok, activity} =
3601 CommonAPI.post(user, %{
3602 "status" => "Am I cute?",
3603 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3604 })
3605
3606 object = Object.normalize(activity)
3607
3608 assert conn
3609 |> assign(:user, user)
3610 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3611 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3612
3613 object = Object.get_by_id(object.id)
3614
3615 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3616 end
3617
3618 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3619 user = insert(:user)
3620 other_user = insert(:user)
3621
3622 {:ok, activity} =
3623 CommonAPI.post(user, %{
3624 "status" => "The glass is",
3625 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3626 })
3627
3628 object = Object.normalize(activity)
3629
3630 assert conn
3631 |> assign(:user, other_user)
3632 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3633 |> json_response(422) == %{"error" => "Too many choices"}
3634
3635 object = Object.get_by_id(object.id)
3636
3637 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3638 total_items == 1
3639 end)
3640 end
3641
3642 test "does not allow choice index to be greater than options count", %{conn: conn} do
3643 user = insert(:user)
3644 other_user = insert(:user)
3645
3646 {:ok, activity} =
3647 CommonAPI.post(user, %{
3648 "status" => "Am I cute?",
3649 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3650 })
3651
3652 object = Object.normalize(activity)
3653
3654 conn =
3655 conn
3656 |> assign(:user, other_user)
3657 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3658
3659 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3660 end
3661
3662 test "returns 404 error when object is not exist", %{conn: conn} do
3663 user = insert(:user)
3664
3665 conn =
3666 conn
3667 |> assign(:user, user)
3668 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3669
3670 assert json_response(conn, 404) == %{"error" => "Record not found"}
3671 end
3672
3673 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3674 user = insert(:user)
3675 other_user = insert(:user)
3676
3677 {:ok, activity} =
3678 CommonAPI.post(user, %{
3679 "status" => "Am I cute?",
3680 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3681 "visibility" => "private"
3682 })
3683
3684 object = Object.normalize(activity)
3685
3686 conn =
3687 conn
3688 |> assign(:user, other_user)
3689 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3690
3691 assert json_response(conn, 404) == %{"error" => "Record not found"}
3692 end
3693 end
3694
3695 describe "GET /api/v1/statuses/:id/favourited_by" do
3696 setup do
3697 user = insert(:user)
3698 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3699
3700 conn =
3701 build_conn()
3702 |> assign(:user, user)
3703
3704 [conn: conn, activity: activity, user: user]
3705 end
3706
3707 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3708 other_user = insert(:user)
3709 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3710
3711 response =
3712 conn
3713 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3714 |> json_response(:ok)
3715
3716 [%{"id" => id}] = response
3717
3718 assert id == other_user.id
3719 end
3720
3721 test "returns empty array when status has not been favorited yet", %{
3722 conn: conn,
3723 activity: activity
3724 } do
3725 response =
3726 conn
3727 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3728 |> json_response(:ok)
3729
3730 assert Enum.empty?(response)
3731 end
3732
3733 test "does not return users who have favorited the status but are blocked", %{
3734 conn: %{assigns: %{user: user}} = conn,
3735 activity: activity
3736 } do
3737 other_user = insert(:user)
3738 {:ok, user} = User.block(user, other_user)
3739
3740 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3741
3742 response =
3743 conn
3744 |> assign(:user, user)
3745 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3746 |> json_response(:ok)
3747
3748 assert Enum.empty?(response)
3749 end
3750
3751 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3752 other_user = insert(:user)
3753 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3754
3755 response =
3756 conn
3757 |> assign(:user, nil)
3758 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3759 |> json_response(:ok)
3760
3761 [%{"id" => id}] = response
3762 assert id == other_user.id
3763 end
3764
3765 test "requires authentification for private posts", %{conn: conn, user: user} do
3766 other_user = insert(:user)
3767
3768 {:ok, activity} =
3769 CommonAPI.post(user, %{
3770 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3771 "visibility" => "direct"
3772 })
3773
3774 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3775
3776 conn
3777 |> assign(:user, nil)
3778 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3779 |> json_response(404)
3780
3781 response =
3782 build_conn()
3783 |> assign(:user, other_user)
3784 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3785 |> json_response(200)
3786
3787 [%{"id" => id}] = response
3788 assert id == other_user.id
3789 end
3790 end
3791
3792 describe "GET /api/v1/statuses/:id/reblogged_by" do
3793 setup do
3794 user = insert(:user)
3795 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3796
3797 conn =
3798 build_conn()
3799 |> assign(:user, user)
3800
3801 [conn: conn, activity: activity, user: user]
3802 end
3803
3804 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3805 other_user = insert(:user)
3806 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3807
3808 response =
3809 conn
3810 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3811 |> json_response(:ok)
3812
3813 [%{"id" => id}] = response
3814
3815 assert id == other_user.id
3816 end
3817
3818 test "returns empty array when status has not been reblogged yet", %{
3819 conn: conn,
3820 activity: activity
3821 } do
3822 response =
3823 conn
3824 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3825 |> json_response(:ok)
3826
3827 assert Enum.empty?(response)
3828 end
3829
3830 test "does not return users who have reblogged the status but are blocked", %{
3831 conn: %{assigns: %{user: user}} = conn,
3832 activity: activity
3833 } do
3834 other_user = insert(:user)
3835 {:ok, user} = User.block(user, other_user)
3836
3837 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3838
3839 response =
3840 conn
3841 |> assign(:user, user)
3842 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3843 |> json_response(:ok)
3844
3845 assert Enum.empty?(response)
3846 end
3847
3848 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3849 other_user = insert(:user)
3850 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3851
3852 response =
3853 conn
3854 |> assign(:user, nil)
3855 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3856 |> json_response(:ok)
3857
3858 [%{"id" => id}] = response
3859 assert id == other_user.id
3860 end
3861
3862 test "requires authentification for private posts", %{conn: conn, user: user} do
3863 other_user = insert(:user)
3864
3865 {:ok, activity} =
3866 CommonAPI.post(user, %{
3867 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3868 "visibility" => "direct"
3869 })
3870
3871 conn
3872 |> assign(:user, nil)
3873 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3874 |> json_response(404)
3875
3876 response =
3877 build_conn()
3878 |> assign(:user, other_user)
3879 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3880 |> json_response(200)
3881
3882 assert [] == response
3883 end
3884 end
3885
3886 describe "POST /auth/password, with valid parameters" do
3887 setup %{conn: conn} do
3888 user = insert(:user)
3889 conn = post(conn, "/auth/password?email=#{user.email}")
3890 %{conn: conn, user: user}
3891 end
3892
3893 test "it returns 204", %{conn: conn} do
3894 assert json_response(conn, :no_content)
3895 end
3896
3897 test "it creates a PasswordResetToken record for user", %{user: user} do
3898 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3899 assert token_record
3900 end
3901
3902 test "it sends an email to user", %{user: user} do
3903 ObanHelpers.perform_all()
3904 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3905
3906 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3907 notify_email = Config.get([:instance, :notify_email])
3908 instance_name = Config.get([:instance, :name])
3909
3910 assert_email_sent(
3911 from: {instance_name, notify_email},
3912 to: {user.name, user.email},
3913 html_body: email.html_body
3914 )
3915 end
3916 end
3917
3918 describe "POST /auth/password, with invalid parameters" do
3919 setup do
3920 user = insert(:user)
3921 {:ok, user: user}
3922 end
3923
3924 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3925 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3926 assert conn.status == 404
3927 assert conn.resp_body == ""
3928 end
3929
3930 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3931 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3932 conn = post(conn, "/auth/password?email=#{user.email}")
3933 assert conn.status == 400
3934 assert conn.resp_body == ""
3935 end
3936 end
3937
3938 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3939 setup do
3940 user = insert(:user)
3941 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3942
3943 {:ok, user} =
3944 user
3945 |> Changeset.change()
3946 |> Changeset.put_embed(:info, info_change)
3947 |> Repo.update()
3948
3949 assert user.info.confirmation_pending
3950
3951 [user: user]
3952 end
3953
3954 clear_config([:instance, :account_activation_required]) do
3955 Config.put([:instance, :account_activation_required], true)
3956 end
3957
3958 test "resend account confirmation email", %{conn: conn, user: user} do
3959 conn
3960 |> assign(:user, user)
3961 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3962 |> json_response(:no_content)
3963
3964 ObanHelpers.perform_all()
3965
3966 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3967 notify_email = Config.get([:instance, :notify_email])
3968 instance_name = Config.get([:instance, :name])
3969
3970 assert_email_sent(
3971 from: {instance_name, notify_email},
3972 to: {user.name, user.email},
3973 html_body: email.html_body
3974 )
3975 end
3976 end
3977
3978 describe "GET /api/v1/suggestions" do
3979 setup do
3980 user = insert(:user)
3981 other_user = insert(:user)
3982 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3983 url500 = "http://test500?#{host}&#{user.nickname}"
3984 url200 = "http://test200?#{host}&#{user.nickname}"
3985
3986 mock(fn
3987 %{method: :get, url: ^url500} ->
3988 %Tesla.Env{status: 500, body: "bad request"}
3989
3990 %{method: :get, url: ^url200} ->
3991 %Tesla.Env{
3992 status: 200,
3993 body:
3994 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
3995 other_user.ap_id
3996 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
3997 }
3998 end)
3999
4000 [user: user, other_user: other_user]
4001 end
4002
4003 clear_config(:suggestions)
4004
4005 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4006 Config.put([:suggestions, :enabled], false)
4007
4008 res =
4009 conn
4010 |> assign(:user, user)
4011 |> get("/api/v1/suggestions")
4012 |> json_response(200)
4013
4014 assert res == []
4015 end
4016
4017 test "returns error", %{conn: conn, user: user} do
4018 Config.put([:suggestions, :enabled], true)
4019 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4020
4021 assert capture_log(fn ->
4022 res =
4023 conn
4024 |> assign(:user, user)
4025 |> get("/api/v1/suggestions")
4026 |> json_response(500)
4027
4028 assert res == "Something went wrong"
4029 end) =~ "Could not retrieve suggestions"
4030 end
4031
4032 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4033 Config.put([:suggestions, :enabled], true)
4034 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4035
4036 res =
4037 conn
4038 |> assign(:user, user)
4039 |> get("/api/v1/suggestions")
4040 |> json_response(200)
4041
4042 assert res == [
4043 %{
4044 "acct" => "yj455",
4045 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4046 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4047 "id" => 0
4048 },
4049 %{
4050 "acct" => other_user.ap_id,
4051 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4052 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4053 "id" => other_user.id
4054 }
4055 ]
4056 end
4057 end
4058 end