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