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