Merge branch 'develop' into feature/addressable-lists
[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/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/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/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/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/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/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 end
1278
1279 describe "reblogging" do
1280 test "reblogs and returns the reblogged status", %{conn: conn} do
1281 activity = insert(:note_activity)
1282 user = insert(:user)
1283
1284 conn =
1285 conn
1286 |> assign(:user, user)
1287 |> post("/api/v1/statuses/#{activity.id}/reblog")
1288
1289 assert %{
1290 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1291 "reblogged" => true
1292 } = json_response(conn, 200)
1293
1294 assert to_string(activity.id) == id
1295 end
1296
1297 test "reblogged status for another user", %{conn: conn} do
1298 activity = insert(:note_activity)
1299 user1 = insert(:user)
1300 user2 = insert(:user)
1301 user3 = insert(:user)
1302 CommonAPI.favorite(activity.id, user2)
1303 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1304 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1305 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1306
1307 conn_res =
1308 conn
1309 |> assign(:user, user3)
1310 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1311
1312 assert %{
1313 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1314 "reblogged" => false,
1315 "favourited" => false,
1316 "bookmarked" => false
1317 } = json_response(conn_res, 200)
1318
1319 conn_res =
1320 conn
1321 |> assign(:user, user2)
1322 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1323
1324 assert %{
1325 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1326 "reblogged" => true,
1327 "favourited" => true,
1328 "bookmarked" => true
1329 } = json_response(conn_res, 200)
1330
1331 assert to_string(activity.id) == id
1332 end
1333 end
1334
1335 describe "unreblogging" do
1336 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1337 activity = insert(:note_activity)
1338 user = insert(:user)
1339
1340 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1341
1342 conn =
1343 conn
1344 |> assign(:user, user)
1345 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1346
1347 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1348
1349 assert to_string(activity.id) == id
1350 end
1351 end
1352
1353 describe "favoriting" do
1354 test "favs a status and returns it", %{conn: conn} do
1355 activity = insert(:note_activity)
1356 user = insert(:user)
1357
1358 conn =
1359 conn
1360 |> assign(:user, user)
1361 |> post("/api/v1/statuses/#{activity.id}/favourite")
1362
1363 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1364 json_response(conn, 200)
1365
1366 assert to_string(activity.id) == id
1367 end
1368
1369 test "returns 500 for a wrong id", %{conn: conn} do
1370 user = insert(:user)
1371
1372 resp =
1373 conn
1374 |> assign(:user, user)
1375 |> post("/api/v1/statuses/1/favourite")
1376 |> json_response(500)
1377
1378 assert resp == "Something went wrong"
1379 end
1380 end
1381
1382 describe "unfavoriting" do
1383 test "unfavorites a status and returns it", %{conn: conn} do
1384 activity = insert(:note_activity)
1385 user = insert(:user)
1386
1387 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1388
1389 conn =
1390 conn
1391 |> assign(:user, user)
1392 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1393
1394 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1395 json_response(conn, 200)
1396
1397 assert to_string(activity.id) == id
1398 end
1399 end
1400
1401 describe "user timelines" do
1402 test "gets a users statuses", %{conn: conn} do
1403 user_one = insert(:user)
1404 user_two = insert(:user)
1405 user_three = insert(:user)
1406
1407 {:ok, user_three} = User.follow(user_three, user_one)
1408
1409 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1410
1411 {:ok, direct_activity} =
1412 CommonAPI.post(user_one, %{
1413 "status" => "Hi, @#{user_two.nickname}.",
1414 "visibility" => "direct"
1415 })
1416
1417 {:ok, private_activity} =
1418 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1419
1420 resp =
1421 conn
1422 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1423
1424 assert [%{"id" => id}] = json_response(resp, 200)
1425 assert id == to_string(activity.id)
1426
1427 resp =
1428 conn
1429 |> assign(:user, user_two)
1430 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1431
1432 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1433 assert id_one == to_string(direct_activity.id)
1434 assert id_two == to_string(activity.id)
1435
1436 resp =
1437 conn
1438 |> assign(:user, user_three)
1439 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1440
1441 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1442 assert id_one == to_string(private_activity.id)
1443 assert id_two == to_string(activity.id)
1444 end
1445
1446 test "unimplemented pinned statuses feature", %{conn: conn} do
1447 note = insert(:note_activity)
1448 user = User.get_cached_by_ap_id(note.data["actor"])
1449
1450 conn =
1451 conn
1452 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1453
1454 assert json_response(conn, 200) == []
1455 end
1456
1457 test "gets an users media", %{conn: conn} do
1458 note = insert(:note_activity)
1459 user = User.get_cached_by_ap_id(note.data["actor"])
1460
1461 file = %Plug.Upload{
1462 content_type: "image/jpg",
1463 path: Path.absname("test/fixtures/image.jpg"),
1464 filename: "an_image.jpg"
1465 }
1466
1467 media =
1468 TwitterAPI.upload(file, user, "json")
1469 |> Poison.decode!()
1470
1471 {:ok, image_post} =
1472 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1473
1474 conn =
1475 conn
1476 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1477
1478 assert [%{"id" => id}] = json_response(conn, 200)
1479 assert id == to_string(image_post.id)
1480
1481 conn =
1482 build_conn()
1483 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1484
1485 assert [%{"id" => id}] = json_response(conn, 200)
1486 assert id == to_string(image_post.id)
1487 end
1488
1489 test "gets a user's statuses without reblogs", %{conn: conn} do
1490 user = insert(:user)
1491 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1492 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1493
1494 conn =
1495 conn
1496 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1497
1498 assert [%{"id" => id}] = json_response(conn, 200)
1499 assert id == to_string(post.id)
1500
1501 conn =
1502 conn
1503 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1504
1505 assert [%{"id" => id}] = json_response(conn, 200)
1506 assert id == to_string(post.id)
1507 end
1508
1509 test "filters user's statuses by a hashtag", %{conn: conn} do
1510 user = insert(:user)
1511 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1512 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1513
1514 conn =
1515 conn
1516 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1517
1518 assert [%{"id" => id}] = json_response(conn, 200)
1519 assert id == to_string(post.id)
1520 end
1521 end
1522
1523 describe "user relationships" do
1524 test "returns the relationships for the current user", %{conn: conn} do
1525 user = insert(:user)
1526 other_user = insert(:user)
1527 {:ok, user} = User.follow(user, other_user)
1528
1529 conn =
1530 conn
1531 |> assign(:user, user)
1532 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1533
1534 assert [relationship] = json_response(conn, 200)
1535
1536 assert to_string(other_user.id) == relationship["id"]
1537 end
1538 end
1539
1540 describe "media upload" do
1541 setup do
1542 upload_config = Pleroma.Config.get([Pleroma.Upload])
1543 proxy_config = Pleroma.Config.get([:media_proxy])
1544
1545 on_exit(fn ->
1546 Pleroma.Config.put([Pleroma.Upload], upload_config)
1547 Pleroma.Config.put([:media_proxy], proxy_config)
1548 end)
1549
1550 user = insert(:user)
1551
1552 conn =
1553 build_conn()
1554 |> assign(:user, user)
1555
1556 image = %Plug.Upload{
1557 content_type: "image/jpg",
1558 path: Path.absname("test/fixtures/image.jpg"),
1559 filename: "an_image.jpg"
1560 }
1561
1562 [conn: conn, image: image]
1563 end
1564
1565 test "returns uploaded image", %{conn: conn, image: image} do
1566 desc = "Description of the image"
1567
1568 media =
1569 conn
1570 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1571 |> json_response(:ok)
1572
1573 assert media["type"] == "image"
1574 assert media["description"] == desc
1575 assert media["id"]
1576
1577 object = Repo.get(Object, media["id"])
1578 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1579 end
1580
1581 test "returns proxied url when media proxy is enabled", %{conn: conn, image: image} do
1582 Pleroma.Config.put([Pleroma.Upload, :base_url], "https://media.pleroma.social")
1583
1584 proxy_url = "https://cache.pleroma.social"
1585 Pleroma.Config.put([:media_proxy, :enabled], true)
1586 Pleroma.Config.put([:media_proxy, :base_url], proxy_url)
1587
1588 media =
1589 conn
1590 |> post("/api/v1/media", %{"file" => image})
1591 |> json_response(:ok)
1592
1593 assert String.starts_with?(media["url"], proxy_url)
1594 end
1595
1596 test "returns media url when proxy is enabled but media url is whitelisted", %{
1597 conn: conn,
1598 image: image
1599 } do
1600 media_url = "https://media.pleroma.social"
1601 Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
1602
1603 Pleroma.Config.put([:media_proxy, :enabled], true)
1604 Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
1605 Pleroma.Config.put([:media_proxy, :whitelist], ["media.pleroma.social"])
1606
1607 media =
1608 conn
1609 |> post("/api/v1/media", %{"file" => image})
1610 |> json_response(:ok)
1611
1612 assert String.starts_with?(media["url"], media_url)
1613 end
1614 end
1615
1616 describe "locked accounts" do
1617 test "/api/v1/follow_requests works" do
1618 user = insert(:user, %{info: %User.Info{locked: true}})
1619 other_user = insert(:user)
1620
1621 {:ok, _activity} = ActivityPub.follow(other_user, user)
1622
1623 user = User.get_cached_by_id(user.id)
1624 other_user = User.get_cached_by_id(other_user.id)
1625
1626 assert User.following?(other_user, user) == false
1627
1628 conn =
1629 build_conn()
1630 |> assign(:user, user)
1631 |> get("/api/v1/follow_requests")
1632
1633 assert [relationship] = json_response(conn, 200)
1634 assert to_string(other_user.id) == relationship["id"]
1635 end
1636
1637 test "/api/v1/follow_requests/:id/authorize works" do
1638 user = insert(:user, %{info: %User.Info{locked: true}})
1639 other_user = insert(:user)
1640
1641 {:ok, _activity} = ActivityPub.follow(other_user, user)
1642
1643 user = User.get_cached_by_id(user.id)
1644 other_user = User.get_cached_by_id(other_user.id)
1645
1646 assert User.following?(other_user, user) == false
1647
1648 conn =
1649 build_conn()
1650 |> assign(:user, user)
1651 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1652
1653 assert relationship = json_response(conn, 200)
1654 assert to_string(other_user.id) == relationship["id"]
1655
1656 user = User.get_cached_by_id(user.id)
1657 other_user = User.get_cached_by_id(other_user.id)
1658
1659 assert User.following?(other_user, user) == true
1660 end
1661
1662 test "verify_credentials", %{conn: conn} do
1663 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1664
1665 conn =
1666 conn
1667 |> assign(:user, user)
1668 |> get("/api/v1/accounts/verify_credentials")
1669
1670 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1671 assert id == to_string(user.id)
1672 end
1673
1674 test "/api/v1/follow_requests/:id/reject works" do
1675 user = insert(:user, %{info: %User.Info{locked: true}})
1676 other_user = insert(:user)
1677
1678 {:ok, _activity} = ActivityPub.follow(other_user, user)
1679
1680 user = User.get_cached_by_id(user.id)
1681
1682 conn =
1683 build_conn()
1684 |> assign(:user, user)
1685 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1686
1687 assert relationship = json_response(conn, 200)
1688 assert to_string(other_user.id) == relationship["id"]
1689
1690 user = User.get_cached_by_id(user.id)
1691 other_user = User.get_cached_by_id(other_user.id)
1692
1693 assert User.following?(other_user, user) == false
1694 end
1695 end
1696
1697 test "account fetching", %{conn: conn} do
1698 user = insert(:user)
1699
1700 conn =
1701 conn
1702 |> get("/api/v1/accounts/#{user.id}")
1703
1704 assert %{"id" => id} = json_response(conn, 200)
1705 assert id == to_string(user.id)
1706
1707 conn =
1708 build_conn()
1709 |> get("/api/v1/accounts/-1")
1710
1711 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1712 end
1713
1714 test "account fetching also works nickname", %{conn: conn} do
1715 user = insert(:user)
1716
1717 conn =
1718 conn
1719 |> get("/api/v1/accounts/#{user.nickname}")
1720
1721 assert %{"id" => id} = json_response(conn, 200)
1722 assert id == user.id
1723 end
1724
1725 test "mascot upload", %{conn: conn} do
1726 user = insert(:user)
1727
1728 non_image_file = %Plug.Upload{
1729 content_type: "audio/mpeg",
1730 path: Path.absname("test/fixtures/sound.mp3"),
1731 filename: "sound.mp3"
1732 }
1733
1734 conn =
1735 conn
1736 |> assign(:user, user)
1737 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1738
1739 assert json_response(conn, 415)
1740
1741 file = %Plug.Upload{
1742 content_type: "image/jpg",
1743 path: Path.absname("test/fixtures/image.jpg"),
1744 filename: "an_image.jpg"
1745 }
1746
1747 conn =
1748 build_conn()
1749 |> assign(:user, user)
1750 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1751
1752 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1753 end
1754
1755 test "mascot retrieving", %{conn: conn} do
1756 user = insert(:user)
1757 # When user hasn't set a mascot, we should just get pleroma tan back
1758 conn =
1759 conn
1760 |> assign(:user, user)
1761 |> get("/api/v1/pleroma/mascot")
1762
1763 assert %{"url" => url} = json_response(conn, 200)
1764 assert url =~ "pleroma-fox-tan-smol"
1765
1766 # When a user sets their mascot, we should get that back
1767 file = %Plug.Upload{
1768 content_type: "image/jpg",
1769 path: Path.absname("test/fixtures/image.jpg"),
1770 filename: "an_image.jpg"
1771 }
1772
1773 conn =
1774 build_conn()
1775 |> assign(:user, user)
1776 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1777
1778 assert json_response(conn, 200)
1779
1780 user = User.get_cached_by_id(user.id)
1781
1782 conn =
1783 build_conn()
1784 |> assign(:user, user)
1785 |> get("/api/v1/pleroma/mascot")
1786
1787 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
1788 assert url =~ "an_image"
1789 end
1790
1791 test "hashtag timeline", %{conn: conn} do
1792 following = insert(:user)
1793
1794 capture_log(fn ->
1795 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1796
1797 {:ok, [_activity]} =
1798 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1799
1800 nconn =
1801 conn
1802 |> get("/api/v1/timelines/tag/2hu")
1803
1804 assert [%{"id" => id}] = json_response(nconn, 200)
1805
1806 assert id == to_string(activity.id)
1807
1808 # works for different capitalization too
1809 nconn =
1810 conn
1811 |> get("/api/v1/timelines/tag/2HU")
1812
1813 assert [%{"id" => id}] = json_response(nconn, 200)
1814
1815 assert id == to_string(activity.id)
1816 end)
1817 end
1818
1819 test "multi-hashtag timeline", %{conn: conn} do
1820 user = insert(:user)
1821
1822 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1823 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1824 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1825
1826 any_test =
1827 conn
1828 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1829
1830 [status_none, status_test1, status_test] = json_response(any_test, 200)
1831
1832 assert to_string(activity_test.id) == status_test["id"]
1833 assert to_string(activity_test1.id) == status_test1["id"]
1834 assert to_string(activity_none.id) == status_none["id"]
1835
1836 restricted_test =
1837 conn
1838 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1839
1840 assert [status_test1] == json_response(restricted_test, 200)
1841
1842 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1843
1844 assert [status_none] == json_response(all_test, 200)
1845 end
1846
1847 test "getting followers", %{conn: conn} do
1848 user = insert(:user)
1849 other_user = insert(:user)
1850 {:ok, user} = User.follow(user, other_user)
1851
1852 conn =
1853 conn
1854 |> get("/api/v1/accounts/#{other_user.id}/followers")
1855
1856 assert [%{"id" => id}] = json_response(conn, 200)
1857 assert id == to_string(user.id)
1858 end
1859
1860 test "getting followers, hide_followers", %{conn: conn} do
1861 user = insert(:user)
1862 other_user = insert(:user, %{info: %{hide_followers: true}})
1863 {:ok, _user} = User.follow(user, other_user)
1864
1865 conn =
1866 conn
1867 |> get("/api/v1/accounts/#{other_user.id}/followers")
1868
1869 assert [] == json_response(conn, 200)
1870 end
1871
1872 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1873 user = insert(:user)
1874 other_user = insert(:user, %{info: %{hide_followers: true}})
1875 {:ok, _user} = User.follow(user, other_user)
1876
1877 conn =
1878 conn
1879 |> assign(:user, other_user)
1880 |> get("/api/v1/accounts/#{other_user.id}/followers")
1881
1882 refute [] == json_response(conn, 200)
1883 end
1884
1885 test "getting followers, pagination", %{conn: conn} do
1886 user = insert(:user)
1887 follower1 = insert(:user)
1888 follower2 = insert(:user)
1889 follower3 = insert(:user)
1890 {:ok, _} = User.follow(follower1, user)
1891 {:ok, _} = User.follow(follower2, user)
1892 {:ok, _} = User.follow(follower3, user)
1893
1894 conn =
1895 conn
1896 |> assign(:user, user)
1897
1898 res_conn =
1899 conn
1900 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1901
1902 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1903 assert id3 == follower3.id
1904 assert id2 == follower2.id
1905
1906 res_conn =
1907 conn
1908 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1909
1910 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1911 assert id2 == follower2.id
1912 assert id1 == follower1.id
1913
1914 res_conn =
1915 conn
1916 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1917
1918 assert [%{"id" => id2}] = json_response(res_conn, 200)
1919 assert id2 == follower2.id
1920
1921 assert [link_header] = get_resp_header(res_conn, "link")
1922 assert link_header =~ ~r/min_id=#{follower2.id}/
1923 assert link_header =~ ~r/max_id=#{follower2.id}/
1924 end
1925
1926 test "getting following", %{conn: conn} do
1927 user = insert(:user)
1928 other_user = insert(:user)
1929 {:ok, user} = User.follow(user, other_user)
1930
1931 conn =
1932 conn
1933 |> get("/api/v1/accounts/#{user.id}/following")
1934
1935 assert [%{"id" => id}] = json_response(conn, 200)
1936 assert id == to_string(other_user.id)
1937 end
1938
1939 test "getting following, hide_follows", %{conn: conn} do
1940 user = insert(:user, %{info: %{hide_follows: true}})
1941 other_user = insert(:user)
1942 {:ok, user} = User.follow(user, other_user)
1943
1944 conn =
1945 conn
1946 |> get("/api/v1/accounts/#{user.id}/following")
1947
1948 assert [] == json_response(conn, 200)
1949 end
1950
1951 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1952 user = insert(:user, %{info: %{hide_follows: true}})
1953 other_user = insert(:user)
1954 {:ok, user} = User.follow(user, other_user)
1955
1956 conn =
1957 conn
1958 |> assign(:user, user)
1959 |> get("/api/v1/accounts/#{user.id}/following")
1960
1961 refute [] == json_response(conn, 200)
1962 end
1963
1964 test "getting following, pagination", %{conn: conn} do
1965 user = insert(:user)
1966 following1 = insert(:user)
1967 following2 = insert(:user)
1968 following3 = insert(:user)
1969 {:ok, _} = User.follow(user, following1)
1970 {:ok, _} = User.follow(user, following2)
1971 {:ok, _} = User.follow(user, following3)
1972
1973 conn =
1974 conn
1975 |> assign(:user, user)
1976
1977 res_conn =
1978 conn
1979 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1980
1981 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1982 assert id3 == following3.id
1983 assert id2 == following2.id
1984
1985 res_conn =
1986 conn
1987 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1988
1989 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1990 assert id2 == following2.id
1991 assert id1 == following1.id
1992
1993 res_conn =
1994 conn
1995 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1996
1997 assert [%{"id" => id2}] = json_response(res_conn, 200)
1998 assert id2 == following2.id
1999
2000 assert [link_header] = get_resp_header(res_conn, "link")
2001 assert link_header =~ ~r/min_id=#{following2.id}/
2002 assert link_header =~ ~r/max_id=#{following2.id}/
2003 end
2004
2005 test "following / unfollowing a user", %{conn: conn} do
2006 user = insert(:user)
2007 other_user = insert(:user)
2008
2009 conn =
2010 conn
2011 |> assign(:user, user)
2012 |> post("/api/v1/accounts/#{other_user.id}/follow")
2013
2014 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2015
2016 user = User.get_cached_by_id(user.id)
2017
2018 conn =
2019 build_conn()
2020 |> assign(:user, user)
2021 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2022
2023 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2024
2025 user = User.get_cached_by_id(user.id)
2026
2027 conn =
2028 build_conn()
2029 |> assign(:user, user)
2030 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2031
2032 assert %{"id" => id} = json_response(conn, 200)
2033 assert id == to_string(other_user.id)
2034 end
2035
2036 test "following without reblogs" do
2037 follower = insert(:user)
2038 followed = insert(:user)
2039 other_user = insert(:user)
2040
2041 conn =
2042 build_conn()
2043 |> assign(:user, follower)
2044 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2045
2046 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2047
2048 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2049 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2050
2051 conn =
2052 build_conn()
2053 |> assign(:user, User.get_cached_by_id(follower.id))
2054 |> get("/api/v1/timelines/home")
2055
2056 assert [] == json_response(conn, 200)
2057
2058 conn =
2059 build_conn()
2060 |> assign(:user, follower)
2061 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2062
2063 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2064
2065 conn =
2066 build_conn()
2067 |> assign(:user, User.get_cached_by_id(follower.id))
2068 |> get("/api/v1/timelines/home")
2069
2070 expected_activity_id = reblog.id
2071 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2072 end
2073
2074 test "following / unfollowing errors" do
2075 user = insert(:user)
2076
2077 conn =
2078 build_conn()
2079 |> assign(:user, user)
2080
2081 # self follow
2082 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2083 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2084
2085 # self unfollow
2086 user = User.get_cached_by_id(user.id)
2087 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2088 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2089
2090 # self follow via uri
2091 user = User.get_cached_by_id(user.id)
2092 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2093 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2094
2095 # follow non existing user
2096 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2097 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2098
2099 # follow non existing user via uri
2100 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2101 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2102
2103 # unfollow non existing user
2104 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2105 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2106 end
2107
2108 test "muting / unmuting a user", %{conn: conn} do
2109 user = insert(:user)
2110 other_user = insert(:user)
2111
2112 conn =
2113 conn
2114 |> assign(:user, user)
2115 |> post("/api/v1/accounts/#{other_user.id}/mute")
2116
2117 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
2118
2119 user = User.get_cached_by_id(user.id)
2120
2121 conn =
2122 build_conn()
2123 |> assign(:user, user)
2124 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2125
2126 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
2127 end
2128
2129 test "subscribing / unsubscribing to a user", %{conn: conn} do
2130 user = insert(:user)
2131 subscription_target = insert(:user)
2132
2133 conn =
2134 conn
2135 |> assign(:user, user)
2136 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2137
2138 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2139
2140 conn =
2141 build_conn()
2142 |> assign(:user, user)
2143 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2144
2145 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2146 end
2147
2148 test "getting a list of mutes", %{conn: conn} do
2149 user = insert(:user)
2150 other_user = insert(:user)
2151
2152 {:ok, user} = User.mute(user, other_user)
2153
2154 conn =
2155 conn
2156 |> assign(:user, user)
2157 |> get("/api/v1/mutes")
2158
2159 other_user_id = to_string(other_user.id)
2160 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2161 end
2162
2163 test "blocking / unblocking a user", %{conn: conn} do
2164 user = insert(:user)
2165 other_user = insert(:user)
2166
2167 conn =
2168 conn
2169 |> assign(:user, user)
2170 |> post("/api/v1/accounts/#{other_user.id}/block")
2171
2172 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2173
2174 user = User.get_cached_by_id(user.id)
2175
2176 conn =
2177 build_conn()
2178 |> assign(:user, user)
2179 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2180
2181 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2182 end
2183
2184 test "getting a list of blocks", %{conn: conn} do
2185 user = insert(:user)
2186 other_user = insert(:user)
2187
2188 {:ok, user} = User.block(user, other_user)
2189
2190 conn =
2191 conn
2192 |> assign(:user, user)
2193 |> get("/api/v1/blocks")
2194
2195 other_user_id = to_string(other_user.id)
2196 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2197 end
2198
2199 test "blocking / unblocking a domain", %{conn: conn} do
2200 user = insert(:user)
2201 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2202
2203 conn =
2204 conn
2205 |> assign(:user, user)
2206 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2207
2208 assert %{} = json_response(conn, 200)
2209 user = User.get_cached_by_ap_id(user.ap_id)
2210 assert User.blocks?(user, other_user)
2211
2212 conn =
2213 build_conn()
2214 |> assign(:user, user)
2215 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2216
2217 assert %{} = json_response(conn, 200)
2218 user = User.get_cached_by_ap_id(user.ap_id)
2219 refute User.blocks?(user, other_user)
2220 end
2221
2222 test "getting a list of domain blocks", %{conn: conn} do
2223 user = insert(:user)
2224
2225 {:ok, user} = User.block_domain(user, "bad.site")
2226 {:ok, user} = User.block_domain(user, "even.worse.site")
2227
2228 conn =
2229 conn
2230 |> assign(:user, user)
2231 |> get("/api/v1/domain_blocks")
2232
2233 domain_blocks = json_response(conn, 200)
2234
2235 assert "bad.site" in domain_blocks
2236 assert "even.worse.site" in domain_blocks
2237 end
2238
2239 test "unimplemented follow_requests, blocks, domain blocks" do
2240 user = insert(:user)
2241
2242 ["blocks", "domain_blocks", "follow_requests"]
2243 |> Enum.each(fn endpoint ->
2244 conn =
2245 build_conn()
2246 |> assign(:user, user)
2247 |> get("/api/v1/#{endpoint}")
2248
2249 assert [] = json_response(conn, 200)
2250 end)
2251 end
2252
2253 test "returns the favorites of a user", %{conn: conn} do
2254 user = insert(:user)
2255 other_user = insert(:user)
2256
2257 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2258 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2259
2260 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2261
2262 first_conn =
2263 conn
2264 |> assign(:user, user)
2265 |> get("/api/v1/favourites")
2266
2267 assert [status] = json_response(first_conn, 200)
2268 assert status["id"] == to_string(activity.id)
2269
2270 assert [{"link", _link_header}] =
2271 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2272
2273 # Honours query params
2274 {:ok, second_activity} =
2275 CommonAPI.post(other_user, %{
2276 "status" =>
2277 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2278 })
2279
2280 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2281
2282 last_like = status["id"]
2283
2284 second_conn =
2285 conn
2286 |> assign(:user, user)
2287 |> get("/api/v1/favourites?since_id=#{last_like}")
2288
2289 assert [second_status] = json_response(second_conn, 200)
2290 assert second_status["id"] == to_string(second_activity.id)
2291
2292 third_conn =
2293 conn
2294 |> assign(:user, user)
2295 |> get("/api/v1/favourites?limit=0")
2296
2297 assert [] = json_response(third_conn, 200)
2298 end
2299
2300 describe "getting favorites timeline of specified user" do
2301 setup do
2302 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2303 [current_user: current_user, user: user]
2304 end
2305
2306 test "returns list of statuses favorited by specified user", %{
2307 conn: conn,
2308 current_user: current_user,
2309 user: user
2310 } do
2311 [activity | _] = insert_pair(:note_activity)
2312 CommonAPI.favorite(activity.id, user)
2313
2314 response =
2315 conn
2316 |> assign(:user, current_user)
2317 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2318 |> json_response(:ok)
2319
2320 [like] = response
2321
2322 assert length(response) == 1
2323 assert like["id"] == activity.id
2324 end
2325
2326 test "returns favorites for specified user_id when user is not logged in", %{
2327 conn: conn,
2328 user: user
2329 } do
2330 activity = insert(:note_activity)
2331 CommonAPI.favorite(activity.id, user)
2332
2333 response =
2334 conn
2335 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2336 |> json_response(:ok)
2337
2338 assert length(response) == 1
2339 end
2340
2341 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2342 conn: conn,
2343 current_user: current_user,
2344 user: user
2345 } do
2346 {:ok, direct} =
2347 CommonAPI.post(current_user, %{
2348 "status" => "Hi @#{user.nickname}!",
2349 "visibility" => "direct"
2350 })
2351
2352 CommonAPI.favorite(direct.id, user)
2353
2354 response =
2355 conn
2356 |> assign(:user, current_user)
2357 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2358 |> json_response(:ok)
2359
2360 assert length(response) == 1
2361
2362 anonymous_response =
2363 conn
2364 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2365 |> json_response(:ok)
2366
2367 assert Enum.empty?(anonymous_response)
2368 end
2369
2370 test "does not return others' favorited DM when user is not one of recipients", %{
2371 conn: conn,
2372 current_user: current_user,
2373 user: user
2374 } do
2375 user_two = insert(:user)
2376
2377 {:ok, direct} =
2378 CommonAPI.post(user_two, %{
2379 "status" => "Hi @#{user.nickname}!",
2380 "visibility" => "direct"
2381 })
2382
2383 CommonAPI.favorite(direct.id, user)
2384
2385 response =
2386 conn
2387 |> assign(:user, current_user)
2388 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2389 |> json_response(:ok)
2390
2391 assert Enum.empty?(response)
2392 end
2393
2394 test "paginates favorites using since_id and max_id", %{
2395 conn: conn,
2396 current_user: current_user,
2397 user: user
2398 } do
2399 activities = insert_list(10, :note_activity)
2400
2401 Enum.each(activities, fn activity ->
2402 CommonAPI.favorite(activity.id, user)
2403 end)
2404
2405 third_activity = Enum.at(activities, 2)
2406 seventh_activity = Enum.at(activities, 6)
2407
2408 response =
2409 conn
2410 |> assign(:user, current_user)
2411 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2412 since_id: third_activity.id,
2413 max_id: seventh_activity.id
2414 })
2415 |> json_response(:ok)
2416
2417 assert length(response) == 3
2418 refute third_activity in response
2419 refute seventh_activity in response
2420 end
2421
2422 test "limits favorites using limit parameter", %{
2423 conn: conn,
2424 current_user: current_user,
2425 user: user
2426 } do
2427 7
2428 |> insert_list(:note_activity)
2429 |> Enum.each(fn activity ->
2430 CommonAPI.favorite(activity.id, user)
2431 end)
2432
2433 response =
2434 conn
2435 |> assign(:user, current_user)
2436 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2437 |> json_response(:ok)
2438
2439 assert length(response) == 3
2440 end
2441
2442 test "returns empty response when user does not have any favorited statuses", %{
2443 conn: conn,
2444 current_user: current_user,
2445 user: user
2446 } do
2447 response =
2448 conn
2449 |> assign(:user, current_user)
2450 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2451 |> json_response(:ok)
2452
2453 assert Enum.empty?(response)
2454 end
2455
2456 test "returns 404 error when specified user is not exist", %{conn: conn} do
2457 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2458
2459 assert json_response(conn, 404) == %{"error" => "Record not found"}
2460 end
2461
2462 test "returns 403 error when user has hidden own favorites", %{
2463 conn: conn,
2464 current_user: current_user
2465 } do
2466 user = insert(:user, %{info: %{hide_favorites: true}})
2467 activity = insert(:note_activity)
2468 CommonAPI.favorite(activity.id, user)
2469
2470 conn =
2471 conn
2472 |> assign(:user, current_user)
2473 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2474
2475 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2476 end
2477
2478 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2479 user = insert(:user)
2480 activity = insert(:note_activity)
2481 CommonAPI.favorite(activity.id, user)
2482
2483 conn =
2484 conn
2485 |> assign(:user, current_user)
2486 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2487
2488 assert user.info.hide_favorites
2489 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2490 end
2491 end
2492
2493 test "get instance information", %{conn: conn} do
2494 conn = get(conn, "/api/v1/instance")
2495 assert result = json_response(conn, 200)
2496
2497 email = Pleroma.Config.get([:instance, :email])
2498 # Note: not checking for "max_toot_chars" since it's optional
2499 assert %{
2500 "uri" => _,
2501 "title" => _,
2502 "description" => _,
2503 "version" => _,
2504 "email" => from_config_email,
2505 "urls" => %{
2506 "streaming_api" => _
2507 },
2508 "stats" => _,
2509 "thumbnail" => _,
2510 "languages" => _,
2511 "registrations" => _,
2512 "poll_limits" => _
2513 } = result
2514
2515 assert email == from_config_email
2516 end
2517
2518 test "get instance stats", %{conn: conn} do
2519 user = insert(:user, %{local: true})
2520
2521 user2 = insert(:user, %{local: true})
2522 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2523
2524 insert(:user, %{local: false, nickname: "u@peer1.com"})
2525 insert(:user, %{local: false, nickname: "u@peer2.com"})
2526
2527 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2528
2529 # Stats should count users with missing or nil `info.deactivated` value
2530 user = User.get_cached_by_id(user.id)
2531 info_change = Changeset.change(user.info, %{deactivated: nil})
2532
2533 {:ok, _user} =
2534 user
2535 |> Changeset.change()
2536 |> Changeset.put_embed(:info, info_change)
2537 |> User.update_and_set_cache()
2538
2539 Pleroma.Stats.update_stats()
2540
2541 conn = get(conn, "/api/v1/instance")
2542
2543 assert result = json_response(conn, 200)
2544
2545 stats = result["stats"]
2546
2547 assert stats
2548 assert stats["user_count"] == 1
2549 assert stats["status_count"] == 1
2550 assert stats["domain_count"] == 2
2551 end
2552
2553 test "get peers", %{conn: conn} do
2554 insert(:user, %{local: false, nickname: "u@peer1.com"})
2555 insert(:user, %{local: false, nickname: "u@peer2.com"})
2556
2557 Pleroma.Stats.update_stats()
2558
2559 conn = get(conn, "/api/v1/instance/peers")
2560
2561 assert result = json_response(conn, 200)
2562
2563 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2564 end
2565
2566 test "put settings", %{conn: conn} do
2567 user = insert(:user)
2568
2569 conn =
2570 conn
2571 |> assign(:user, user)
2572 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2573
2574 assert _result = json_response(conn, 200)
2575
2576 user = User.get_cached_by_ap_id(user.ap_id)
2577 assert user.info.settings == %{"programming" => "socks"}
2578 end
2579
2580 describe "pinned statuses" do
2581 setup do
2582 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2583
2584 user = insert(:user)
2585 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2586
2587 [user: user, activity: activity]
2588 end
2589
2590 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2591 {:ok, _} = CommonAPI.pin(activity.id, user)
2592
2593 result =
2594 conn
2595 |> assign(:user, user)
2596 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2597 |> json_response(200)
2598
2599 id_str = to_string(activity.id)
2600
2601 assert [%{"id" => ^id_str, "pinned" => true}] = result
2602 end
2603
2604 test "pin status", %{conn: conn, user: user, activity: activity} do
2605 id_str = to_string(activity.id)
2606
2607 assert %{"id" => ^id_str, "pinned" => true} =
2608 conn
2609 |> assign(:user, user)
2610 |> post("/api/v1/statuses/#{activity.id}/pin")
2611 |> json_response(200)
2612
2613 assert [%{"id" => ^id_str, "pinned" => true}] =
2614 conn
2615 |> assign(:user, user)
2616 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2617 |> json_response(200)
2618 end
2619
2620 test "unpin status", %{conn: conn, user: user, activity: activity} do
2621 {:ok, _} = CommonAPI.pin(activity.id, user)
2622
2623 id_str = to_string(activity.id)
2624 user = refresh_record(user)
2625
2626 assert %{"id" => ^id_str, "pinned" => false} =
2627 conn
2628 |> assign(:user, user)
2629 |> post("/api/v1/statuses/#{activity.id}/unpin")
2630 |> json_response(200)
2631
2632 assert [] =
2633 conn
2634 |> assign(:user, user)
2635 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2636 |> json_response(200)
2637 end
2638
2639 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2640 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2641
2642 id_str_one = to_string(activity_one.id)
2643
2644 assert %{"id" => ^id_str_one, "pinned" => true} =
2645 conn
2646 |> assign(:user, user)
2647 |> post("/api/v1/statuses/#{id_str_one}/pin")
2648 |> json_response(200)
2649
2650 user = refresh_record(user)
2651
2652 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2653 conn
2654 |> assign(:user, user)
2655 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2656 |> json_response(400)
2657 end
2658 end
2659
2660 describe "cards" do
2661 setup do
2662 Pleroma.Config.put([:rich_media, :enabled], true)
2663
2664 on_exit(fn ->
2665 Pleroma.Config.put([:rich_media, :enabled], false)
2666 end)
2667
2668 user = insert(:user)
2669 %{user: user}
2670 end
2671
2672 test "returns rich-media card", %{conn: conn, user: user} do
2673 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2674
2675 card_data = %{
2676 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2677 "provider_name" => "www.imdb.com",
2678 "provider_url" => "http://www.imdb.com",
2679 "title" => "The Rock",
2680 "type" => "link",
2681 "url" => "http://www.imdb.com/title/tt0117500/",
2682 "description" =>
2683 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2684 "pleroma" => %{
2685 "opengraph" => %{
2686 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2687 "title" => "The Rock",
2688 "type" => "video.movie",
2689 "url" => "http://www.imdb.com/title/tt0117500/",
2690 "description" =>
2691 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2692 }
2693 }
2694 }
2695
2696 response =
2697 conn
2698 |> get("/api/v1/statuses/#{activity.id}/card")
2699 |> json_response(200)
2700
2701 assert response == card_data
2702
2703 # works with private posts
2704 {:ok, activity} =
2705 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2706
2707 response_two =
2708 conn
2709 |> assign(:user, user)
2710 |> get("/api/v1/statuses/#{activity.id}/card")
2711 |> json_response(200)
2712
2713 assert response_two == card_data
2714 end
2715
2716 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2717 {:ok, activity} =
2718 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2719
2720 response =
2721 conn
2722 |> get("/api/v1/statuses/#{activity.id}/card")
2723 |> json_response(:ok)
2724
2725 assert response == %{
2726 "type" => "link",
2727 "title" => "Pleroma",
2728 "description" => "",
2729 "image" => nil,
2730 "provider_name" => "pleroma.social",
2731 "provider_url" => "https://pleroma.social",
2732 "url" => "https://pleroma.social/",
2733 "pleroma" => %{
2734 "opengraph" => %{
2735 "title" => "Pleroma",
2736 "type" => "website",
2737 "url" => "https://pleroma.social/"
2738 }
2739 }
2740 }
2741 end
2742 end
2743
2744 test "bookmarks" do
2745 user = insert(:user)
2746 for_user = insert(:user)
2747
2748 {:ok, activity1} =
2749 CommonAPI.post(user, %{
2750 "status" => "heweoo?"
2751 })
2752
2753 {:ok, activity2} =
2754 CommonAPI.post(user, %{
2755 "status" => "heweoo!"
2756 })
2757
2758 response1 =
2759 build_conn()
2760 |> assign(:user, for_user)
2761 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2762
2763 assert json_response(response1, 200)["bookmarked"] == true
2764
2765 response2 =
2766 build_conn()
2767 |> assign(:user, for_user)
2768 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2769
2770 assert json_response(response2, 200)["bookmarked"] == true
2771
2772 bookmarks =
2773 build_conn()
2774 |> assign(:user, for_user)
2775 |> get("/api/v1/bookmarks")
2776
2777 assert [json_response(response2, 200), json_response(response1, 200)] ==
2778 json_response(bookmarks, 200)
2779
2780 response1 =
2781 build_conn()
2782 |> assign(:user, for_user)
2783 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2784
2785 assert json_response(response1, 200)["bookmarked"] == false
2786
2787 bookmarks =
2788 build_conn()
2789 |> assign(:user, for_user)
2790 |> get("/api/v1/bookmarks")
2791
2792 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2793 end
2794
2795 describe "conversation muting" do
2796 setup do
2797 user = insert(:user)
2798 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2799
2800 [user: user, activity: activity]
2801 end
2802
2803 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2804 id_str = to_string(activity.id)
2805
2806 assert %{"id" => ^id_str, "muted" => true} =
2807 conn
2808 |> assign(:user, user)
2809 |> post("/api/v1/statuses/#{activity.id}/mute")
2810 |> json_response(200)
2811 end
2812
2813 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2814 {:ok, _} = CommonAPI.add_mute(user, activity)
2815
2816 id_str = to_string(activity.id)
2817 user = refresh_record(user)
2818
2819 assert %{"id" => ^id_str, "muted" => false} =
2820 conn
2821 |> assign(:user, user)
2822 |> post("/api/v1/statuses/#{activity.id}/unmute")
2823 |> json_response(200)
2824 end
2825 end
2826
2827 describe "reports" do
2828 setup do
2829 reporter = insert(:user)
2830 target_user = insert(:user)
2831
2832 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2833
2834 [reporter: reporter, target_user: target_user, activity: activity]
2835 end
2836
2837 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2838 assert %{"action_taken" => false, "id" => _} =
2839 conn
2840 |> assign(:user, reporter)
2841 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2842 |> json_response(200)
2843 end
2844
2845 test "submit a report with statuses and comment", %{
2846 conn: conn,
2847 reporter: reporter,
2848 target_user: target_user,
2849 activity: activity
2850 } do
2851 assert %{"action_taken" => false, "id" => _} =
2852 conn
2853 |> assign(:user, reporter)
2854 |> post("/api/v1/reports", %{
2855 "account_id" => target_user.id,
2856 "status_ids" => [activity.id],
2857 "comment" => "bad status!"
2858 })
2859 |> json_response(200)
2860 end
2861
2862 test "account_id is required", %{
2863 conn: conn,
2864 reporter: reporter,
2865 activity: activity
2866 } do
2867 assert %{"error" => "Valid `account_id` required"} =
2868 conn
2869 |> assign(:user, reporter)
2870 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2871 |> json_response(400)
2872 end
2873
2874 test "comment must be up to the size specified in the config", %{
2875 conn: conn,
2876 reporter: reporter,
2877 target_user: target_user
2878 } do
2879 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2880 comment = String.pad_trailing("a", max_size + 1, "a")
2881
2882 error = %{"error" => "Comment must be up to #{max_size} characters"}
2883
2884 assert ^error =
2885 conn
2886 |> assign(:user, reporter)
2887 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2888 |> json_response(400)
2889 end
2890 end
2891
2892 describe "link headers" do
2893 test "preserves parameters in link headers", %{conn: conn} do
2894 user = insert(:user)
2895 other_user = insert(:user)
2896
2897 {:ok, activity1} =
2898 CommonAPI.post(other_user, %{
2899 "status" => "hi @#{user.nickname}",
2900 "visibility" => "public"
2901 })
2902
2903 {:ok, activity2} =
2904 CommonAPI.post(other_user, %{
2905 "status" => "hi @#{user.nickname}",
2906 "visibility" => "public"
2907 })
2908
2909 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2910 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2911
2912 conn =
2913 conn
2914 |> assign(:user, user)
2915 |> get("/api/v1/notifications", %{media_only: true})
2916
2917 assert [link_header] = get_resp_header(conn, "link")
2918 assert link_header =~ ~r/media_only=true/
2919 assert link_header =~ ~r/min_id=#{notification2.id}/
2920 assert link_header =~ ~r/max_id=#{notification1.id}/
2921 end
2922 end
2923
2924 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2925 # Need to set an old-style integer ID to reproduce the problem
2926 # (these are no longer assigned to new accounts but were preserved
2927 # for existing accounts during the migration to flakeIDs)
2928 user_one = insert(:user, %{id: 1212})
2929 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2930
2931 resp_one =
2932 conn
2933 |> get("/api/v1/accounts/#{user_one.id}")
2934
2935 resp_two =
2936 conn
2937 |> get("/api/v1/accounts/#{user_two.nickname}")
2938
2939 resp_three =
2940 conn
2941 |> get("/api/v1/accounts/#{user_two.id}")
2942
2943 acc_one = json_response(resp_one, 200)
2944 acc_two = json_response(resp_two, 200)
2945 acc_three = json_response(resp_three, 200)
2946 refute acc_one == acc_two
2947 assert acc_two == acc_three
2948 end
2949
2950 describe "custom emoji" do
2951 test "with tags", %{conn: conn} do
2952 [emoji | _body] =
2953 conn
2954 |> get("/api/v1/custom_emojis")
2955 |> json_response(200)
2956
2957 assert Map.has_key?(emoji, "shortcode")
2958 assert Map.has_key?(emoji, "static_url")
2959 assert Map.has_key?(emoji, "tags")
2960 assert is_list(emoji["tags"])
2961 assert Map.has_key?(emoji, "category")
2962 assert Map.has_key?(emoji, "url")
2963 assert Map.has_key?(emoji, "visible_in_picker")
2964 end
2965 end
2966
2967 describe "index/2 redirections" do
2968 setup %{conn: conn} do
2969 session_opts = [
2970 store: :cookie,
2971 key: "_test",
2972 signing_salt: "cooldude"
2973 ]
2974
2975 conn =
2976 conn
2977 |> Plug.Session.call(Plug.Session.init(session_opts))
2978 |> fetch_session()
2979
2980 test_path = "/web/statuses/test"
2981 %{conn: conn, path: test_path}
2982 end
2983
2984 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2985 conn = get(conn, path)
2986
2987 assert conn.status == 302
2988 assert redirected_to(conn) == "/web/login"
2989 end
2990
2991 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2992 token = insert(:oauth_token)
2993
2994 conn =
2995 conn
2996 |> assign(:user, token.user)
2997 |> put_session(:oauth_token, token.token)
2998 |> get(path)
2999
3000 assert conn.status == 200
3001 end
3002
3003 test "saves referer path to session", %{conn: conn, path: path} do
3004 conn = get(conn, path)
3005 return_to = Plug.Conn.get_session(conn, :return_to)
3006
3007 assert return_to == path
3008 end
3009
3010 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3011 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3012 auth = insert(:oauth_authorization, app: app)
3013
3014 conn =
3015 conn
3016 |> put_session(:return_to, path)
3017 |> get("/web/login", %{code: auth.token})
3018
3019 assert conn.status == 302
3020 assert redirected_to(conn) == path
3021 end
3022
3023 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3024 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3025 auth = insert(:oauth_authorization, app: app)
3026
3027 conn = get(conn, "/web/login", %{code: auth.token})
3028
3029 assert conn.status == 302
3030 assert redirected_to(conn) == "/web/getting-started"
3031 end
3032 end
3033
3034 describe "scheduled activities" do
3035 test "creates a scheduled activity", %{conn: conn} do
3036 user = insert(:user)
3037 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3038
3039 conn =
3040 conn
3041 |> assign(:user, user)
3042 |> post("/api/v1/statuses", %{
3043 "status" => "scheduled",
3044 "scheduled_at" => scheduled_at
3045 })
3046
3047 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3048 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3049 assert [] == Repo.all(Activity)
3050 end
3051
3052 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3053 user = insert(:user)
3054 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3055
3056 file = %Plug.Upload{
3057 content_type: "image/jpg",
3058 path: Path.absname("test/fixtures/image.jpg"),
3059 filename: "an_image.jpg"
3060 }
3061
3062 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3063
3064 conn =
3065 conn
3066 |> assign(:user, user)
3067 |> post("/api/v1/statuses", %{
3068 "media_ids" => [to_string(upload.id)],
3069 "status" => "scheduled",
3070 "scheduled_at" => scheduled_at
3071 })
3072
3073 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3074 assert %{"type" => "image"} = media_attachment
3075 end
3076
3077 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3078 %{conn: conn} do
3079 user = insert(:user)
3080
3081 scheduled_at =
3082 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3083
3084 conn =
3085 conn
3086 |> assign(:user, user)
3087 |> post("/api/v1/statuses", %{
3088 "status" => "not scheduled",
3089 "scheduled_at" => scheduled_at
3090 })
3091
3092 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3093 assert [] == Repo.all(ScheduledActivity)
3094 end
3095
3096 test "returns error when daily user limit is exceeded", %{conn: conn} do
3097 user = insert(:user)
3098
3099 today =
3100 NaiveDateTime.utc_now()
3101 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3102 |> NaiveDateTime.to_iso8601()
3103
3104 attrs = %{params: %{}, scheduled_at: today}
3105 {:ok, _} = ScheduledActivity.create(user, attrs)
3106 {:ok, _} = ScheduledActivity.create(user, attrs)
3107
3108 conn =
3109 conn
3110 |> assign(:user, user)
3111 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3112
3113 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3114 end
3115
3116 test "returns error when total user limit is exceeded", %{conn: conn} do
3117 user = insert(:user)
3118
3119 today =
3120 NaiveDateTime.utc_now()
3121 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3122 |> NaiveDateTime.to_iso8601()
3123
3124 tomorrow =
3125 NaiveDateTime.utc_now()
3126 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3127 |> NaiveDateTime.to_iso8601()
3128
3129 attrs = %{params: %{}, scheduled_at: today}
3130 {:ok, _} = ScheduledActivity.create(user, attrs)
3131 {:ok, _} = ScheduledActivity.create(user, attrs)
3132 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3133
3134 conn =
3135 conn
3136 |> assign(:user, user)
3137 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3138
3139 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3140 end
3141
3142 test "shows scheduled activities", %{conn: conn} do
3143 user = insert(:user)
3144 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3145 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3146 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3147 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3148
3149 conn =
3150 conn
3151 |> assign(:user, user)
3152
3153 # min_id
3154 conn_res =
3155 conn
3156 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3157
3158 result = json_response(conn_res, 200)
3159 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3160
3161 # since_id
3162 conn_res =
3163 conn
3164 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3165
3166 result = json_response(conn_res, 200)
3167 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3168
3169 # max_id
3170 conn_res =
3171 conn
3172 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3173
3174 result = json_response(conn_res, 200)
3175 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3176 end
3177
3178 test "shows a scheduled activity", %{conn: conn} do
3179 user = insert(:user)
3180 scheduled_activity = insert(:scheduled_activity, user: user)
3181
3182 res_conn =
3183 conn
3184 |> assign(:user, user)
3185 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3186
3187 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3188 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3189
3190 res_conn =
3191 conn
3192 |> assign(:user, user)
3193 |> get("/api/v1/scheduled_statuses/404")
3194
3195 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3196 end
3197
3198 test "updates a scheduled activity", %{conn: conn} do
3199 user = insert(:user)
3200 scheduled_activity = insert(:scheduled_activity, user: user)
3201
3202 new_scheduled_at =
3203 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3204
3205 res_conn =
3206 conn
3207 |> assign(:user, user)
3208 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3209 scheduled_at: new_scheduled_at
3210 })
3211
3212 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3213 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3214
3215 res_conn =
3216 conn
3217 |> assign(:user, user)
3218 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3219
3220 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3221 end
3222
3223 test "deletes a scheduled activity", %{conn: conn} do
3224 user = insert(:user)
3225 scheduled_activity = insert(:scheduled_activity, user: user)
3226
3227 res_conn =
3228 conn
3229 |> assign(:user, user)
3230 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3231
3232 assert %{} = json_response(res_conn, 200)
3233 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3234
3235 res_conn =
3236 conn
3237 |> assign(:user, user)
3238 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3239
3240 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3241 end
3242 end
3243
3244 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3245 user1 = insert(:user)
3246 user2 = insert(:user)
3247 user3 = insert(:user)
3248
3249 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3250
3251 # Reply to status from another user
3252 conn1 =
3253 conn
3254 |> assign(:user, user2)
3255 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3256
3257 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3258
3259 activity = Activity.get_by_id_with_object(id)
3260
3261 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3262 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3263
3264 # Reblog from the third user
3265 conn2 =
3266 conn
3267 |> assign(:user, user3)
3268 |> post("/api/v1/statuses/#{activity.id}/reblog")
3269
3270 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3271 json_response(conn2, 200)
3272
3273 assert to_string(activity.id) == id
3274
3275 # Getting third user status
3276 conn3 =
3277 conn
3278 |> assign(:user, user3)
3279 |> get("api/v1/timelines/home")
3280
3281 [reblogged_activity] = json_response(conn3, 200)
3282
3283 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3284
3285 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3286 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3287 end
3288
3289 describe "create account by app" do
3290 test "Account registration via Application", %{conn: conn} do
3291 conn =
3292 conn
3293 |> post("/api/v1/apps", %{
3294 client_name: "client_name",
3295 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3296 scopes: "read, write, follow"
3297 })
3298
3299 %{
3300 "client_id" => client_id,
3301 "client_secret" => client_secret,
3302 "id" => _,
3303 "name" => "client_name",
3304 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3305 "vapid_key" => _,
3306 "website" => nil
3307 } = json_response(conn, 200)
3308
3309 conn =
3310 conn
3311 |> post("/oauth/token", %{
3312 grant_type: "client_credentials",
3313 client_id: client_id,
3314 client_secret: client_secret
3315 })
3316
3317 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3318 json_response(conn, 200)
3319
3320 assert token
3321 token_from_db = Repo.get_by(Token, token: token)
3322 assert token_from_db
3323 assert refresh
3324 assert scope == "read write follow"
3325
3326 conn =
3327 build_conn()
3328 |> put_req_header("authorization", "Bearer " <> token)
3329 |> post("/api/v1/accounts", %{
3330 username: "lain",
3331 email: "lain@example.org",
3332 password: "PlzDontHackLain",
3333 agreement: true
3334 })
3335
3336 %{
3337 "access_token" => token,
3338 "created_at" => _created_at,
3339 "scope" => _scope,
3340 "token_type" => "Bearer"
3341 } = json_response(conn, 200)
3342
3343 token_from_db = Repo.get_by(Token, token: token)
3344 assert token_from_db
3345 token_from_db = Repo.preload(token_from_db, :user)
3346 assert token_from_db.user
3347
3348 assert token_from_db.user.info.confirmation_pending
3349 end
3350
3351 test "rate limit", %{conn: conn} do
3352 app_token = insert(:oauth_token, user: nil)
3353
3354 conn =
3355 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3356 |> Map.put(:remote_ip, {15, 15, 15, 15})
3357
3358 for i <- 1..5 do
3359 conn =
3360 conn
3361 |> post("/api/v1/accounts", %{
3362 username: "#{i}lain",
3363 email: "#{i}lain@example.org",
3364 password: "PlzDontHackLain",
3365 agreement: true
3366 })
3367
3368 %{
3369 "access_token" => token,
3370 "created_at" => _created_at,
3371 "scope" => _scope,
3372 "token_type" => "Bearer"
3373 } = json_response(conn, 200)
3374
3375 token_from_db = Repo.get_by(Token, token: token)
3376 assert token_from_db
3377 token_from_db = Repo.preload(token_from_db, :user)
3378 assert token_from_db.user
3379
3380 assert token_from_db.user.info.confirmation_pending
3381 end
3382
3383 conn =
3384 conn
3385 |> post("/api/v1/accounts", %{
3386 username: "6lain",
3387 email: "6lain@example.org",
3388 password: "PlzDontHackLain",
3389 agreement: true
3390 })
3391
3392 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3393 end
3394 end
3395
3396 describe "GET /api/v1/polls/:id" do
3397 test "returns poll entity for object id", %{conn: conn} do
3398 user = insert(:user)
3399
3400 {:ok, activity} =
3401 CommonAPI.post(user, %{
3402 "status" => "Pleroma does",
3403 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3404 })
3405
3406 object = Object.normalize(activity)
3407
3408 conn =
3409 conn
3410 |> assign(:user, user)
3411 |> get("/api/v1/polls/#{object.id}")
3412
3413 response = json_response(conn, 200)
3414 id = object.id
3415 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3416 end
3417
3418 test "does not expose polls for private statuses", %{conn: conn} do
3419 user = insert(:user)
3420 other_user = insert(:user)
3421
3422 {:ok, activity} =
3423 CommonAPI.post(user, %{
3424 "status" => "Pleroma does",
3425 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3426 "visibility" => "private"
3427 })
3428
3429 object = Object.normalize(activity)
3430
3431 conn =
3432 conn
3433 |> assign(:user, other_user)
3434 |> get("/api/v1/polls/#{object.id}")
3435
3436 assert json_response(conn, 404)
3437 end
3438 end
3439
3440 describe "POST /api/v1/polls/:id/votes" do
3441 test "votes are added to the poll", %{conn: conn} do
3442 user = insert(:user)
3443 other_user = insert(:user)
3444
3445 {:ok, activity} =
3446 CommonAPI.post(user, %{
3447 "status" => "A very delicious sandwich",
3448 "poll" => %{
3449 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3450 "expires_in" => 20,
3451 "multiple" => true
3452 }
3453 })
3454
3455 object = Object.normalize(activity)
3456
3457 conn =
3458 conn
3459 |> assign(:user, other_user)
3460 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3461
3462 assert json_response(conn, 200)
3463 object = Object.get_by_id(object.id)
3464
3465 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3466 total_items == 1
3467 end)
3468 end
3469
3470 test "author can't vote", %{conn: conn} do
3471 user = insert(:user)
3472
3473 {:ok, activity} =
3474 CommonAPI.post(user, %{
3475 "status" => "Am I cute?",
3476 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3477 })
3478
3479 object = Object.normalize(activity)
3480
3481 assert conn
3482 |> assign(:user, user)
3483 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3484 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3485
3486 object = Object.get_by_id(object.id)
3487
3488 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3489 end
3490
3491 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3492 user = insert(:user)
3493 other_user = insert(:user)
3494
3495 {:ok, activity} =
3496 CommonAPI.post(user, %{
3497 "status" => "The glass is",
3498 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3499 })
3500
3501 object = Object.normalize(activity)
3502
3503 assert conn
3504 |> assign(:user, other_user)
3505 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3506 |> json_response(422) == %{"error" => "Too many choices"}
3507
3508 object = Object.get_by_id(object.id)
3509
3510 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3511 total_items == 1
3512 end)
3513 end
3514 end
3515 end