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