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