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