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