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