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