Merge branch 'support-rel-ugc' 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 user = User.get_cached_by_id(user.id)
2617 info_change = Changeset.change(user.info, %{deactivated: nil})
2618
2619 {:ok, _user} =
2620 user
2621 |> Changeset.change()
2622 |> Changeset.put_embed(:info, info_change)
2623 |> User.update_and_set_cache()
2624
2625 Pleroma.Stats.force_update()
2626
2627 conn = get(conn, "/api/v1/instance")
2628
2629 assert result = json_response(conn, 200)
2630
2631 stats = result["stats"]
2632
2633 assert stats
2634 assert stats["user_count"] == 1
2635 assert stats["status_count"] == 1
2636 assert stats["domain_count"] == 2
2637 end
2638
2639 test "get peers", %{conn: conn} do
2640 insert(:user, %{local: false, nickname: "u@peer1.com"})
2641 insert(:user, %{local: false, nickname: "u@peer2.com"})
2642
2643 Pleroma.Stats.force_update()
2644
2645 conn = get(conn, "/api/v1/instance/peers")
2646
2647 assert result = json_response(conn, 200)
2648
2649 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2650 end
2651
2652 test "put settings", %{conn: conn} do
2653 user = insert(:user)
2654
2655 conn =
2656 conn
2657 |> assign(:user, user)
2658 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2659
2660 assert _result = json_response(conn, 200)
2661
2662 user = User.get_cached_by_ap_id(user.ap_id)
2663 assert user.info.settings == %{"programming" => "socks"}
2664 end
2665
2666 describe "pinned statuses" do
2667 setup do
2668 user = insert(:user)
2669 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2670
2671 [user: user, activity: activity]
2672 end
2673
2674 clear_config([:instance, :max_pinned_statuses]) do
2675 Config.put([:instance, :max_pinned_statuses], 1)
2676 end
2677
2678 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2679 {:ok, _} = CommonAPI.pin(activity.id, user)
2680
2681 result =
2682 conn
2683 |> assign(:user, user)
2684 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2685 |> json_response(200)
2686
2687 id_str = to_string(activity.id)
2688
2689 assert [%{"id" => ^id_str, "pinned" => true}] = result
2690 end
2691
2692 test "pin status", %{conn: conn, user: user, activity: activity} do
2693 id_str = to_string(activity.id)
2694
2695 assert %{"id" => ^id_str, "pinned" => true} =
2696 conn
2697 |> assign(:user, user)
2698 |> post("/api/v1/statuses/#{activity.id}/pin")
2699 |> json_response(200)
2700
2701 assert [%{"id" => ^id_str, "pinned" => true}] =
2702 conn
2703 |> assign(:user, user)
2704 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2705 |> json_response(200)
2706 end
2707
2708 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2709 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2710
2711 conn =
2712 conn
2713 |> assign(:user, user)
2714 |> post("/api/v1/statuses/#{dm.id}/pin")
2715
2716 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2717 end
2718
2719 test "unpin status", %{conn: conn, user: user, activity: activity} do
2720 {:ok, _} = CommonAPI.pin(activity.id, user)
2721
2722 id_str = to_string(activity.id)
2723 user = refresh_record(user)
2724
2725 assert %{"id" => ^id_str, "pinned" => false} =
2726 conn
2727 |> assign(:user, user)
2728 |> post("/api/v1/statuses/#{activity.id}/unpin")
2729 |> json_response(200)
2730
2731 assert [] =
2732 conn
2733 |> assign(:user, user)
2734 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2735 |> json_response(200)
2736 end
2737
2738 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2739 conn =
2740 conn
2741 |> assign(:user, user)
2742 |> post("/api/v1/statuses/1/unpin")
2743
2744 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2745 end
2746
2747 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2748 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2749
2750 id_str_one = to_string(activity_one.id)
2751
2752 assert %{"id" => ^id_str_one, "pinned" => true} =
2753 conn
2754 |> assign(:user, user)
2755 |> post("/api/v1/statuses/#{id_str_one}/pin")
2756 |> json_response(200)
2757
2758 user = refresh_record(user)
2759
2760 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2761 conn
2762 |> assign(:user, user)
2763 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2764 |> json_response(400)
2765 end
2766 end
2767
2768 describe "cards" do
2769 setup do
2770 Config.put([:rich_media, :enabled], true)
2771
2772 user = insert(:user)
2773 %{user: user}
2774 end
2775
2776 test "returns rich-media card", %{conn: conn, user: user} do
2777 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2778
2779 card_data = %{
2780 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2781 "provider_name" => "example.com",
2782 "provider_url" => "https://example.com",
2783 "title" => "The Rock",
2784 "type" => "link",
2785 "url" => "https://example.com/ogp",
2786 "description" =>
2787 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2788 "pleroma" => %{
2789 "opengraph" => %{
2790 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2791 "title" => "The Rock",
2792 "type" => "video.movie",
2793 "url" => "https://example.com/ogp",
2794 "description" =>
2795 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2796 }
2797 }
2798 }
2799
2800 response =
2801 conn
2802 |> get("/api/v1/statuses/#{activity.id}/card")
2803 |> json_response(200)
2804
2805 assert response == card_data
2806
2807 # works with private posts
2808 {:ok, activity} =
2809 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2810
2811 response_two =
2812 conn
2813 |> assign(:user, user)
2814 |> get("/api/v1/statuses/#{activity.id}/card")
2815 |> json_response(200)
2816
2817 assert response_two == card_data
2818 end
2819
2820 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2821 {:ok, activity} =
2822 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2823
2824 response =
2825 conn
2826 |> get("/api/v1/statuses/#{activity.id}/card")
2827 |> json_response(:ok)
2828
2829 assert response == %{
2830 "type" => "link",
2831 "title" => "Pleroma",
2832 "description" => "",
2833 "image" => nil,
2834 "provider_name" => "example.com",
2835 "provider_url" => "https://example.com",
2836 "url" => "https://example.com/ogp-missing-data",
2837 "pleroma" => %{
2838 "opengraph" => %{
2839 "title" => "Pleroma",
2840 "type" => "website",
2841 "url" => "https://example.com/ogp-missing-data"
2842 }
2843 }
2844 }
2845 end
2846 end
2847
2848 test "bookmarks" do
2849 user = insert(:user)
2850 for_user = insert(:user)
2851
2852 {:ok, activity1} =
2853 CommonAPI.post(user, %{
2854 "status" => "heweoo?"
2855 })
2856
2857 {:ok, activity2} =
2858 CommonAPI.post(user, %{
2859 "status" => "heweoo!"
2860 })
2861
2862 response1 =
2863 build_conn()
2864 |> assign(:user, for_user)
2865 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2866
2867 assert json_response(response1, 200)["bookmarked"] == true
2868
2869 response2 =
2870 build_conn()
2871 |> assign(:user, for_user)
2872 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2873
2874 assert json_response(response2, 200)["bookmarked"] == true
2875
2876 bookmarks =
2877 build_conn()
2878 |> assign(:user, for_user)
2879 |> get("/api/v1/bookmarks")
2880
2881 assert [json_response(response2, 200), json_response(response1, 200)] ==
2882 json_response(bookmarks, 200)
2883
2884 response1 =
2885 build_conn()
2886 |> assign(:user, for_user)
2887 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2888
2889 assert json_response(response1, 200)["bookmarked"] == false
2890
2891 bookmarks =
2892 build_conn()
2893 |> assign(:user, for_user)
2894 |> get("/api/v1/bookmarks")
2895
2896 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2897 end
2898
2899 describe "conversation muting" do
2900 setup do
2901 post_user = insert(:user)
2902 user = insert(:user)
2903
2904 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
2905
2906 [user: user, activity: activity]
2907 end
2908
2909 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2910 id_str = to_string(activity.id)
2911
2912 assert %{"id" => ^id_str, "muted" => true} =
2913 conn
2914 |> assign(:user, user)
2915 |> post("/api/v1/statuses/#{activity.id}/mute")
2916 |> json_response(200)
2917 end
2918
2919 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
2920 {:ok, _} = CommonAPI.add_mute(user, activity)
2921
2922 conn =
2923 conn
2924 |> assign(:user, user)
2925 |> post("/api/v1/statuses/#{activity.id}/mute")
2926
2927 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
2928 end
2929
2930 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2931 {:ok, _} = CommonAPI.add_mute(user, activity)
2932
2933 id_str = to_string(activity.id)
2934 user = refresh_record(user)
2935
2936 assert %{"id" => ^id_str, "muted" => false} =
2937 conn
2938 |> assign(:user, user)
2939 |> post("/api/v1/statuses/#{activity.id}/unmute")
2940 |> json_response(200)
2941 end
2942 end
2943
2944 describe "reports" do
2945 setup do
2946 reporter = insert(:user)
2947 target_user = insert(:user)
2948
2949 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2950
2951 [reporter: reporter, target_user: target_user, activity: activity]
2952 end
2953
2954 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2955 assert %{"action_taken" => false, "id" => _} =
2956 conn
2957 |> assign(:user, reporter)
2958 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2959 |> json_response(200)
2960 end
2961
2962 test "submit a report with statuses and comment", %{
2963 conn: conn,
2964 reporter: reporter,
2965 target_user: target_user,
2966 activity: activity
2967 } do
2968 assert %{"action_taken" => false, "id" => _} =
2969 conn
2970 |> assign(:user, reporter)
2971 |> post("/api/v1/reports", %{
2972 "account_id" => target_user.id,
2973 "status_ids" => [activity.id],
2974 "comment" => "bad status!",
2975 "forward" => "false"
2976 })
2977 |> json_response(200)
2978 end
2979
2980 test "account_id is required", %{
2981 conn: conn,
2982 reporter: reporter,
2983 activity: activity
2984 } do
2985 assert %{"error" => "Valid `account_id` required"} =
2986 conn
2987 |> assign(:user, reporter)
2988 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2989 |> json_response(400)
2990 end
2991
2992 test "comment must be up to the size specified in the config", %{
2993 conn: conn,
2994 reporter: reporter,
2995 target_user: target_user
2996 } do
2997 max_size = Config.get([:instance, :max_report_comment_size], 1000)
2998 comment = String.pad_trailing("a", max_size + 1, "a")
2999
3000 error = %{"error" => "Comment must be up to #{max_size} characters"}
3001
3002 assert ^error =
3003 conn
3004 |> assign(:user, reporter)
3005 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3006 |> json_response(400)
3007 end
3008
3009 test "returns error when account is not exist", %{
3010 conn: conn,
3011 reporter: reporter,
3012 activity: activity
3013 } do
3014 conn =
3015 conn
3016 |> assign(:user, reporter)
3017 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3018
3019 assert json_response(conn, 400) == %{"error" => "Account not found"}
3020 end
3021 end
3022
3023 describe "link headers" do
3024 test "preserves parameters in link headers", %{conn: conn} do
3025 user = insert(:user)
3026 other_user = insert(:user)
3027
3028 {:ok, activity1} =
3029 CommonAPI.post(other_user, %{
3030 "status" => "hi @#{user.nickname}",
3031 "visibility" => "public"
3032 })
3033
3034 {:ok, activity2} =
3035 CommonAPI.post(other_user, %{
3036 "status" => "hi @#{user.nickname}",
3037 "visibility" => "public"
3038 })
3039
3040 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3041 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3042
3043 conn =
3044 conn
3045 |> assign(:user, user)
3046 |> get("/api/v1/notifications", %{media_only: true})
3047
3048 assert [link_header] = get_resp_header(conn, "link")
3049 assert link_header =~ ~r/media_only=true/
3050 assert link_header =~ ~r/min_id=#{notification2.id}/
3051 assert link_header =~ ~r/max_id=#{notification1.id}/
3052 end
3053 end
3054
3055 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3056 # Need to set an old-style integer ID to reproduce the problem
3057 # (these are no longer assigned to new accounts but were preserved
3058 # for existing accounts during the migration to flakeIDs)
3059 user_one = insert(:user, %{id: 1212})
3060 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3061
3062 resp_one =
3063 conn
3064 |> get("/api/v1/accounts/#{user_one.id}")
3065
3066 resp_two =
3067 conn
3068 |> get("/api/v1/accounts/#{user_two.nickname}")
3069
3070 resp_three =
3071 conn
3072 |> get("/api/v1/accounts/#{user_two.id}")
3073
3074 acc_one = json_response(resp_one, 200)
3075 acc_two = json_response(resp_two, 200)
3076 acc_three = json_response(resp_three, 200)
3077 refute acc_one == acc_two
3078 assert acc_two == acc_three
3079 end
3080
3081 describe "custom emoji" do
3082 test "with tags", %{conn: conn} do
3083 [emoji | _body] =
3084 conn
3085 |> get("/api/v1/custom_emojis")
3086 |> json_response(200)
3087
3088 assert Map.has_key?(emoji, "shortcode")
3089 assert Map.has_key?(emoji, "static_url")
3090 assert Map.has_key?(emoji, "tags")
3091 assert is_list(emoji["tags"])
3092 assert Map.has_key?(emoji, "category")
3093 assert Map.has_key?(emoji, "url")
3094 assert Map.has_key?(emoji, "visible_in_picker")
3095 end
3096 end
3097
3098 describe "index/2 redirections" do
3099 setup %{conn: conn} do
3100 session_opts = [
3101 store: :cookie,
3102 key: "_test",
3103 signing_salt: "cooldude"
3104 ]
3105
3106 conn =
3107 conn
3108 |> Plug.Session.call(Plug.Session.init(session_opts))
3109 |> fetch_session()
3110
3111 test_path = "/web/statuses/test"
3112 %{conn: conn, path: test_path}
3113 end
3114
3115 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3116 conn = get(conn, path)
3117
3118 assert conn.status == 302
3119 assert redirected_to(conn) == "/web/login"
3120 end
3121
3122 test "redirects not logged-in users to the login page on private instances", %{
3123 conn: conn,
3124 path: path
3125 } do
3126 Config.put([:instance, :public], false)
3127
3128 conn = get(conn, path)
3129
3130 assert conn.status == 302
3131 assert redirected_to(conn) == "/web/login"
3132 end
3133
3134 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3135 token = insert(:oauth_token)
3136
3137 conn =
3138 conn
3139 |> assign(:user, token.user)
3140 |> put_session(:oauth_token, token.token)
3141 |> get(path)
3142
3143 assert conn.status == 200
3144 end
3145
3146 test "saves referer path to session", %{conn: conn, path: path} do
3147 conn = get(conn, path)
3148 return_to = Plug.Conn.get_session(conn, :return_to)
3149
3150 assert return_to == path
3151 end
3152
3153 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3154 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3155 auth = insert(:oauth_authorization, app: app)
3156
3157 conn =
3158 conn
3159 |> put_session(:return_to, path)
3160 |> get("/web/login", %{code: auth.token})
3161
3162 assert conn.status == 302
3163 assert redirected_to(conn) == path
3164 end
3165
3166 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3167 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3168 auth = insert(:oauth_authorization, app: app)
3169
3170 conn = get(conn, "/web/login", %{code: auth.token})
3171
3172 assert conn.status == 302
3173 assert redirected_to(conn) == "/web/getting-started"
3174 end
3175 end
3176
3177 describe "scheduled activities" do
3178 test "creates a scheduled activity", %{conn: conn} do
3179 user = insert(:user)
3180 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3181
3182 conn =
3183 conn
3184 |> assign(:user, user)
3185 |> post("/api/v1/statuses", %{
3186 "status" => "scheduled",
3187 "scheduled_at" => scheduled_at
3188 })
3189
3190 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3191 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3192 assert [] == Repo.all(Activity)
3193 end
3194
3195 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3196 user = insert(:user)
3197 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3198
3199 file = %Plug.Upload{
3200 content_type: "image/jpg",
3201 path: Path.absname("test/fixtures/image.jpg"),
3202 filename: "an_image.jpg"
3203 }
3204
3205 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3206
3207 conn =
3208 conn
3209 |> assign(:user, user)
3210 |> post("/api/v1/statuses", %{
3211 "media_ids" => [to_string(upload.id)],
3212 "status" => "scheduled",
3213 "scheduled_at" => scheduled_at
3214 })
3215
3216 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3217 assert %{"type" => "image"} = media_attachment
3218 end
3219
3220 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3221 %{conn: conn} do
3222 user = insert(:user)
3223
3224 scheduled_at =
3225 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3226
3227 conn =
3228 conn
3229 |> assign(:user, user)
3230 |> post("/api/v1/statuses", %{
3231 "status" => "not scheduled",
3232 "scheduled_at" => scheduled_at
3233 })
3234
3235 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3236 assert [] == Repo.all(ScheduledActivity)
3237 end
3238
3239 test "returns error when daily user limit is exceeded", %{conn: conn} do
3240 user = insert(:user)
3241
3242 today =
3243 NaiveDateTime.utc_now()
3244 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3245 |> NaiveDateTime.to_iso8601()
3246
3247 attrs = %{params: %{}, scheduled_at: today}
3248 {:ok, _} = ScheduledActivity.create(user, attrs)
3249 {:ok, _} = ScheduledActivity.create(user, attrs)
3250
3251 conn =
3252 conn
3253 |> assign(:user, user)
3254 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3255
3256 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3257 end
3258
3259 test "returns error when total user limit is exceeded", %{conn: conn} do
3260 user = insert(:user)
3261
3262 today =
3263 NaiveDateTime.utc_now()
3264 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3265 |> NaiveDateTime.to_iso8601()
3266
3267 tomorrow =
3268 NaiveDateTime.utc_now()
3269 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3270 |> NaiveDateTime.to_iso8601()
3271
3272 attrs = %{params: %{}, scheduled_at: today}
3273 {:ok, _} = ScheduledActivity.create(user, attrs)
3274 {:ok, _} = ScheduledActivity.create(user, attrs)
3275 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3276
3277 conn =
3278 conn
3279 |> assign(:user, user)
3280 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3281
3282 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3283 end
3284
3285 test "shows scheduled activities", %{conn: conn} do
3286 user = insert(:user)
3287 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3288 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3289 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3290 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3291
3292 conn =
3293 conn
3294 |> assign(:user, user)
3295
3296 # min_id
3297 conn_res =
3298 conn
3299 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3300
3301 result = json_response(conn_res, 200)
3302 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3303
3304 # since_id
3305 conn_res =
3306 conn
3307 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3308
3309 result = json_response(conn_res, 200)
3310 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3311
3312 # max_id
3313 conn_res =
3314 conn
3315 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3316
3317 result = json_response(conn_res, 200)
3318 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3319 end
3320
3321 test "shows a scheduled activity", %{conn: conn} do
3322 user = insert(:user)
3323 scheduled_activity = insert(:scheduled_activity, user: user)
3324
3325 res_conn =
3326 conn
3327 |> assign(:user, user)
3328 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3329
3330 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3331 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3332
3333 res_conn =
3334 conn
3335 |> assign(:user, user)
3336 |> get("/api/v1/scheduled_statuses/404")
3337
3338 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3339 end
3340
3341 test "updates a scheduled activity", %{conn: conn} do
3342 user = insert(:user)
3343 scheduled_activity = insert(:scheduled_activity, user: user)
3344
3345 new_scheduled_at =
3346 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3347
3348 res_conn =
3349 conn
3350 |> assign(:user, user)
3351 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3352 scheduled_at: new_scheduled_at
3353 })
3354
3355 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3356 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3357
3358 res_conn =
3359 conn
3360 |> assign(:user, user)
3361 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3362
3363 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3364 end
3365
3366 test "deletes a scheduled activity", %{conn: conn} do
3367 user = insert(:user)
3368 scheduled_activity = insert(:scheduled_activity, user: user)
3369
3370 res_conn =
3371 conn
3372 |> assign(:user, user)
3373 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3374
3375 assert %{} = json_response(res_conn, 200)
3376 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3377
3378 res_conn =
3379 conn
3380 |> assign(:user, user)
3381 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3382
3383 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3384 end
3385 end
3386
3387 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3388 user1 = insert(:user)
3389 user2 = insert(:user)
3390 user3 = insert(:user)
3391
3392 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3393
3394 # Reply to status from another user
3395 conn1 =
3396 conn
3397 |> assign(:user, user2)
3398 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3399
3400 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3401
3402 activity = Activity.get_by_id_with_object(id)
3403
3404 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3405 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3406
3407 # Reblog from the third user
3408 conn2 =
3409 conn
3410 |> assign(:user, user3)
3411 |> post("/api/v1/statuses/#{activity.id}/reblog")
3412
3413 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3414 json_response(conn2, 200)
3415
3416 assert to_string(activity.id) == id
3417
3418 # Getting third user status
3419 conn3 =
3420 conn
3421 |> assign(:user, user3)
3422 |> get("api/v1/timelines/home")
3423
3424 [reblogged_activity] = json_response(conn3, 200)
3425
3426 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3427
3428 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3429 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3430 end
3431
3432 describe "create account by app" do
3433 test "Account registration via Application", %{conn: conn} do
3434 conn =
3435 conn
3436 |> post("/api/v1/apps", %{
3437 client_name: "client_name",
3438 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3439 scopes: "read, write, follow"
3440 })
3441
3442 %{
3443 "client_id" => client_id,
3444 "client_secret" => client_secret,
3445 "id" => _,
3446 "name" => "client_name",
3447 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3448 "vapid_key" => _,
3449 "website" => nil
3450 } = json_response(conn, 200)
3451
3452 conn =
3453 conn
3454 |> post("/oauth/token", %{
3455 grant_type: "client_credentials",
3456 client_id: client_id,
3457 client_secret: client_secret
3458 })
3459
3460 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3461 json_response(conn, 200)
3462
3463 assert token
3464 token_from_db = Repo.get_by(Token, token: token)
3465 assert token_from_db
3466 assert refresh
3467 assert scope == "read write follow"
3468
3469 conn =
3470 build_conn()
3471 |> put_req_header("authorization", "Bearer " <> token)
3472 |> post("/api/v1/accounts", %{
3473 username: "lain",
3474 email: "lain@example.org",
3475 password: "PlzDontHackLain",
3476 agreement: true
3477 })
3478
3479 %{
3480 "access_token" => token,
3481 "created_at" => _created_at,
3482 "scope" => _scope,
3483 "token_type" => "Bearer"
3484 } = json_response(conn, 200)
3485
3486 token_from_db = Repo.get_by(Token, token: token)
3487 assert token_from_db
3488 token_from_db = Repo.preload(token_from_db, :user)
3489 assert token_from_db.user
3490
3491 assert token_from_db.user.info.confirmation_pending
3492 end
3493
3494 test "rate limit", %{conn: conn} do
3495 app_token = insert(:oauth_token, user: nil)
3496
3497 conn =
3498 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3499 |> Map.put(:remote_ip, {15, 15, 15, 15})
3500
3501 for i <- 1..5 do
3502 conn =
3503 conn
3504 |> post("/api/v1/accounts", %{
3505 username: "#{i}lain",
3506 email: "#{i}lain@example.org",
3507 password: "PlzDontHackLain",
3508 agreement: true
3509 })
3510
3511 %{
3512 "access_token" => token,
3513 "created_at" => _created_at,
3514 "scope" => _scope,
3515 "token_type" => "Bearer"
3516 } = json_response(conn, 200)
3517
3518 token_from_db = Repo.get_by(Token, token: token)
3519 assert token_from_db
3520 token_from_db = Repo.preload(token_from_db, :user)
3521 assert token_from_db.user
3522
3523 assert token_from_db.user.info.confirmation_pending
3524 end
3525
3526 conn =
3527 conn
3528 |> post("/api/v1/accounts", %{
3529 username: "6lain",
3530 email: "6lain@example.org",
3531 password: "PlzDontHackLain",
3532 agreement: true
3533 })
3534
3535 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3536 end
3537 end
3538
3539 describe "GET /api/v1/polls/:id" do
3540 test "returns poll entity for object id", %{conn: conn} do
3541 user = insert(:user)
3542
3543 {:ok, activity} =
3544 CommonAPI.post(user, %{
3545 "status" => "Pleroma does",
3546 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3547 })
3548
3549 object = Object.normalize(activity)
3550
3551 conn =
3552 conn
3553 |> assign(:user, user)
3554 |> get("/api/v1/polls/#{object.id}")
3555
3556 response = json_response(conn, 200)
3557 id = to_string(object.id)
3558 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3559 end
3560
3561 test "does not expose polls for private statuses", %{conn: conn} do
3562 user = insert(:user)
3563 other_user = insert(:user)
3564
3565 {:ok, activity} =
3566 CommonAPI.post(user, %{
3567 "status" => "Pleroma does",
3568 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3569 "visibility" => "private"
3570 })
3571
3572 object = Object.normalize(activity)
3573
3574 conn =
3575 conn
3576 |> assign(:user, other_user)
3577 |> get("/api/v1/polls/#{object.id}")
3578
3579 assert json_response(conn, 404)
3580 end
3581 end
3582
3583 describe "POST /api/v1/polls/:id/votes" do
3584 test "votes are added to the poll", %{conn: conn} do
3585 user = insert(:user)
3586 other_user = insert(:user)
3587
3588 {:ok, activity} =
3589 CommonAPI.post(user, %{
3590 "status" => "A very delicious sandwich",
3591 "poll" => %{
3592 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3593 "expires_in" => 20,
3594 "multiple" => true
3595 }
3596 })
3597
3598 object = Object.normalize(activity)
3599
3600 conn =
3601 conn
3602 |> assign(:user, other_user)
3603 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3604
3605 assert json_response(conn, 200)
3606 object = Object.get_by_id(object.id)
3607
3608 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3609 total_items == 1
3610 end)
3611 end
3612
3613 test "author can't vote", %{conn: conn} do
3614 user = insert(:user)
3615
3616 {:ok, activity} =
3617 CommonAPI.post(user, %{
3618 "status" => "Am I cute?",
3619 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3620 })
3621
3622 object = Object.normalize(activity)
3623
3624 assert conn
3625 |> assign(:user, user)
3626 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3627 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3628
3629 object = Object.get_by_id(object.id)
3630
3631 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3632 end
3633
3634 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3635 user = insert(:user)
3636 other_user = insert(:user)
3637
3638 {:ok, activity} =
3639 CommonAPI.post(user, %{
3640 "status" => "The glass is",
3641 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3642 })
3643
3644 object = Object.normalize(activity)
3645
3646 assert conn
3647 |> assign(:user, other_user)
3648 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3649 |> json_response(422) == %{"error" => "Too many choices"}
3650
3651 object = Object.get_by_id(object.id)
3652
3653 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3654 total_items == 1
3655 end)
3656 end
3657
3658 test "does not allow choice index to be greater than options count", %{conn: conn} do
3659 user = insert(:user)
3660 other_user = insert(:user)
3661
3662 {:ok, activity} =
3663 CommonAPI.post(user, %{
3664 "status" => "Am I cute?",
3665 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3666 })
3667
3668 object = Object.normalize(activity)
3669
3670 conn =
3671 conn
3672 |> assign(:user, other_user)
3673 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3674
3675 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3676 end
3677
3678 test "returns 404 error when object is not exist", %{conn: conn} do
3679 user = insert(:user)
3680
3681 conn =
3682 conn
3683 |> assign(:user, user)
3684 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3685
3686 assert json_response(conn, 404) == %{"error" => "Record not found"}
3687 end
3688
3689 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3690 user = insert(:user)
3691 other_user = insert(:user)
3692
3693 {:ok, activity} =
3694 CommonAPI.post(user, %{
3695 "status" => "Am I cute?",
3696 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3697 "visibility" => "private"
3698 })
3699
3700 object = Object.normalize(activity)
3701
3702 conn =
3703 conn
3704 |> assign(:user, other_user)
3705 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3706
3707 assert json_response(conn, 404) == %{"error" => "Record not found"}
3708 end
3709 end
3710
3711 describe "GET /api/v1/statuses/:id/favourited_by" do
3712 setup do
3713 user = insert(:user)
3714 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3715
3716 conn =
3717 build_conn()
3718 |> assign(:user, user)
3719
3720 [conn: conn, activity: activity, user: user]
3721 end
3722
3723 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3724 other_user = insert(:user)
3725 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3726
3727 response =
3728 conn
3729 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3730 |> json_response(:ok)
3731
3732 [%{"id" => id}] = response
3733
3734 assert id == other_user.id
3735 end
3736
3737 test "returns empty array when status has not been favorited yet", %{
3738 conn: conn,
3739 activity: activity
3740 } do
3741 response =
3742 conn
3743 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3744 |> json_response(:ok)
3745
3746 assert Enum.empty?(response)
3747 end
3748
3749 test "does not return users who have favorited the status but are blocked", %{
3750 conn: %{assigns: %{user: user}} = conn,
3751 activity: activity
3752 } do
3753 other_user = insert(:user)
3754 {:ok, user} = User.block(user, other_user)
3755
3756 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3757
3758 response =
3759 conn
3760 |> assign(:user, user)
3761 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3762 |> json_response(:ok)
3763
3764 assert Enum.empty?(response)
3765 end
3766
3767 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3768 other_user = insert(:user)
3769 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3770
3771 response =
3772 conn
3773 |> assign(:user, nil)
3774 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3775 |> json_response(:ok)
3776
3777 [%{"id" => id}] = response
3778 assert id == other_user.id
3779 end
3780
3781 test "requires authentification for private posts", %{conn: conn, user: user} do
3782 other_user = insert(:user)
3783
3784 {:ok, activity} =
3785 CommonAPI.post(user, %{
3786 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3787 "visibility" => "direct"
3788 })
3789
3790 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3791
3792 conn
3793 |> assign(:user, nil)
3794 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3795 |> json_response(404)
3796
3797 response =
3798 build_conn()
3799 |> assign(:user, other_user)
3800 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3801 |> json_response(200)
3802
3803 [%{"id" => id}] = response
3804 assert id == other_user.id
3805 end
3806 end
3807
3808 describe "GET /api/v1/statuses/:id/reblogged_by" do
3809 setup do
3810 user = insert(:user)
3811 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3812
3813 conn =
3814 build_conn()
3815 |> assign(:user, user)
3816
3817 [conn: conn, activity: activity, user: user]
3818 end
3819
3820 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3821 other_user = insert(:user)
3822 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3823
3824 response =
3825 conn
3826 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3827 |> json_response(:ok)
3828
3829 [%{"id" => id}] = response
3830
3831 assert id == other_user.id
3832 end
3833
3834 test "returns empty array when status has not been reblogged yet", %{
3835 conn: conn,
3836 activity: activity
3837 } do
3838 response =
3839 conn
3840 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3841 |> json_response(:ok)
3842
3843 assert Enum.empty?(response)
3844 end
3845
3846 test "does not return users who have reblogged the status but are blocked", %{
3847 conn: %{assigns: %{user: user}} = conn,
3848 activity: activity
3849 } do
3850 other_user = insert(:user)
3851 {:ok, user} = User.block(user, other_user)
3852
3853 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3854
3855 response =
3856 conn
3857 |> assign(:user, user)
3858 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3859 |> json_response(:ok)
3860
3861 assert Enum.empty?(response)
3862 end
3863
3864 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3865 other_user = insert(:user)
3866 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3867
3868 response =
3869 conn
3870 |> assign(:user, nil)
3871 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3872 |> json_response(:ok)
3873
3874 [%{"id" => id}] = response
3875 assert id == other_user.id
3876 end
3877
3878 test "requires authentification for private posts", %{conn: conn, user: user} do
3879 other_user = insert(:user)
3880
3881 {:ok, activity} =
3882 CommonAPI.post(user, %{
3883 "status" => "@#{other_user.nickname} wanna get some #cofe together?",
3884 "visibility" => "direct"
3885 })
3886
3887 conn
3888 |> assign(:user, nil)
3889 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3890 |> json_response(404)
3891
3892 response =
3893 build_conn()
3894 |> assign(:user, other_user)
3895 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3896 |> json_response(200)
3897
3898 assert [] == response
3899 end
3900 end
3901
3902 describe "POST /auth/password, with valid parameters" do
3903 setup %{conn: conn} do
3904 user = insert(:user)
3905 conn = post(conn, "/auth/password?email=#{user.email}")
3906 %{conn: conn, user: user}
3907 end
3908
3909 test "it returns 204", %{conn: conn} do
3910 assert json_response(conn, :no_content)
3911 end
3912
3913 test "it creates a PasswordResetToken record for user", %{user: user} do
3914 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3915 assert token_record
3916 end
3917
3918 test "it sends an email to user", %{user: user} do
3919 ObanHelpers.perform_all()
3920 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
3921
3922 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
3923 notify_email = Config.get([:instance, :notify_email])
3924 instance_name = Config.get([:instance, :name])
3925
3926 assert_email_sent(
3927 from: {instance_name, notify_email},
3928 to: {user.name, user.email},
3929 html_body: email.html_body
3930 )
3931 end
3932 end
3933
3934 describe "POST /auth/password, with invalid parameters" do
3935 setup do
3936 user = insert(:user)
3937 {:ok, user: user}
3938 end
3939
3940 test "it returns 404 when user is not found", %{conn: conn, user: user} do
3941 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
3942 assert conn.status == 404
3943 assert conn.resp_body == ""
3944 end
3945
3946 test "it returns 400 when user is not local", %{conn: conn, user: user} do
3947 {:ok, user} = Repo.update(Changeset.change(user, local: false))
3948 conn = post(conn, "/auth/password?email=#{user.email}")
3949 assert conn.status == 400
3950 assert conn.resp_body == ""
3951 end
3952 end
3953
3954 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
3955 setup do
3956 user = insert(:user)
3957 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
3958
3959 {:ok, user} =
3960 user
3961 |> Changeset.change()
3962 |> Changeset.put_embed(:info, info_change)
3963 |> Repo.update()
3964
3965 assert user.info.confirmation_pending
3966
3967 [user: user]
3968 end
3969
3970 clear_config([:instance, :account_activation_required]) do
3971 Config.put([:instance, :account_activation_required], true)
3972 end
3973
3974 test "resend account confirmation email", %{conn: conn, user: user} do
3975 conn
3976 |> assign(:user, user)
3977 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
3978 |> json_response(:no_content)
3979
3980 ObanHelpers.perform_all()
3981
3982 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
3983 notify_email = Config.get([:instance, :notify_email])
3984 instance_name = Config.get([:instance, :name])
3985
3986 assert_email_sent(
3987 from: {instance_name, notify_email},
3988 to: {user.name, user.email},
3989 html_body: email.html_body
3990 )
3991 end
3992 end
3993
3994 describe "GET /api/v1/suggestions" do
3995 setup do
3996 user = insert(:user)
3997 other_user = insert(:user)
3998 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
3999 url500 = "http://test500?#{host}&#{user.nickname}"
4000 url200 = "http://test200?#{host}&#{user.nickname}"
4001
4002 mock(fn
4003 %{method: :get, url: ^url500} ->
4004 %Tesla.Env{status: 500, body: "bad request"}
4005
4006 %{method: :get, url: ^url200} ->
4007 %Tesla.Env{
4008 status: 200,
4009 body:
4010 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4011 other_user.ap_id
4012 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4013 }
4014 end)
4015
4016 [user: user, other_user: other_user]
4017 end
4018
4019 clear_config(:suggestions)
4020
4021 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4022 Config.put([:suggestions, :enabled], false)
4023
4024 res =
4025 conn
4026 |> assign(:user, user)
4027 |> get("/api/v1/suggestions")
4028 |> json_response(200)
4029
4030 assert res == []
4031 end
4032
4033 test "returns error", %{conn: conn, user: user} do
4034 Config.put([:suggestions, :enabled], true)
4035 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4036
4037 assert capture_log(fn ->
4038 res =
4039 conn
4040 |> assign(:user, user)
4041 |> get("/api/v1/suggestions")
4042 |> json_response(500)
4043
4044 assert res == "Something went wrong"
4045 end) =~ "Could not retrieve suggestions"
4046 end
4047
4048 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4049 Config.put([:suggestions, :enabled], true)
4050 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4051
4052 res =
4053 conn
4054 |> assign(:user, user)
4055 |> get("/api/v1/suggestions")
4056 |> json_response(200)
4057
4058 assert res == [
4059 %{
4060 "acct" => "yj455",
4061 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4062 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4063 "id" => 0
4064 },
4065 %{
4066 "acct" => other_user.ap_id,
4067 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4068 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4069 "id" => other_user.id
4070 }
4071 ]
4072 end
4073 end
4074 end