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