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