Apply suggestion to test/web/mastodon_api/mastodon_api_controller_test.exs
[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
1555 test "returns an empty list on a bad request", %{conn: conn} do
1556 user = insert(:user)
1557
1558 conn =
1559 conn
1560 |> assign(:user, user)
1561 |> get("/api/v1/accounts/relationships", %{})
1562
1563 assert [] = json_response(conn, 200)
1564 end
1565 end
1566
1567 describe "media upload" do
1568 setup do
1569 user = insert(:user)
1570
1571 conn =
1572 build_conn()
1573 |> assign(:user, user)
1574
1575 image = %Plug.Upload{
1576 content_type: "image/jpg",
1577 path: Path.absname("test/fixtures/image.jpg"),
1578 filename: "an_image.jpg"
1579 }
1580
1581 [conn: conn, image: image]
1582 end
1583
1584 clear_config([:media_proxy])
1585 clear_config([Pleroma.Upload])
1586
1587 test "returns uploaded image", %{conn: conn, image: image} do
1588 desc = "Description of the image"
1589
1590 media =
1591 conn
1592 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1593 |> json_response(:ok)
1594
1595 assert media["type"] == "image"
1596 assert media["description"] == desc
1597 assert media["id"]
1598
1599 object = Repo.get(Object, media["id"])
1600 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1601 end
1602 end
1603
1604 describe "locked accounts" do
1605 test "/api/v1/follow_requests works" do
1606 user = insert(:user, %{info: %User.Info{locked: true}})
1607 other_user = insert(:user)
1608
1609 {:ok, _activity} = ActivityPub.follow(other_user, user)
1610
1611 user = User.get_cached_by_id(user.id)
1612 other_user = User.get_cached_by_id(other_user.id)
1613
1614 assert User.following?(other_user, user) == false
1615
1616 conn =
1617 build_conn()
1618 |> assign(:user, user)
1619 |> get("/api/v1/follow_requests")
1620
1621 assert [relationship] = json_response(conn, 200)
1622 assert to_string(other_user.id) == relationship["id"]
1623 end
1624
1625 test "/api/v1/follow_requests/:id/authorize works" do
1626 user = insert(:user, %{info: %User.Info{locked: true}})
1627 other_user = insert(:user)
1628
1629 {:ok, _activity} = ActivityPub.follow(other_user, user)
1630
1631 user = User.get_cached_by_id(user.id)
1632 other_user = User.get_cached_by_id(other_user.id)
1633
1634 assert User.following?(other_user, user) == false
1635
1636 conn =
1637 build_conn()
1638 |> assign(:user, user)
1639 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1640
1641 assert relationship = json_response(conn, 200)
1642 assert to_string(other_user.id) == relationship["id"]
1643
1644 user = User.get_cached_by_id(user.id)
1645 other_user = User.get_cached_by_id(other_user.id)
1646
1647 assert User.following?(other_user, user) == true
1648 end
1649
1650 test "verify_credentials", %{conn: conn} do
1651 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1652
1653 conn =
1654 conn
1655 |> assign(:user, user)
1656 |> get("/api/v1/accounts/verify_credentials")
1657
1658 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1659 assert id == to_string(user.id)
1660 end
1661
1662 test "/api/v1/follow_requests/:id/reject works" do
1663 user = insert(:user, %{info: %User.Info{locked: true}})
1664 other_user = insert(:user)
1665
1666 {:ok, _activity} = ActivityPub.follow(other_user, user)
1667
1668 user = User.get_cached_by_id(user.id)
1669
1670 conn =
1671 build_conn()
1672 |> assign(:user, user)
1673 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1674
1675 assert relationship = json_response(conn, 200)
1676 assert to_string(other_user.id) == relationship["id"]
1677
1678 user = User.get_cached_by_id(user.id)
1679 other_user = User.get_cached_by_id(other_user.id)
1680
1681 assert User.following?(other_user, user) == false
1682 end
1683 end
1684
1685 describe "account fetching" do
1686 test "works by id" do
1687 user = insert(:user)
1688
1689 conn =
1690 build_conn()
1691 |> get("/api/v1/accounts/#{user.id}")
1692
1693 assert %{"id" => id} = json_response(conn, 200)
1694 assert id == to_string(user.id)
1695
1696 conn =
1697 build_conn()
1698 |> get("/api/v1/accounts/-1")
1699
1700 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1701 end
1702
1703 test "works by nickname" do
1704 user = insert(:user)
1705
1706 conn =
1707 build_conn()
1708 |> get("/api/v1/accounts/#{user.nickname}")
1709
1710 assert %{"id" => id} = json_response(conn, 200)
1711 assert id == user.id
1712 end
1713
1714 test "works by nickname for remote users" do
1715 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1716 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1717 user = insert(:user, nickname: "user@example.com", local: false)
1718
1719 conn =
1720 build_conn()
1721 |> get("/api/v1/accounts/#{user.nickname}")
1722
1723 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1724 assert %{"id" => id} = json_response(conn, 200)
1725 assert id == user.id
1726 end
1727
1728 test "respects limit_to_local_content == :all for remote user nicknames" do
1729 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1730 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1731
1732 user = insert(:user, nickname: "user@example.com", local: false)
1733
1734 conn =
1735 build_conn()
1736 |> get("/api/v1/accounts/#{user.nickname}")
1737
1738 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1739 assert json_response(conn, 404)
1740 end
1741
1742 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1743 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1744 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1745
1746 user = insert(:user, nickname: "user@example.com", local: false)
1747 reading_user = insert(:user)
1748
1749 conn =
1750 build_conn()
1751 |> get("/api/v1/accounts/#{user.nickname}")
1752
1753 assert json_response(conn, 404)
1754
1755 conn =
1756 build_conn()
1757 |> assign(:user, reading_user)
1758 |> get("/api/v1/accounts/#{user.nickname}")
1759
1760 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1761 assert %{"id" => id} = json_response(conn, 200)
1762 assert id == user.id
1763 end
1764 end
1765
1766 describe "/api/v1/pleroma/mascot" do
1767 test "mascot upload", %{conn: conn} do
1768 user = insert(:user)
1769
1770 non_image_file = %Plug.Upload{
1771 content_type: "audio/mpeg",
1772 path: Path.absname("test/fixtures/sound.mp3"),
1773 filename: "sound.mp3"
1774 }
1775
1776 conn =
1777 conn
1778 |> assign(:user, user)
1779 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1780
1781 assert json_response(conn, 415)
1782
1783 file = %Plug.Upload{
1784 content_type: "image/jpg",
1785 path: Path.absname("test/fixtures/image.jpg"),
1786 filename: "an_image.jpg"
1787 }
1788
1789 conn =
1790 build_conn()
1791 |> assign(:user, user)
1792 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1793
1794 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1795 end
1796
1797 test "mascot retrieving", %{conn: conn} do
1798 user = insert(:user)
1799 # When user hasn't set a mascot, we should just get pleroma tan back
1800 conn =
1801 conn
1802 |> assign(:user, user)
1803 |> get("/api/v1/pleroma/mascot")
1804
1805 assert %{"url" => url} = json_response(conn, 200)
1806 assert url =~ "pleroma-fox-tan-smol"
1807
1808 # When a user sets their mascot, we should get that back
1809 file = %Plug.Upload{
1810 content_type: "image/jpg",
1811 path: Path.absname("test/fixtures/image.jpg"),
1812 filename: "an_image.jpg"
1813 }
1814
1815 conn =
1816 build_conn()
1817 |> assign(:user, user)
1818 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1819
1820 assert json_response(conn, 200)
1821
1822 user = User.get_cached_by_id(user.id)
1823
1824 conn =
1825 build_conn()
1826 |> assign(:user, user)
1827 |> get("/api/v1/pleroma/mascot")
1828
1829 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1830 assert url =~ "an_image"
1831 end
1832 end
1833
1834 test "hashtag timeline", %{conn: conn} do
1835 following = insert(:user)
1836
1837 capture_log(fn ->
1838 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
1839
1840 {:ok, [_activity]} =
1841 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1842
1843 nconn =
1844 conn
1845 |> get("/api/v1/timelines/tag/2hu")
1846
1847 assert [%{"id" => id}] = json_response(nconn, 200)
1848
1849 assert id == to_string(activity.id)
1850
1851 # works for different capitalization too
1852 nconn =
1853 conn
1854 |> get("/api/v1/timelines/tag/2HU")
1855
1856 assert [%{"id" => id}] = json_response(nconn, 200)
1857
1858 assert id == to_string(activity.id)
1859 end)
1860 end
1861
1862 test "multi-hashtag timeline", %{conn: conn} do
1863 user = insert(:user)
1864
1865 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1866 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1867 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1868
1869 any_test =
1870 conn
1871 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1872
1873 [status_none, status_test1, status_test] = json_response(any_test, 200)
1874
1875 assert to_string(activity_test.id) == status_test["id"]
1876 assert to_string(activity_test1.id) == status_test1["id"]
1877 assert to_string(activity_none.id) == status_none["id"]
1878
1879 restricted_test =
1880 conn
1881 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1882
1883 assert [status_test1] == json_response(restricted_test, 200)
1884
1885 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1886
1887 assert [status_none] == json_response(all_test, 200)
1888 end
1889
1890 test "getting followers", %{conn: conn} do
1891 user = insert(:user)
1892 other_user = insert(:user)
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 [%{"id" => id}] = json_response(conn, 200)
1900 assert id == to_string(user.id)
1901 end
1902
1903 test "getting followers, hide_followers", %{conn: conn} do
1904 user = insert(:user)
1905 other_user = insert(:user, %{info: %{hide_followers: true}})
1906 {:ok, _user} = User.follow(user, other_user)
1907
1908 conn =
1909 conn
1910 |> get("/api/v1/accounts/#{other_user.id}/followers")
1911
1912 assert [] == json_response(conn, 200)
1913 end
1914
1915 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1916 user = insert(:user)
1917 other_user = insert(:user, %{info: %{hide_followers: true}})
1918 {:ok, _user} = User.follow(user, other_user)
1919
1920 conn =
1921 conn
1922 |> assign(:user, other_user)
1923 |> get("/api/v1/accounts/#{other_user.id}/followers")
1924
1925 refute [] == json_response(conn, 200)
1926 end
1927
1928 test "getting followers, pagination", %{conn: conn} do
1929 user = insert(:user)
1930 follower1 = insert(:user)
1931 follower2 = insert(:user)
1932 follower3 = insert(:user)
1933 {:ok, _} = User.follow(follower1, user)
1934 {:ok, _} = User.follow(follower2, user)
1935 {:ok, _} = User.follow(follower3, user)
1936
1937 conn =
1938 conn
1939 |> assign(:user, user)
1940
1941 res_conn =
1942 conn
1943 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1944
1945 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1946 assert id3 == follower3.id
1947 assert id2 == follower2.id
1948
1949 res_conn =
1950 conn
1951 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1952
1953 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1954 assert id2 == follower2.id
1955 assert id1 == follower1.id
1956
1957 res_conn =
1958 conn
1959 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1960
1961 assert [%{"id" => id2}] = json_response(res_conn, 200)
1962 assert id2 == follower2.id
1963
1964 assert [link_header] = get_resp_header(res_conn, "link")
1965 assert link_header =~ ~r/min_id=#{follower2.id}/
1966 assert link_header =~ ~r/max_id=#{follower2.id}/
1967 end
1968
1969 test "getting following", %{conn: conn} do
1970 user = insert(:user)
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 [%{"id" => id}] = json_response(conn, 200)
1979 assert id == to_string(other_user.id)
1980 end
1981
1982 test "getting following, hide_follows", %{conn: conn} do
1983 user = insert(:user, %{info: %{hide_follows: true}})
1984 other_user = insert(:user)
1985 {:ok, user} = User.follow(user, other_user)
1986
1987 conn =
1988 conn
1989 |> get("/api/v1/accounts/#{user.id}/following")
1990
1991 assert [] == json_response(conn, 200)
1992 end
1993
1994 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1995 user = insert(:user, %{info: %{hide_follows: true}})
1996 other_user = insert(:user)
1997 {:ok, user} = User.follow(user, other_user)
1998
1999 conn =
2000 conn
2001 |> assign(:user, user)
2002 |> get("/api/v1/accounts/#{user.id}/following")
2003
2004 refute [] == json_response(conn, 200)
2005 end
2006
2007 test "getting following, pagination", %{conn: conn} do
2008 user = insert(:user)
2009 following1 = insert(:user)
2010 following2 = insert(:user)
2011 following3 = insert(:user)
2012 {:ok, _} = User.follow(user, following1)
2013 {:ok, _} = User.follow(user, following2)
2014 {:ok, _} = User.follow(user, following3)
2015
2016 conn =
2017 conn
2018 |> assign(:user, user)
2019
2020 res_conn =
2021 conn
2022 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2023
2024 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2025 assert id3 == following3.id
2026 assert id2 == following2.id
2027
2028 res_conn =
2029 conn
2030 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2031
2032 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2033 assert id2 == following2.id
2034 assert id1 == following1.id
2035
2036 res_conn =
2037 conn
2038 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2039
2040 assert [%{"id" => id2}] = json_response(res_conn, 200)
2041 assert id2 == following2.id
2042
2043 assert [link_header] = get_resp_header(res_conn, "link")
2044 assert link_header =~ ~r/min_id=#{following2.id}/
2045 assert link_header =~ ~r/max_id=#{following2.id}/
2046 end
2047
2048 test "following / unfollowing a user", %{conn: conn} do
2049 user = insert(:user)
2050 other_user = insert(:user)
2051
2052 conn =
2053 conn
2054 |> assign(:user, user)
2055 |> post("/api/v1/accounts/#{other_user.id}/follow")
2056
2057 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2058
2059 user = User.get_cached_by_id(user.id)
2060
2061 conn =
2062 build_conn()
2063 |> assign(:user, user)
2064 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2065
2066 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2067
2068 user = User.get_cached_by_id(user.id)
2069
2070 conn =
2071 build_conn()
2072 |> assign(:user, user)
2073 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2074
2075 assert %{"id" => id} = json_response(conn, 200)
2076 assert id == to_string(other_user.id)
2077 end
2078
2079 test "following without reblogs" do
2080 follower = insert(:user)
2081 followed = insert(:user)
2082 other_user = insert(:user)
2083
2084 conn =
2085 build_conn()
2086 |> assign(:user, follower)
2087 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2088
2089 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2090
2091 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2092 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2093
2094 conn =
2095 build_conn()
2096 |> assign(:user, User.get_cached_by_id(follower.id))
2097 |> get("/api/v1/timelines/home")
2098
2099 assert [] == json_response(conn, 200)
2100
2101 conn =
2102 build_conn()
2103 |> assign(:user, follower)
2104 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2105
2106 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2107
2108 conn =
2109 build_conn()
2110 |> assign(:user, User.get_cached_by_id(follower.id))
2111 |> get("/api/v1/timelines/home")
2112
2113 expected_activity_id = reblog.id
2114 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2115 end
2116
2117 test "following / unfollowing errors" do
2118 user = insert(:user)
2119
2120 conn =
2121 build_conn()
2122 |> assign(:user, user)
2123
2124 # self follow
2125 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2126 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2127
2128 # self unfollow
2129 user = User.get_cached_by_id(user.id)
2130 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2131 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2132
2133 # self follow via uri
2134 user = User.get_cached_by_id(user.id)
2135 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2136 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2137
2138 # follow non existing user
2139 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2140 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2141
2142 # follow non existing user via uri
2143 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2144 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2145
2146 # unfollow non existing user
2147 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2148 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2149 end
2150
2151 describe "mute/unmute" do
2152 test "with notifications", %{conn: conn} do
2153 user = insert(:user)
2154 other_user = insert(:user)
2155
2156 conn =
2157 conn
2158 |> assign(:user, user)
2159 |> post("/api/v1/accounts/#{other_user.id}/mute")
2160
2161 response = json_response(conn, 200)
2162
2163 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2164 user = User.get_cached_by_id(user.id)
2165
2166 conn =
2167 build_conn()
2168 |> assign(:user, user)
2169 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2170
2171 response = json_response(conn, 200)
2172 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2173 end
2174
2175 test "without notifications", %{conn: conn} do
2176 user = insert(:user)
2177 other_user = insert(:user)
2178
2179 conn =
2180 conn
2181 |> assign(:user, user)
2182 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2183
2184 response = json_response(conn, 200)
2185
2186 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2187 user = User.get_cached_by_id(user.id)
2188
2189 conn =
2190 build_conn()
2191 |> assign(:user, user)
2192 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2193
2194 response = json_response(conn, 200)
2195 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2196 end
2197 end
2198
2199 describe "subscribing / unsubscribing" do
2200 test "subscribing / unsubscribing to a user", %{conn: conn} do
2201 user = insert(:user)
2202 subscription_target = insert(:user)
2203
2204 conn =
2205 conn
2206 |> assign(:user, user)
2207 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2208
2209 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2210
2211 conn =
2212 build_conn()
2213 |> assign(:user, user)
2214 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2215
2216 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2217 end
2218 end
2219
2220 describe "subscribing" do
2221 test "returns 404 when subscription_target not found", %{conn: conn} do
2222 user = insert(:user)
2223
2224 conn =
2225 conn
2226 |> assign(:user, user)
2227 |> post("/api/v1/pleroma/accounts/target_id/subscribe")
2228
2229 assert %{"error" => "Record not found"} = json_response(conn, 404)
2230 end
2231 end
2232
2233 describe "unsubscribing" do
2234 test "returns 404 when subscription_target not found", %{conn: conn} do
2235 user = insert(:user)
2236
2237 conn =
2238 conn
2239 |> assign(:user, user)
2240 |> post("/api/v1/pleroma/accounts/target_id/unsubscribe")
2241
2242 assert %{"error" => "Record not found"} = json_response(conn, 404)
2243 end
2244 end
2245
2246 test "getting a list of mutes", %{conn: conn} do
2247 user = insert(:user)
2248 other_user = insert(:user)
2249
2250 {:ok, user} = User.mute(user, other_user)
2251
2252 conn =
2253 conn
2254 |> assign(:user, user)
2255 |> get("/api/v1/mutes")
2256
2257 other_user_id = to_string(other_user.id)
2258 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2259 end
2260
2261 test "blocking / unblocking a user", %{conn: conn} do
2262 user = insert(:user)
2263 other_user = insert(:user)
2264
2265 conn =
2266 conn
2267 |> assign(:user, user)
2268 |> post("/api/v1/accounts/#{other_user.id}/block")
2269
2270 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2271
2272 user = User.get_cached_by_id(user.id)
2273
2274 conn =
2275 build_conn()
2276 |> assign(:user, user)
2277 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2278
2279 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2280 end
2281
2282 test "getting a list of blocks", %{conn: conn} do
2283 user = insert(:user)
2284 other_user = insert(:user)
2285
2286 {:ok, user} = User.block(user, other_user)
2287
2288 conn =
2289 conn
2290 |> assign(:user, user)
2291 |> get("/api/v1/blocks")
2292
2293 other_user_id = to_string(other_user.id)
2294 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2295 end
2296
2297 test "blocking / unblocking a domain", %{conn: conn} do
2298 user = insert(:user)
2299 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2300
2301 conn =
2302 conn
2303 |> assign(:user, user)
2304 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2305
2306 assert %{} = json_response(conn, 200)
2307 user = User.get_cached_by_ap_id(user.ap_id)
2308 assert User.blocks?(user, other_user)
2309
2310 conn =
2311 build_conn()
2312 |> assign(:user, user)
2313 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2314
2315 assert %{} = json_response(conn, 200)
2316 user = User.get_cached_by_ap_id(user.ap_id)
2317 refute User.blocks?(user, other_user)
2318 end
2319
2320 test "getting a list of domain blocks", %{conn: conn} do
2321 user = insert(:user)
2322
2323 {:ok, user} = User.block_domain(user, "bad.site")
2324 {:ok, user} = User.block_domain(user, "even.worse.site")
2325
2326 conn =
2327 conn
2328 |> assign(:user, user)
2329 |> get("/api/v1/domain_blocks")
2330
2331 domain_blocks = json_response(conn, 200)
2332
2333 assert "bad.site" in domain_blocks
2334 assert "even.worse.site" in domain_blocks
2335 end
2336
2337 test "unimplemented follow_requests, blocks, domain blocks" do
2338 user = insert(:user)
2339
2340 ["blocks", "domain_blocks", "follow_requests"]
2341 |> Enum.each(fn endpoint ->
2342 conn =
2343 build_conn()
2344 |> assign(:user, user)
2345 |> get("/api/v1/#{endpoint}")
2346
2347 assert [] = json_response(conn, 200)
2348 end)
2349 end
2350
2351 test "returns the favorites of a user", %{conn: conn} do
2352 user = insert(:user)
2353 other_user = insert(:user)
2354
2355 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2356 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2357
2358 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2359
2360 first_conn =
2361 conn
2362 |> assign(:user, user)
2363 |> get("/api/v1/favourites")
2364
2365 assert [status] = json_response(first_conn, 200)
2366 assert status["id"] == to_string(activity.id)
2367
2368 assert [{"link", _link_header}] =
2369 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2370
2371 # Honours query params
2372 {:ok, second_activity} =
2373 CommonAPI.post(other_user, %{
2374 "status" =>
2375 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2376 })
2377
2378 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2379
2380 last_like = status["id"]
2381
2382 second_conn =
2383 conn
2384 |> assign(:user, user)
2385 |> get("/api/v1/favourites?since_id=#{last_like}")
2386
2387 assert [second_status] = json_response(second_conn, 200)
2388 assert second_status["id"] == to_string(second_activity.id)
2389
2390 third_conn =
2391 conn
2392 |> assign(:user, user)
2393 |> get("/api/v1/favourites?limit=0")
2394
2395 assert [] = json_response(third_conn, 200)
2396 end
2397
2398 describe "getting favorites timeline of specified user" do
2399 setup do
2400 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2401 [current_user: current_user, user: user]
2402 end
2403
2404 test "returns list of statuses favorited by specified user", %{
2405 conn: conn,
2406 current_user: current_user,
2407 user: user
2408 } do
2409 [activity | _] = insert_pair(:note_activity)
2410 CommonAPI.favorite(activity.id, user)
2411
2412 response =
2413 conn
2414 |> assign(:user, current_user)
2415 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2416 |> json_response(:ok)
2417
2418 [like] = response
2419
2420 assert length(response) == 1
2421 assert like["id"] == activity.id
2422 end
2423
2424 test "returns favorites for specified user_id when user is not logged in", %{
2425 conn: conn,
2426 user: user
2427 } do
2428 activity = insert(:note_activity)
2429 CommonAPI.favorite(activity.id, user)
2430
2431 response =
2432 conn
2433 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2434 |> json_response(:ok)
2435
2436 assert length(response) == 1
2437 end
2438
2439 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2440 conn: conn,
2441 current_user: current_user,
2442 user: user
2443 } do
2444 {:ok, direct} =
2445 CommonAPI.post(current_user, %{
2446 "status" => "Hi @#{user.nickname}!",
2447 "visibility" => "direct"
2448 })
2449
2450 CommonAPI.favorite(direct.id, user)
2451
2452 response =
2453 conn
2454 |> assign(:user, current_user)
2455 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2456 |> json_response(:ok)
2457
2458 assert length(response) == 1
2459
2460 anonymous_response =
2461 conn
2462 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2463 |> json_response(:ok)
2464
2465 assert Enum.empty?(anonymous_response)
2466 end
2467
2468 test "does not return others' favorited DM when user is not one of recipients", %{
2469 conn: conn,
2470 current_user: current_user,
2471 user: user
2472 } do
2473 user_two = insert(:user)
2474
2475 {:ok, direct} =
2476 CommonAPI.post(user_two, %{
2477 "status" => "Hi @#{user.nickname}!",
2478 "visibility" => "direct"
2479 })
2480
2481 CommonAPI.favorite(direct.id, user)
2482
2483 response =
2484 conn
2485 |> assign(:user, current_user)
2486 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2487 |> json_response(:ok)
2488
2489 assert Enum.empty?(response)
2490 end
2491
2492 test "paginates favorites using since_id and max_id", %{
2493 conn: conn,
2494 current_user: current_user,
2495 user: user
2496 } do
2497 activities = insert_list(10, :note_activity)
2498
2499 Enum.each(activities, fn activity ->
2500 CommonAPI.favorite(activity.id, user)
2501 end)
2502
2503 third_activity = Enum.at(activities, 2)
2504 seventh_activity = Enum.at(activities, 6)
2505
2506 response =
2507 conn
2508 |> assign(:user, current_user)
2509 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2510 since_id: third_activity.id,
2511 max_id: seventh_activity.id
2512 })
2513 |> json_response(:ok)
2514
2515 assert length(response) == 3
2516 refute third_activity in response
2517 refute seventh_activity in response
2518 end
2519
2520 test "limits favorites using limit parameter", %{
2521 conn: conn,
2522 current_user: current_user,
2523 user: user
2524 } do
2525 7
2526 |> insert_list(:note_activity)
2527 |> Enum.each(fn activity ->
2528 CommonAPI.favorite(activity.id, user)
2529 end)
2530
2531 response =
2532 conn
2533 |> assign(:user, current_user)
2534 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2535 |> json_response(:ok)
2536
2537 assert length(response) == 3
2538 end
2539
2540 test "returns empty response when user does not have any favorited statuses", %{
2541 conn: conn,
2542 current_user: current_user,
2543 user: user
2544 } do
2545 response =
2546 conn
2547 |> assign(:user, current_user)
2548 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2549 |> json_response(:ok)
2550
2551 assert Enum.empty?(response)
2552 end
2553
2554 test "returns 404 error when specified user is not exist", %{conn: conn} do
2555 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2556
2557 assert json_response(conn, 404) == %{"error" => "Record not found"}
2558 end
2559
2560 test "returns 403 error when user has hidden own favorites", %{
2561 conn: conn,
2562 current_user: current_user
2563 } do
2564 user = insert(:user, %{info: %{hide_favorites: true}})
2565 activity = insert(:note_activity)
2566 CommonAPI.favorite(activity.id, user)
2567
2568 conn =
2569 conn
2570 |> assign(:user, current_user)
2571 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2572
2573 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2574 end
2575
2576 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2577 user = insert(:user)
2578 activity = insert(:note_activity)
2579 CommonAPI.favorite(activity.id, user)
2580
2581 conn =
2582 conn
2583 |> assign(:user, current_user)
2584 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2585
2586 assert user.info.hide_favorites
2587 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2588 end
2589 end
2590
2591 test "get instance information", %{conn: conn} do
2592 conn = get(conn, "/api/v1/instance")
2593 assert result = json_response(conn, 200)
2594
2595 email = Config.get([:instance, :email])
2596 # Note: not checking for "max_toot_chars" since it's optional
2597 assert %{
2598 "uri" => _,
2599 "title" => _,
2600 "description" => _,
2601 "version" => _,
2602 "email" => from_config_email,
2603 "urls" => %{
2604 "streaming_api" => _
2605 },
2606 "stats" => _,
2607 "thumbnail" => _,
2608 "languages" => _,
2609 "registrations" => _,
2610 "poll_limits" => _
2611 } = result
2612
2613 assert email == from_config_email
2614 end
2615
2616 test "get instance stats", %{conn: conn} do
2617 user = insert(:user, %{local: true})
2618
2619 user2 = insert(:user, %{local: true})
2620 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2621
2622 insert(:user, %{local: false, nickname: "u@peer1.com"})
2623 insert(:user, %{local: false, nickname: "u@peer2.com"})
2624
2625 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2626
2627 # Stats should count users with missing or nil `info.deactivated` value
2628 user = User.get_cached_by_id(user.id)
2629 info_change = Changeset.change(user.info, %{deactivated: nil})
2630
2631 {:ok, _user} =
2632 user
2633 |> Changeset.change()
2634 |> Changeset.put_embed(:info, info_change)
2635 |> User.update_and_set_cache()
2636
2637 Pleroma.Stats.force_update()
2638
2639 conn = get(conn, "/api/v1/instance")
2640
2641 assert result = json_response(conn, 200)
2642
2643 stats = result["stats"]
2644
2645 assert stats
2646 assert stats["user_count"] == 1
2647 assert stats["status_count"] == 1
2648 assert stats["domain_count"] == 2
2649 end
2650
2651 test "get peers", %{conn: conn} do
2652 insert(:user, %{local: false, nickname: "u@peer1.com"})
2653 insert(:user, %{local: false, nickname: "u@peer2.com"})
2654
2655 Pleroma.Stats.force_update()
2656
2657 conn = get(conn, "/api/v1/instance/peers")
2658
2659 assert result = json_response(conn, 200)
2660
2661 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2662 end
2663
2664 test "put settings", %{conn: conn} do
2665 user = insert(:user)
2666
2667 conn =
2668 conn
2669 |> assign(:user, user)
2670 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2671
2672 assert _result = json_response(conn, 200)
2673
2674 user = User.get_cached_by_ap_id(user.ap_id)
2675 assert user.info.settings == %{"programming" => "socks"}
2676 end
2677
2678 describe "pinned statuses" do
2679 setup do
2680 user = insert(:user)
2681 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2682
2683 [user: user, activity: activity]
2684 end
2685
2686 clear_config([:instance, :max_pinned_statuses]) do
2687 Config.put([:instance, :max_pinned_statuses], 1)
2688 end
2689
2690 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2691 {:ok, _} = CommonAPI.pin(activity.id, user)
2692
2693 result =
2694 conn
2695 |> assign(:user, user)
2696 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2697 |> json_response(200)
2698
2699 id_str = to_string(activity.id)
2700
2701 assert [%{"id" => ^id_str, "pinned" => true}] = result
2702 end
2703
2704 test "pin status", %{conn: conn, user: user, activity: activity} do
2705 id_str = to_string(activity.id)
2706
2707 assert %{"id" => ^id_str, "pinned" => true} =
2708 conn
2709 |> assign(:user, user)
2710 |> post("/api/v1/statuses/#{activity.id}/pin")
2711 |> json_response(200)
2712
2713 assert [%{"id" => ^id_str, "pinned" => true}] =
2714 conn
2715 |> assign(:user, user)
2716 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2717 |> json_response(200)
2718 end
2719
2720 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2721 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2722
2723 conn =
2724 conn
2725 |> assign(:user, user)
2726 |> post("/api/v1/statuses/#{dm.id}/pin")
2727
2728 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2729 end
2730
2731 test "unpin status", %{conn: conn, user: user, activity: activity} do
2732 {:ok, _} = CommonAPI.pin(activity.id, user)
2733
2734 id_str = to_string(activity.id)
2735 user = refresh_record(user)
2736
2737 assert %{"id" => ^id_str, "pinned" => false} =
2738 conn
2739 |> assign(:user, user)
2740 |> post("/api/v1/statuses/#{activity.id}/unpin")
2741 |> json_response(200)
2742
2743 assert [] =
2744 conn
2745 |> assign(:user, user)
2746 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2747 |> json_response(200)
2748 end
2749
2750 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2751 conn =
2752 conn
2753 |> assign(:user, user)
2754 |> post("/api/v1/statuses/1/unpin")
2755
2756 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2757 end
2758
2759 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2760 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2761
2762 id_str_one = to_string(activity_one.id)
2763
2764 assert %{"id" => ^id_str_one, "pinned" => true} =
2765 conn
2766 |> assign(:user, user)
2767 |> post("/api/v1/statuses/#{id_str_one}/pin")
2768 |> json_response(200)
2769
2770 user = refresh_record(user)
2771
2772 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2773 conn
2774 |> assign(:user, user)
2775 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2776 |> json_response(400)
2777 end
2778 end
2779
2780 describe "cards" do
2781 setup do
2782 Config.put([:rich_media, :enabled], true)
2783
2784 user = insert(:user)
2785 %{user: user}
2786 end
2787
2788 test "returns rich-media card", %{conn: conn, user: user} do
2789 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2790
2791 card_data = %{
2792 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2793 "provider_name" => "example.com",
2794 "provider_url" => "https://example.com",
2795 "title" => "The Rock",
2796 "type" => "link",
2797 "url" => "https://example.com/ogp",
2798 "description" =>
2799 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2800 "pleroma" => %{
2801 "opengraph" => %{
2802 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2803 "title" => "The Rock",
2804 "type" => "video.movie",
2805 "url" => "https://example.com/ogp",
2806 "description" =>
2807 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2808 }
2809 }
2810 }
2811
2812 response =
2813 conn
2814 |> get("/api/v1/statuses/#{activity.id}/card")
2815 |> json_response(200)
2816
2817 assert response == card_data
2818
2819 # works with private posts
2820 {:ok, activity} =
2821 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2822
2823 response_two =
2824 conn
2825 |> assign(:user, user)
2826 |> get("/api/v1/statuses/#{activity.id}/card")
2827 |> json_response(200)
2828
2829 assert response_two == card_data
2830 end
2831
2832 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2833 {:ok, activity} =
2834 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2835
2836 response =
2837 conn
2838 |> get("/api/v1/statuses/#{activity.id}/card")
2839 |> json_response(:ok)
2840
2841 assert response == %{
2842 "type" => "link",
2843 "title" => "Pleroma",
2844 "description" => "",
2845 "image" => nil,
2846 "provider_name" => "example.com",
2847 "provider_url" => "https://example.com",
2848 "url" => "https://example.com/ogp-missing-data",
2849 "pleroma" => %{
2850 "opengraph" => %{
2851 "title" => "Pleroma",
2852 "type" => "website",
2853 "url" => "https://example.com/ogp-missing-data"
2854 }
2855 }
2856 }
2857 end
2858
2859 test "returns empty object when id invalid", %{conn: conn} do
2860 response =
2861 conn
2862 |> get("/api/v1/statuses/9eoozpwTul5mjSEDRI/card")
2863 |> json_response(200)
2864
2865 assert response == %{}
2866 end
2867 end
2868
2869 test "bookmarks" do
2870 user = insert(:user)
2871 for_user = insert(:user)
2872
2873 {:ok, activity1} =
2874 CommonAPI.post(user, %{
2875 "status" => "heweoo?"
2876 })
2877
2878 {:ok, activity2} =
2879 CommonAPI.post(user, %{
2880 "status" => "heweoo!"
2881 })
2882
2883 response1 =
2884 build_conn()
2885 |> assign(:user, for_user)
2886 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2887
2888 assert json_response(response1, 200)["bookmarked"] == true
2889
2890 response2 =
2891 build_conn()
2892 |> assign(:user, for_user)
2893 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2894
2895 assert json_response(response2, 200)["bookmarked"] == true
2896
2897 bookmarks =
2898 build_conn()
2899 |> assign(:user, for_user)
2900 |> get("/api/v1/bookmarks")
2901
2902 assert [json_response(response2, 200), json_response(response1, 200)] ==
2903 json_response(bookmarks, 200)
2904
2905 response1 =
2906 build_conn()
2907 |> assign(:user, for_user)
2908 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2909
2910 assert json_response(response1, 200)["bookmarked"] == false
2911
2912 bookmarks =
2913 build_conn()
2914 |> assign(:user, for_user)
2915 |> get("/api/v1/bookmarks")
2916
2917 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2918 end
2919
2920 describe "conversation muting" do
2921 setup do
2922 post_user = insert(:user)
2923 user = insert(:user)
2924
2925 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2926
2927 [user: user, activity: activity]
2928 end
2929
2930 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2931 id_str = to_string(activity.id)
2932
2933 assert %{"id" => ^id_str, "muted" => true} =
2934 conn
2935 |> assign(:user, user)
2936 |> post("/api/v1/statuses/#{activity.id}/mute")
2937 |> json_response(200)
2938 end
2939
2940 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2941 {:ok, _} = CommonAPI.add_mute(user, activity)
2942
2943 conn =
2944 conn
2945 |> assign(:user, user)
2946 |> post("/api/v1/statuses/#{activity.id}/mute")
2947
2948 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2949 end
2950
2951 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2952 {:ok, _} = CommonAPI.add_mute(user, activity)
2953
2954 id_str = to_string(activity.id)
2955 user = refresh_record(user)
2956
2957 assert %{"id" => ^id_str, "muted" => false} =
2958 conn
2959 |> assign(:user, user)
2960 |> post("/api/v1/statuses/#{activity.id}/unmute")
2961 |> json_response(200)
2962 end
2963 end
2964
2965 describe "reports" do
2966 setup do
2967 reporter = insert(:user)
2968 target_user = insert(:user)
2969
2970 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2971
2972 [reporter: reporter, target_user: target_user, activity: activity]
2973 end
2974
2975 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2976 assert %{"action_taken" => false, "id" => _} =
2977 conn
2978 |> assign(:user, reporter)
2979 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2980 |> json_response(200)
2981 end
2982
2983 test "submit a report with statuses and comment", %{
2984 conn: conn,
2985 reporter: reporter,
2986 target_user: target_user,
2987 activity: activity
2988 } do
2989 assert %{"action_taken" => false, "id" => _} =
2990 conn
2991 |> assign(:user, reporter)
2992 |> post("/api/v1/reports", %{
2993 "account_id" => target_user.id,
2994 "status_ids" => [activity.id],
2995 "comment" => "bad status!",
2996 "forward" => "false"
2997 })
2998 |> json_response(200)
2999 end
3000
3001 test "account_id is required", %{
3002 conn: conn,
3003 reporter: reporter,
3004 activity: activity
3005 } do
3006 assert %{"error" => "Valid `account_id` required"} =
3007 conn
3008 |> assign(:user, reporter)
3009 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3010 |> json_response(400)
3011 end
3012
3013 test "comment must be up to the size specified in the config", %{
3014 conn: conn,
3015 reporter: reporter,
3016 target_user: target_user
3017 } do
3018 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3019 comment = String.pad_trailing("a", max_size + 1, "a")
3020
3021 error = %{"error" => "Comment must be up to #{max_size} characters"}
3022
3023 assert ^error =
3024 conn
3025 |> assign(:user, reporter)
3026 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3027 |> json_response(400)
3028 end
3029
3030 test "returns error when account is not exist", %{
3031 conn: conn,
3032 reporter: reporter,
3033 activity: activity
3034 } do
3035 conn =
3036 conn
3037 |> assign(:user, reporter)
3038 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3039
3040 assert json_response(conn, 400) == %{"error" => "Account not found"}
3041 end
3042 end
3043
3044 describe "link headers" do
3045 test "preserves parameters in link headers", %{conn: conn} do
3046 user = insert(:user)
3047 other_user = insert(:user)
3048
3049 {:ok, activity1} =
3050 CommonAPI.post(other_user, %{
3051 "status" => "hi @#{user.nickname}",
3052 "visibility" => "public"
3053 })
3054
3055 {:ok, activity2} =
3056 CommonAPI.post(other_user, %{
3057 "status" => "hi @#{user.nickname}",
3058 "visibility" => "public"
3059 })
3060
3061 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3062 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3063
3064 conn =
3065 conn
3066 |> assign(:user, user)
3067 |> get("/api/v1/notifications", %{media_only: true})
3068
3069 assert [link_header] = get_resp_header(conn, "link")
3070 assert link_header =~ ~r/media_only=true/
3071 assert link_header =~ ~r/min_id=#{notification2.id}/
3072 assert link_header =~ ~r/max_id=#{notification1.id}/
3073 end
3074 end
3075
3076 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3077 # Need to set an old-style integer ID to reproduce the problem
3078 # (these are no longer assigned to new accounts but were preserved
3079 # for existing accounts during the migration to flakeIDs)
3080 user_one = insert(:user, %{id: 1212})
3081 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3082
3083 resp_one =
3084 conn
3085 |> get("/api/v1/accounts/#{user_one.id}")
3086
3087 resp_two =
3088 conn
3089 |> get("/api/v1/accounts/#{user_two.nickname}")
3090
3091 resp_three =
3092 conn
3093 |> get("/api/v1/accounts/#{user_two.id}")
3094
3095 acc_one = json_response(resp_one, 200)
3096 acc_two = json_response(resp_two, 200)
3097 acc_three = json_response(resp_three, 200)
3098 refute acc_one == acc_two
3099 assert acc_two == acc_three
3100 end
3101
3102 describe "custom emoji" do
3103 test "with tags", %{conn: conn} do
3104 [emoji | _body] =
3105 conn
3106 |> get("/api/v1/custom_emojis")
3107 |> json_response(200)
3108
3109 assert Map.has_key?(emoji, "shortcode")
3110 assert Map.has_key?(emoji, "static_url")
3111 assert Map.has_key?(emoji, "tags")
3112 assert is_list(emoji["tags"])
3113 assert Map.has_key?(emoji, "category")
3114 assert Map.has_key?(emoji, "url")
3115 assert Map.has_key?(emoji, "visible_in_picker")
3116 end
3117 end
3118
3119 describe "index/2 redirections" do
3120 setup %{conn: conn} do
3121 session_opts = [
3122 store: :cookie,
3123 key: "_test",
3124 signing_salt: "cooldude"
3125 ]
3126
3127 conn =
3128 conn
3129 |> Plug.Session.call(Plug.Session.init(session_opts))
3130 |> fetch_session()
3131
3132 test_path = "/web/statuses/test"
3133 %{conn: conn, path: test_path}
3134 end
3135
3136 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3137 conn = get(conn, path)
3138
3139 assert conn.status == 302
3140 assert redirected_to(conn) == "/web/login"
3141 end
3142
3143 test "redirects not logged-in users to the login page on private instances", %{
3144 conn: conn,
3145 path: path
3146 } do
3147 Config.put([:instance, :public], false)
3148
3149 conn = get(conn, path)
3150
3151 assert conn.status == 302
3152 assert redirected_to(conn) == "/web/login"
3153 end
3154
3155 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3156 token = insert(:oauth_token)
3157
3158 conn =
3159 conn
3160 |> assign(:user, token.user)
3161 |> put_session(:oauth_token, token.token)
3162 |> get(path)
3163
3164 assert conn.status == 200
3165 end
3166
3167 test "saves referer path to session", %{conn: conn, path: path} do
3168 conn = get(conn, path)
3169 return_to = Plug.Conn.get_session(conn, :return_to)
3170
3171 assert return_to == path
3172 end
3173
3174 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3175 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3176 auth = insert(:oauth_authorization, app: app)
3177
3178 conn =
3179 conn
3180 |> put_session(:return_to, path)
3181 |> get("/web/login", %{code: auth.token})
3182
3183 assert conn.status == 302
3184 assert redirected_to(conn) == path
3185 end
3186 end
3187
3188 describe "GET /web/login" do
3189 test "redirects to /oauth/authorize", %{conn: conn} do
3190 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3191 conn = get(conn, "/web/login", %{})
3192
3193 assert conn.status == 302
3194
3195 assert redirected_to(conn) ==
3196 "/oauth/authorize?response_type=code&client_id=#{app.client_id}&redirect_uri=.&scope=read+write+follow+push"
3197 end
3198
3199 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3200 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3201 auth = insert(:oauth_authorization, app: app)
3202
3203 conn = get(conn, "/web/login", %{code: auth.token})
3204
3205 assert conn.status == 302
3206 assert redirected_to(conn) == "/web/getting-started"
3207 end
3208
3209 test "redirects to the getting-started page when user assigned", %{conn: conn} do
3210 user = insert(:user)
3211
3212 conn =
3213 conn
3214 |> assign(:user, user)
3215 |> get("/web/login", %{})
3216
3217 assert conn.status == 302
3218 assert redirected_to(conn) == "/web/getting-started"
3219 end
3220 end
3221
3222 describe "scheduled activities" do
3223 test "creates a scheduled activity", %{conn: conn} do
3224 user = insert(:user)
3225 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3226
3227 conn =
3228 conn
3229 |> assign(:user, user)
3230 |> post("/api/v1/statuses", %{
3231 "status" => "scheduled",
3232 "scheduled_at" => scheduled_at
3233 })
3234
3235 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3236 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3237 assert [] == Repo.all(Activity)
3238 end
3239
3240 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3241 user = insert(:user)
3242 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3243
3244 file = %Plug.Upload{
3245 content_type: "image/jpg",
3246 path: Path.absname("test/fixtures/image.jpg"),
3247 filename: "an_image.jpg"
3248 }
3249
3250 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3251
3252 conn =
3253 conn
3254 |> assign(:user, user)
3255 |> post("/api/v1/statuses", %{
3256 "media_ids" => [to_string(upload.id)],
3257 "status" => "scheduled",
3258 "scheduled_at" => scheduled_at
3259 })
3260
3261 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3262 assert %{"type" => "image"} = media_attachment
3263 end
3264
3265 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3266 %{conn: conn} do
3267 user = insert(:user)
3268
3269 scheduled_at =
3270 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3271
3272 conn =
3273 conn
3274 |> assign(:user, user)
3275 |> post("/api/v1/statuses", %{
3276 "status" => "not scheduled",
3277 "scheduled_at" => scheduled_at
3278 })
3279
3280 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3281 assert [] == Repo.all(ScheduledActivity)
3282 end
3283
3284 test "returns error when daily user limit is exceeded", %{conn: conn} do
3285 user = insert(:user)
3286
3287 today =
3288 NaiveDateTime.utc_now()
3289 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3290 |> NaiveDateTime.to_iso8601()
3291
3292 attrs = %{params: %{}, scheduled_at: today}
3293 {:ok, _} = ScheduledActivity.create(user, attrs)
3294 {:ok, _} = ScheduledActivity.create(user, attrs)
3295
3296 conn =
3297 conn
3298 |> assign(:user, user)
3299 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3300
3301 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3302 end
3303
3304 test "returns error when total user limit is exceeded", %{conn: conn} do
3305 user = insert(:user)
3306
3307 today =
3308 NaiveDateTime.utc_now()
3309 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3310 |> NaiveDateTime.to_iso8601()
3311
3312 tomorrow =
3313 NaiveDateTime.utc_now()
3314 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3315 |> NaiveDateTime.to_iso8601()
3316
3317 attrs = %{params: %{}, scheduled_at: today}
3318 {:ok, _} = ScheduledActivity.create(user, attrs)
3319 {:ok, _} = ScheduledActivity.create(user, attrs)
3320 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3321
3322 conn =
3323 conn
3324 |> assign(:user, user)
3325 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3326
3327 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3328 end
3329
3330 test "shows scheduled activities", %{conn: conn} do
3331 user = insert(:user)
3332 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3333 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3334 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3335 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3336
3337 conn =
3338 conn
3339 |> assign(:user, user)
3340
3341 # min_id
3342 conn_res =
3343 conn
3344 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3345
3346 result = json_response(conn_res, 200)
3347 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3348
3349 # since_id
3350 conn_res =
3351 conn
3352 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3353
3354 result = json_response(conn_res, 200)
3355 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3356
3357 # max_id
3358 conn_res =
3359 conn
3360 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3361
3362 result = json_response(conn_res, 200)
3363 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3364 end
3365
3366 test "shows a scheduled activity", %{conn: conn} do
3367 user = insert(:user)
3368 scheduled_activity = insert(:scheduled_activity, user: user)
3369
3370 res_conn =
3371 conn
3372 |> assign(:user, user)
3373 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3374
3375 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3376 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3377
3378 res_conn =
3379 conn
3380 |> assign(:user, user)
3381 |> get("/api/v1/scheduled_statuses/404")
3382
3383 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3384 end
3385
3386 test "updates a scheduled activity", %{conn: conn} do
3387 user = insert(:user)
3388 scheduled_activity = insert(:scheduled_activity, user: user)
3389
3390 new_scheduled_at =
3391 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3392
3393 res_conn =
3394 conn
3395 |> assign(:user, user)
3396 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3397 scheduled_at: new_scheduled_at
3398 })
3399
3400 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3401 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3402
3403 res_conn =
3404 conn
3405 |> assign(:user, user)
3406 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3407
3408 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3409 end
3410
3411 test "deletes a scheduled activity", %{conn: conn} do
3412 user = insert(:user)
3413 scheduled_activity = insert(:scheduled_activity, user: user)
3414
3415 res_conn =
3416 conn
3417 |> assign(:user, user)
3418 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3419
3420 assert %{} = json_response(res_conn, 200)
3421 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3422
3423 res_conn =
3424 conn
3425 |> assign(:user, user)
3426 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3427
3428 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3429 end
3430 end
3431
3432 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3433 user1 = insert(:user)
3434 user2 = insert(:user)
3435 user3 = insert(:user)
3436
3437 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3438
3439 # Reply to status from another user
3440 conn1 =
3441 conn
3442 |> assign(:user, user2)
3443 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3444
3445 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3446
3447 activity = Activity.get_by_id_with_object(id)
3448
3449 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3450 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3451
3452 # Reblog from the third user
3453 conn2 =
3454 conn
3455 |> assign(:user, user3)
3456 |> post("/api/v1/statuses/#{activity.id}/reblog")
3457
3458 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3459 json_response(conn2, 200)
3460
3461 assert to_string(activity.id) == id
3462
3463 # Getting third user status
3464 conn3 =
3465 conn
3466 |> assign(:user, user3)
3467 |> get("api/v1/timelines/home")
3468
3469 [reblogged_activity] = json_response(conn3, 200)
3470
3471 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3472
3473 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3474 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3475 end
3476
3477 describe "create account by app" do
3478 setup do
3479 valid_params = %{
3480 username: "lain",
3481 email: "lain@example.org",
3482 password: "PlzDontHackLain",
3483 agreement: true
3484 }
3485
3486 [valid_params: valid_params]
3487 end
3488
3489 test "Account registration via Application", %{conn: conn} do
3490 conn =
3491 conn
3492 |> post("/api/v1/apps", %{
3493 client_name: "client_name",
3494 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3495 scopes: "read, write, follow"
3496 })
3497
3498 %{
3499 "client_id" => client_id,
3500 "client_secret" => client_secret,
3501 "id" => _,
3502 "name" => "client_name",
3503 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3504 "vapid_key" => _,
3505 "website" => nil
3506 } = json_response(conn, 200)
3507
3508 conn =
3509 conn
3510 |> post("/oauth/token", %{
3511 grant_type: "client_credentials",
3512 client_id: client_id,
3513 client_secret: client_secret
3514 })
3515
3516 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3517 json_response(conn, 200)
3518
3519 assert token
3520 token_from_db = Repo.get_by(Token, token: token)
3521 assert token_from_db
3522 assert refresh
3523 assert scope == "read write follow"
3524
3525 conn =
3526 build_conn()
3527 |> put_req_header("authorization", "Bearer " <> token)
3528 |> post("/api/v1/accounts", %{
3529 username: "lain",
3530 email: "lain@example.org",
3531 password: "PlzDontHackLain",
3532 bio: "Test Bio",
3533 agreement: true
3534 })
3535
3536 %{
3537 "access_token" => token,
3538 "created_at" => _created_at,
3539 "scope" => _scope,
3540 "token_type" => "Bearer"
3541 } = json_response(conn, 200)
3542
3543 token_from_db = Repo.get_by(Token, token: token)
3544 assert token_from_db
3545 token_from_db = Repo.preload(token_from_db, :user)
3546 assert token_from_db.user
3547
3548 assert token_from_db.user.info.confirmation_pending
3549 end
3550
3551 test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
3552 _user = insert(:user, email: "lain@example.org")
3553 app_token = insert(:oauth_token, user: nil)
3554
3555 conn =
3556 conn
3557 |> put_req_header("authorization", "Bearer " <> app_token.token)
3558
3559 res = post(conn, "/api/v1/accounts", valid_params)
3560 assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
3561 end
3562
3563 test "rate limit", %{conn: conn} do
3564 app_token = insert(:oauth_token, user: nil)
3565
3566 conn =
3567 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3568 |> Map.put(:remote_ip, {15, 15, 15, 15})
3569
3570 for i <- 1..5 do
3571 conn =
3572 conn
3573 |> post("/api/v1/accounts", %{
3574 username: "#{i}lain",
3575 email: "#{i}lain@example.org",
3576 password: "PlzDontHackLain",
3577 agreement: true
3578 })
3579
3580 %{
3581 "access_token" => token,
3582 "created_at" => _created_at,
3583 "scope" => _scope,
3584 "token_type" => "Bearer"
3585 } = json_response(conn, 200)
3586
3587 token_from_db = Repo.get_by(Token, token: token)
3588 assert token_from_db
3589 token_from_db = Repo.preload(token_from_db, :user)
3590 assert token_from_db.user
3591
3592 assert token_from_db.user.info.confirmation_pending
3593 end
3594
3595 conn =
3596 conn
3597 |> post("/api/v1/accounts", %{
3598 username: "6lain",
3599 email: "6lain@example.org",
3600 password: "PlzDontHackLain",
3601 agreement: true
3602 })
3603
3604 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3605 end
3606
3607 test "returns bad_request if missing required params", %{
3608 conn: conn,
3609 valid_params: valid_params
3610 } do
3611 app_token = insert(:oauth_token, user: nil)
3612
3613 conn =
3614 conn
3615 |> put_req_header("authorization", "Bearer " <> app_token.token)
3616
3617 res = post(conn, "/api/v1/accounts", valid_params)
3618 assert json_response(res, 200)
3619
3620 Enum.each(valid_params, fn {attr, _} ->
3621 res =
3622 conn
3623 |> Map.put(
3624 :remote_ip,
3625 {:rand.uniform(15), :rand.uniform(15), :rand.uniform(15), :rand.uniform(15)}
3626 )
3627 |> post("/api/v1/accounts", Map.delete(valid_params, attr))
3628
3629 assert json_response(res, 400) == %{"error" => "Missing parameters"}
3630 end)
3631 end
3632
3633 test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
3634 conn =
3635 conn
3636 |> put_req_header("authorization", "Bearer " <> "invalid-token")
3637
3638 res = post(conn, "/api/v1/accounts", valid_params)
3639 assert json_response(res, 403) == %{"error" => "Invalid credentials"}
3640 end
3641 end
3642
3643 describe "GET /api/v1/polls/:id" do
3644 test "returns poll entity for object id", %{conn: conn} do
3645 user = insert(:user)
3646
3647 {:ok, activity} =
3648 CommonAPI.post(user, %{
3649 "status" => "Pleroma does",
3650 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3651 })
3652
3653 object = Object.normalize(activity)
3654
3655 conn =
3656 conn
3657 |> assign(:user, user)
3658 |> get("/api/v1/polls/#{object.id}")
3659
3660 response = json_response(conn, 200)
3661 id = to_string(object.id)
3662 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3663 end
3664
3665 test "does not expose polls for private statuses", %{conn: conn} do
3666 user = insert(:user)
3667 other_user = insert(:user)
3668
3669 {:ok, activity} =
3670 CommonAPI.post(user, %{
3671 "status" => "Pleroma does",
3672 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3673 "visibility" => "private"
3674 })
3675
3676 object = Object.normalize(activity)
3677
3678 conn =
3679 conn
3680 |> assign(:user, other_user)
3681 |> get("/api/v1/polls/#{object.id}")
3682
3683 assert json_response(conn, 404)
3684 end
3685 end
3686
3687 describe "POST /api/v1/polls/:id/votes" do
3688 test "votes are added to the poll", %{conn: conn} do
3689 user = insert(:user)
3690 other_user = insert(:user)
3691
3692 {:ok, activity} =
3693 CommonAPI.post(user, %{
3694 "status" => "A very delicious sandwich",
3695 "poll" => %{
3696 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3697 "expires_in" => 20,
3698 "multiple" => true
3699 }
3700 })
3701
3702 object = Object.normalize(activity)
3703
3704 conn =
3705 conn
3706 |> assign(:user, other_user)
3707 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3708
3709 assert json_response(conn, 200)
3710 object = Object.get_by_id(object.id)
3711
3712 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3713 total_items == 1
3714 end)
3715 end
3716
3717 test "author can't vote", %{conn: conn} do
3718 user = insert(:user)
3719
3720 {:ok, activity} =
3721 CommonAPI.post(user, %{
3722 "status" => "Am I cute?",
3723 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3724 })
3725
3726 object = Object.normalize(activity)
3727
3728 assert conn
3729 |> assign(:user, user)
3730 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3731 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3732
3733 object = Object.get_by_id(object.id)
3734
3735 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3736 end
3737
3738 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3739 user = insert(:user)
3740 other_user = insert(:user)
3741
3742 {:ok, activity} =
3743 CommonAPI.post(user, %{
3744 "status" => "The glass is",
3745 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3746 })
3747
3748 object = Object.normalize(activity)
3749
3750 assert conn
3751 |> assign(:user, other_user)
3752 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3753 |> json_response(422) == %{"error" => "Too many choices"}
3754
3755 object = Object.get_by_id(object.id)
3756
3757 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3758 total_items == 1
3759 end)
3760 end
3761
3762 test "does not allow choice index to be greater than options count", %{conn: conn} do
3763 user = insert(:user)
3764 other_user = insert(:user)
3765
3766 {:ok, activity} =
3767 CommonAPI.post(user, %{
3768 "status" => "Am I cute?",
3769 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3770 })
3771
3772 object = Object.normalize(activity)
3773
3774 conn =
3775 conn
3776 |> assign(:user, other_user)
3777 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3778
3779 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3780 end
3781
3782 test "returns 404 error when object is not exist", %{conn: conn} do
3783 user = insert(:user)
3784
3785 conn =
3786 conn
3787 |> assign(:user, user)
3788 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3789
3790 assert json_response(conn, 404) == %{"error" => "Record not found"}
3791 end
3792
3793 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3794 user = insert(:user)
3795 other_user = insert(:user)
3796
3797 {:ok, activity} =
3798 CommonAPI.post(user, %{
3799 "status" => "Am I cute?",
3800 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3801 "visibility" => "private"
3802 })
3803
3804 object = Object.normalize(activity)
3805
3806 conn =
3807 conn
3808 |> assign(:user, other_user)
3809 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3810
3811 assert json_response(conn, 404) == %{"error" => "Record not found"}
3812 end
3813 end
3814
3815 describe "GET /api/v1/statuses/:id/favourited_by" do
3816 setup do
3817 user = insert(:user)
3818 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3819
3820 conn =
3821 build_conn()
3822 |> assign(:user, user)
3823
3824 [conn: conn, activity: activity]
3825 end
3826
3827 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3828 other_user = insert(:user)
3829 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3830
3831 response =
3832 conn
3833 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3834 |> json_response(:ok)
3835
3836 [%{"id" => id}] = response
3837
3838 assert id == other_user.id
3839 end
3840
3841 test "returns empty array when status has not been favorited yet", %{
3842 conn: conn,
3843 activity: activity
3844 } do
3845 response =
3846 conn
3847 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3848 |> json_response(:ok)
3849
3850 assert Enum.empty?(response)
3851 end
3852
3853 test "does not return users who have favorited the status but are blocked", %{
3854 conn: %{assigns: %{user: user}} = conn,
3855 activity: activity
3856 } do
3857 other_user = insert(:user)
3858 {:ok, user} = User.block(user, other_user)
3859
3860 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3861
3862 response =
3863 conn
3864 |> assign(:user, user)
3865 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3866 |> json_response(:ok)
3867
3868 assert Enum.empty?(response)
3869 end
3870
3871 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3872 other_user = insert(:user)
3873 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3874
3875 response =
3876 conn
3877 |> assign(:user, nil)
3878 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3879 |> json_response(:ok)
3880
3881 [%{"id" => id}] = response
3882 assert id == other_user.id
3883 end
3884 end
3885
3886 describe "GET /api/v1/statuses/:id/reblogged_by" do
3887 setup do
3888 user = insert(:user)
3889 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3890
3891 conn =
3892 build_conn()
3893 |> assign(:user, user)
3894
3895 [conn: conn, activity: activity]
3896 end
3897
3898 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3899 other_user = insert(:user)
3900 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3901
3902 response =
3903 conn
3904 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3905 |> json_response(:ok)
3906
3907 [%{"id" => id}] = response
3908
3909 assert id == other_user.id
3910 end
3911
3912 test "returns empty array when status has not been reblogged yet", %{
3913 conn: conn,
3914 activity: activity
3915 } do
3916 response =
3917 conn
3918 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3919 |> json_response(:ok)
3920
3921 assert Enum.empty?(response)
3922 end
3923
3924 test "does not return users who have reblogged the status but are blocked", %{
3925 conn: %{assigns: %{user: user}} = conn,
3926 activity: activity
3927 } do
3928 other_user = insert(:user)
3929 {:ok, user} = User.block(user, other_user)
3930
3931 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3932
3933 response =
3934 conn
3935 |> assign(:user, user)
3936 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3937 |> json_response(:ok)
3938
3939 assert Enum.empty?(response)
3940 end
3941
3942 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3943 other_user = insert(:user)
3944 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3945
3946 response =
3947 conn
3948 |> assign(:user, nil)
3949 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3950 |> json_response(:ok)
3951
3952 [%{"id" => id}] = response
3953 assert id == other_user.id
3954 end
3955 end
3956
3957 describe "POST /auth/password, with valid parameters" do
3958 setup %{conn: conn} do
3959 user = insert(:user)
3960 conn = post(conn, "/auth/password?email=#{user.email}")
3961 %{conn: conn, user: user}
3962 end
3963
3964 test "it returns 204", %{conn: conn} do
3965 assert json_response(conn, :no_content)
3966 end
3967
3968 test "it creates a PasswordResetToken record for user", %{user: user} do
3969 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3970 assert token_record
3971 end
3972
3973 test "it sends an email to user", %{user: user} do
3974 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3975
3976 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3977 notify_email = Config.get([:instance, :notify_email])
3978 instance_name = Config.get([:instance, :name])
3979
3980 assert_email_sent(
3981 from: {instance_name, notify_email},
3982 to: {user.name, user.email},
3983 html_body: email.html_body
3984 )
3985 end
3986 end
3987
3988 describe "POST /auth/password, with invalid parameters" do
3989 setup do
3990 user = insert(:user)
3991 {:ok, user: user}
3992 end
3993
3994 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3995 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3996 assert conn.status == 404
3997 assert conn.resp_body == ""
3998 end
3999
4000 test "it returns 400 when user is not local", %{conn: conn, user: user} do
4001 {:ok, user} = Repo.update(Changeset.change(user, local: false))
4002 conn = post(conn, "/auth/password?email=#{user.email}")
4003 assert conn.status == 400
4004 assert conn.resp_body == ""
4005 end
4006 end
4007
4008 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
4009 setup do
4010 user = insert(:user)
4011 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
4012
4013 {:ok, user} =
4014 user
4015 |> Changeset.change()
4016 |> Changeset.put_embed(:info, info_change)
4017 |> Repo.update()
4018
4019 assert user.info.confirmation_pending
4020
4021 [user: user]
4022 end
4023
4024 clear_config([:instance, :account_activation_required]) do
4025 Config.put([:instance, :account_activation_required], true)
4026 end
4027
4028 test "resend account confirmation email", %{conn: conn, user: user} do
4029 conn
4030 |> assign(:user, user)
4031 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
4032 |> json_response(:no_content)
4033
4034 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
4035 notify_email = Config.get([:instance, :notify_email])
4036 instance_name = Config.get([:instance, :name])
4037
4038 assert_email_sent(
4039 from: {instance_name, notify_email},
4040 to: {user.name, user.email},
4041 html_body: email.html_body
4042 )
4043 end
4044 end
4045
4046 describe "GET /api/v1/suggestions" do
4047 setup do
4048 user = insert(:user)
4049 other_user = insert(:user)
4050 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
4051 url500 = "http://test500?#{host}&#{user.nickname}"
4052 url200 = "http://test200?#{host}&#{user.nickname}"
4053
4054 mock(fn
4055 %{method: :get, url: ^url500} ->
4056 %Tesla.Env{status: 500, body: "bad request"}
4057
4058 %{method: :get, url: ^url200} ->
4059 %Tesla.Env{
4060 status: 200,
4061 body:
4062 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4063 other_user.ap_id
4064 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4065 }
4066 end)
4067
4068 [user: user, other_user: other_user]
4069 end
4070
4071 clear_config(:suggestions)
4072
4073 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4074 Config.put([:suggestions, :enabled], false)
4075
4076 res =
4077 conn
4078 |> assign(:user, user)
4079 |> get("/api/v1/suggestions")
4080 |> json_response(200)
4081
4082 assert res == []
4083 end
4084
4085 test "returns error", %{conn: conn, user: user} do
4086 Config.put([:suggestions, :enabled], true)
4087 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4088
4089 res =
4090 conn
4091 |> assign(:user, user)
4092 |> get("/api/v1/suggestions")
4093 |> json_response(500)
4094
4095 assert res == "Something went wrong"
4096 end
4097
4098 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4099 Config.put([:suggestions, :enabled], true)
4100 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4101
4102 res =
4103 conn
4104 |> assign(:user, user)
4105 |> get("/api/v1/suggestions")
4106 |> json_response(200)
4107
4108 assert res == [
4109 %{
4110 "acct" => "yj455",
4111 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4112 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4113 "id" => 0
4114 },
4115 %{
4116 "acct" => other_user.ap_id,
4117 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4118 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4119 "id" => other_user.id
4120 }
4121 ]
4122 end
4123 end
4124
4125 describe "PUT /api/v1/media/:id" do
4126 setup do
4127 actor = insert(:user)
4128
4129 file = %Plug.Upload{
4130 content_type: "image/jpg",
4131 path: Path.absname("test/fixtures/image.jpg"),
4132 filename: "an_image.jpg"
4133 }
4134
4135 {:ok, %Object{} = object} =
4136 ActivityPub.upload(
4137 file,
4138 actor: User.ap_id(actor),
4139 description: "test-m"
4140 )
4141
4142 [actor: actor, object: object]
4143 end
4144
4145 test "updates name of media", %{conn: conn, actor: actor, object: object} do
4146 media =
4147 conn
4148 |> assign(:user, actor)
4149 |> put("/api/v1/media/#{object.id}", %{"description" => "test-media"})
4150 |> json_response(:ok)
4151
4152 assert media["description"] == "test-media"
4153 assert refresh_record(object).data["name"] == "test-media"
4154 end
4155
4156 test "returns error wheb request is bad", %{conn: conn, actor: actor, object: object} do
4157 media =
4158 conn
4159 |> assign(:user, actor)
4160 |> put("/api/v1/media/#{object.id}", %{})
4161 |> json_response(400)
4162
4163 assert media == %{"error" => "bad_request"}
4164 end
4165 end
4166
4167 describe "DELETE /auth/sign_out" do
4168 test "redirect to root page", %{conn: conn} do
4169 user = insert(:user)
4170
4171 conn =
4172 conn
4173 |> assign(:user, user)
4174 |> delete("/auth/sign_out")
4175
4176 assert conn.status == 302
4177 assert redirected_to(conn) == "/"
4178 end
4179 end
4180
4181 describe "GET /api/v1/accounts/:id/lists - account_lists" do
4182 test "returns lists to which the account belongs", %{conn: conn} do
4183 user = insert(:user)
4184 other_user = insert(:user)
4185 assert {:ok, %Pleroma.List{} = list} = Pleroma.List.create("Test List", user)
4186 {:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
4187
4188 res =
4189 conn
4190 |> assign(:user, user)
4191 |> get("/api/v1/accounts/#{other_user.id}/lists")
4192 |> json_response(200)
4193
4194 assert res == [%{"id" => to_string(list.id), "title" => "Test List"}]
4195 end
4196 end
4197
4198 describe "empty_array, stubs for mastodon api" do
4199 test "GET /api/v1/accounts/:id/identity_proofs", %{conn: conn} do
4200 user = insert(:user)
4201
4202 res =
4203 conn
4204 |> assign(:user, user)
4205 |> get("/api/v1/accounts/#{user.id}/identity_proofs")
4206 |> json_response(200)
4207
4208 assert res == []
4209 end
4210
4211 test "GET /api/v1/endorsements", %{conn: conn} do
4212 user = insert(:user)
4213
4214 res =
4215 conn
4216 |> assign(:user, user)
4217 |> get("/api/v1/endorsements")
4218 |> json_response(200)
4219
4220 assert res == []
4221 end
4222
4223 test "GET /api/v1/trends", %{conn: conn} do
4224 user = insert(:user)
4225
4226 res =
4227 conn
4228 |> assign(:user, user)
4229 |> get("/api/v1/trends")
4230 |> json_response(200)
4231
4232 assert res == []
4233 end
4234 end
4235 end