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