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