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