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