Merge branch 'bugfix/980-polls-double-vote' 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" => "http://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" => "http://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" => "http://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} = CommonAPI.post(user, %{"status" => "http://example.com/ogp-missing-data"})
2605
2606 response =
2607 conn
2608 |> get("/api/v1/statuses/#{activity.id}/card")
2609 |> json_response(:ok)
2610
2611 assert response == %{
2612 "type" => "link",
2613 "title" => "Pleroma",
2614 "description" => "",
2615 "image" => nil,
2616 "provider_name" => "pleroma.social",
2617 "provider_url" => "https://pleroma.social",
2618 "url" => "https://pleroma.social/",
2619 "pleroma" => %{
2620 "opengraph" => %{
2621 "title" => "Pleroma",
2622 "type" => "website",
2623 "url" => "https://pleroma.social/"
2624 }
2625 }
2626 }
2627 end
2628 end
2629
2630 test "bookmarks" do
2631 user = insert(:user)
2632 for_user = insert(:user)
2633
2634 {:ok, activity1} =
2635 CommonAPI.post(user, %{
2636 "status" => "heweoo?"
2637 })
2638
2639 {:ok, activity2} =
2640 CommonAPI.post(user, %{
2641 "status" => "heweoo!"
2642 })
2643
2644 response1 =
2645 build_conn()
2646 |> assign(:user, for_user)
2647 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2648
2649 assert json_response(response1, 200)["bookmarked"] == true
2650
2651 response2 =
2652 build_conn()
2653 |> assign(:user, for_user)
2654 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2655
2656 assert json_response(response2, 200)["bookmarked"] == true
2657
2658 bookmarks =
2659 build_conn()
2660 |> assign(:user, for_user)
2661 |> get("/api/v1/bookmarks")
2662
2663 assert [json_response(response2, 200), json_response(response1, 200)] ==
2664 json_response(bookmarks, 200)
2665
2666 response1 =
2667 build_conn()
2668 |> assign(:user, for_user)
2669 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2670
2671 assert json_response(response1, 200)["bookmarked"] == false
2672
2673 bookmarks =
2674 build_conn()
2675 |> assign(:user, for_user)
2676 |> get("/api/v1/bookmarks")
2677
2678 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2679 end
2680
2681 describe "conversation muting" do
2682 setup do
2683 user = insert(:user)
2684 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2685
2686 [user: user, activity: activity]
2687 end
2688
2689 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2690 id_str = to_string(activity.id)
2691
2692 assert %{"id" => ^id_str, "muted" => true} =
2693 conn
2694 |> assign(:user, user)
2695 |> post("/api/v1/statuses/#{activity.id}/mute")
2696 |> json_response(200)
2697 end
2698
2699 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2700 {:ok, _} = CommonAPI.add_mute(user, activity)
2701
2702 id_str = to_string(activity.id)
2703 user = refresh_record(user)
2704
2705 assert %{"id" => ^id_str, "muted" => false} =
2706 conn
2707 |> assign(:user, user)
2708 |> post("/api/v1/statuses/#{activity.id}/unmute")
2709 |> json_response(200)
2710 end
2711 end
2712
2713 describe "reports" do
2714 setup do
2715 reporter = insert(:user)
2716 target_user = insert(:user)
2717
2718 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2719
2720 [reporter: reporter, target_user: target_user, activity: activity]
2721 end
2722
2723 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2724 assert %{"action_taken" => false, "id" => _} =
2725 conn
2726 |> assign(:user, reporter)
2727 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2728 |> json_response(200)
2729 end
2730
2731 test "submit a report with statuses and comment", %{
2732 conn: conn,
2733 reporter: reporter,
2734 target_user: target_user,
2735 activity: activity
2736 } do
2737 assert %{"action_taken" => false, "id" => _} =
2738 conn
2739 |> assign(:user, reporter)
2740 |> post("/api/v1/reports", %{
2741 "account_id" => target_user.id,
2742 "status_ids" => [activity.id],
2743 "comment" => "bad status!"
2744 })
2745 |> json_response(200)
2746 end
2747
2748 test "account_id is required", %{
2749 conn: conn,
2750 reporter: reporter,
2751 activity: activity
2752 } do
2753 assert %{"error" => "Valid `account_id` required"} =
2754 conn
2755 |> assign(:user, reporter)
2756 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2757 |> json_response(400)
2758 end
2759
2760 test "comment must be up to the size specified in the config", %{
2761 conn: conn,
2762 reporter: reporter,
2763 target_user: target_user
2764 } do
2765 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2766 comment = String.pad_trailing("a", max_size + 1, "a")
2767
2768 error = %{"error" => "Comment must be up to #{max_size} characters"}
2769
2770 assert ^error =
2771 conn
2772 |> assign(:user, reporter)
2773 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2774 |> json_response(400)
2775 end
2776 end
2777
2778 describe "link headers" do
2779 test "preserves parameters in link headers", %{conn: conn} do
2780 user = insert(:user)
2781 other_user = insert(:user)
2782
2783 {:ok, activity1} =
2784 CommonAPI.post(other_user, %{
2785 "status" => "hi @#{user.nickname}",
2786 "visibility" => "public"
2787 })
2788
2789 {:ok, activity2} =
2790 CommonAPI.post(other_user, %{
2791 "status" => "hi @#{user.nickname}",
2792 "visibility" => "public"
2793 })
2794
2795 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2796 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2797
2798 conn =
2799 conn
2800 |> assign(:user, user)
2801 |> get("/api/v1/notifications", %{media_only: true})
2802
2803 assert [link_header] = get_resp_header(conn, "link")
2804 assert link_header =~ ~r/media_only=true/
2805 assert link_header =~ ~r/min_id=#{notification2.id}/
2806 assert link_header =~ ~r/max_id=#{notification1.id}/
2807 end
2808 end
2809
2810 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2811 # Need to set an old-style integer ID to reproduce the problem
2812 # (these are no longer assigned to new accounts but were preserved
2813 # for existing accounts during the migration to flakeIDs)
2814 user_one = insert(:user, %{id: 1212})
2815 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2816
2817 resp_one =
2818 conn
2819 |> get("/api/v1/accounts/#{user_one.id}")
2820
2821 resp_two =
2822 conn
2823 |> get("/api/v1/accounts/#{user_two.nickname}")
2824
2825 resp_three =
2826 conn
2827 |> get("/api/v1/accounts/#{user_two.id}")
2828
2829 acc_one = json_response(resp_one, 200)
2830 acc_two = json_response(resp_two, 200)
2831 acc_three = json_response(resp_three, 200)
2832 refute acc_one == acc_two
2833 assert acc_two == acc_three
2834 end
2835
2836 describe "custom emoji" do
2837 test "with tags", %{conn: conn} do
2838 [emoji | _body] =
2839 conn
2840 |> get("/api/v1/custom_emojis")
2841 |> json_response(200)
2842
2843 assert Map.has_key?(emoji, "shortcode")
2844 assert Map.has_key?(emoji, "static_url")
2845 assert Map.has_key?(emoji, "tags")
2846 assert is_list(emoji["tags"])
2847 assert Map.has_key?(emoji, "url")
2848 assert Map.has_key?(emoji, "visible_in_picker")
2849 end
2850 end
2851
2852 describe "index/2 redirections" do
2853 setup %{conn: conn} do
2854 session_opts = [
2855 store: :cookie,
2856 key: "_test",
2857 signing_salt: "cooldude"
2858 ]
2859
2860 conn =
2861 conn
2862 |> Plug.Session.call(Plug.Session.init(session_opts))
2863 |> fetch_session()
2864
2865 test_path = "/web/statuses/test"
2866 %{conn: conn, path: test_path}
2867 end
2868
2869 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2870 conn = get(conn, path)
2871
2872 assert conn.status == 302
2873 assert redirected_to(conn) == "/web/login"
2874 end
2875
2876 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2877 token = insert(:oauth_token)
2878
2879 conn =
2880 conn
2881 |> assign(:user, token.user)
2882 |> put_session(:oauth_token, token.token)
2883 |> get(path)
2884
2885 assert conn.status == 200
2886 end
2887
2888 test "saves referer path to session", %{conn: conn, path: path} do
2889 conn = get(conn, path)
2890 return_to = Plug.Conn.get_session(conn, :return_to)
2891
2892 assert return_to == path
2893 end
2894
2895 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2896 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2897 auth = insert(:oauth_authorization, app: app)
2898
2899 conn =
2900 conn
2901 |> put_session(:return_to, path)
2902 |> get("/web/login", %{code: auth.token})
2903
2904 assert conn.status == 302
2905 assert redirected_to(conn) == path
2906 end
2907
2908 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2909 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2910 auth = insert(:oauth_authorization, app: app)
2911
2912 conn = get(conn, "/web/login", %{code: auth.token})
2913
2914 assert conn.status == 302
2915 assert redirected_to(conn) == "/web/getting-started"
2916 end
2917 end
2918
2919 describe "scheduled activities" do
2920 test "creates a scheduled activity", %{conn: conn} do
2921 user = insert(:user)
2922 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2923
2924 conn =
2925 conn
2926 |> assign(:user, user)
2927 |> post("/api/v1/statuses", %{
2928 "status" => "scheduled",
2929 "scheduled_at" => scheduled_at
2930 })
2931
2932 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2933 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2934 assert [] == Repo.all(Activity)
2935 end
2936
2937 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2938 user = insert(:user)
2939 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2940
2941 file = %Plug.Upload{
2942 content_type: "image/jpg",
2943 path: Path.absname("test/fixtures/image.jpg"),
2944 filename: "an_image.jpg"
2945 }
2946
2947 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
2948
2949 conn =
2950 conn
2951 |> assign(:user, user)
2952 |> post("/api/v1/statuses", %{
2953 "media_ids" => [to_string(upload.id)],
2954 "status" => "scheduled",
2955 "scheduled_at" => scheduled_at
2956 })
2957
2958 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
2959 assert %{"type" => "image"} = media_attachment
2960 end
2961
2962 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
2963 %{conn: conn} do
2964 user = insert(:user)
2965
2966 scheduled_at =
2967 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
2968
2969 conn =
2970 conn
2971 |> assign(:user, user)
2972 |> post("/api/v1/statuses", %{
2973 "status" => "not scheduled",
2974 "scheduled_at" => scheduled_at
2975 })
2976
2977 assert %{"content" => "not scheduled"} = json_response(conn, 200)
2978 assert [] == Repo.all(ScheduledActivity)
2979 end
2980
2981 test "returns error when daily user limit is exceeded", %{conn: conn} do
2982 user = insert(:user)
2983
2984 today =
2985 NaiveDateTime.utc_now()
2986 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2987 |> NaiveDateTime.to_iso8601()
2988
2989 attrs = %{params: %{}, scheduled_at: today}
2990 {:ok, _} = ScheduledActivity.create(user, attrs)
2991 {:ok, _} = ScheduledActivity.create(user, attrs)
2992
2993 conn =
2994 conn
2995 |> assign(:user, user)
2996 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
2997
2998 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
2999 end
3000
3001 test "returns error when total user limit is exceeded", %{conn: conn} do
3002 user = insert(:user)
3003
3004 today =
3005 NaiveDateTime.utc_now()
3006 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3007 |> NaiveDateTime.to_iso8601()
3008
3009 tomorrow =
3010 NaiveDateTime.utc_now()
3011 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3012 |> NaiveDateTime.to_iso8601()
3013
3014 attrs = %{params: %{}, scheduled_at: today}
3015 {:ok, _} = ScheduledActivity.create(user, attrs)
3016 {:ok, _} = ScheduledActivity.create(user, attrs)
3017 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3018
3019 conn =
3020 conn
3021 |> assign(:user, user)
3022 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3023
3024 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3025 end
3026
3027 test "shows scheduled activities", %{conn: conn} do
3028 user = insert(:user)
3029 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3030 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3031 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3032 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3033
3034 conn =
3035 conn
3036 |> assign(:user, user)
3037
3038 # min_id
3039 conn_res =
3040 conn
3041 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3042
3043 result = json_response(conn_res, 200)
3044 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3045
3046 # since_id
3047 conn_res =
3048 conn
3049 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3050
3051 result = json_response(conn_res, 200)
3052 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3053
3054 # max_id
3055 conn_res =
3056 conn
3057 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3058
3059 result = json_response(conn_res, 200)
3060 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3061 end
3062
3063 test "shows a scheduled activity", %{conn: conn} do
3064 user = insert(:user)
3065 scheduled_activity = insert(:scheduled_activity, user: user)
3066
3067 res_conn =
3068 conn
3069 |> assign(:user, user)
3070 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3071
3072 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3073 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3074
3075 res_conn =
3076 conn
3077 |> assign(:user, user)
3078 |> get("/api/v1/scheduled_statuses/404")
3079
3080 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3081 end
3082
3083 test "updates a scheduled activity", %{conn: conn} do
3084 user = insert(:user)
3085 scheduled_activity = insert(:scheduled_activity, user: user)
3086
3087 new_scheduled_at =
3088 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3089
3090 res_conn =
3091 conn
3092 |> assign(:user, user)
3093 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3094 scheduled_at: new_scheduled_at
3095 })
3096
3097 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3098 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3099
3100 res_conn =
3101 conn
3102 |> assign(:user, user)
3103 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3104
3105 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3106 end
3107
3108 test "deletes a scheduled activity", %{conn: conn} do
3109 user = insert(:user)
3110 scheduled_activity = insert(:scheduled_activity, user: user)
3111
3112 res_conn =
3113 conn
3114 |> assign(:user, user)
3115 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3116
3117 assert %{} = json_response(res_conn, 200)
3118 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3119
3120 res_conn =
3121 conn
3122 |> assign(:user, user)
3123 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3124
3125 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3126 end
3127 end
3128
3129 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3130 user1 = insert(:user)
3131 user2 = insert(:user)
3132 user3 = insert(:user)
3133
3134 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3135
3136 # Reply to status from another user
3137 conn1 =
3138 conn
3139 |> assign(:user, user2)
3140 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3141
3142 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3143
3144 activity = Activity.get_by_id_with_object(id)
3145
3146 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3147 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3148
3149 # Reblog from the third user
3150 conn2 =
3151 conn
3152 |> assign(:user, user3)
3153 |> post("/api/v1/statuses/#{activity.id}/reblog")
3154
3155 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3156 json_response(conn2, 200)
3157
3158 assert to_string(activity.id) == id
3159
3160 # Getting third user status
3161 conn3 =
3162 conn
3163 |> assign(:user, user3)
3164 |> get("api/v1/timelines/home")
3165
3166 [reblogged_activity] = json_response(conn3, 200)
3167
3168 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3169
3170 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3171 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3172 end
3173
3174 describe "create account by app" do
3175 test "Account registration via Application", %{conn: conn} do
3176 conn =
3177 conn
3178 |> post("/api/v1/apps", %{
3179 client_name: "client_name",
3180 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3181 scopes: "read, write, follow"
3182 })
3183
3184 %{
3185 "client_id" => client_id,
3186 "client_secret" => client_secret,
3187 "id" => _,
3188 "name" => "client_name",
3189 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3190 "vapid_key" => _,
3191 "website" => nil
3192 } = json_response(conn, 200)
3193
3194 conn =
3195 conn
3196 |> post("/oauth/token", %{
3197 grant_type: "client_credentials",
3198 client_id: client_id,
3199 client_secret: client_secret
3200 })
3201
3202 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3203 json_response(conn, 200)
3204
3205 assert token
3206 token_from_db = Repo.get_by(Token, token: token)
3207 assert token_from_db
3208 assert refresh
3209 assert scope == "read write follow"
3210
3211 conn =
3212 build_conn()
3213 |> put_req_header("authorization", "Bearer " <> token)
3214 |> post("/api/v1/accounts", %{
3215 username: "lain",
3216 email: "lain@example.org",
3217 password: "PlzDontHackLain",
3218 agreement: true
3219 })
3220
3221 %{
3222 "access_token" => token,
3223 "created_at" => _created_at,
3224 "scope" => _scope,
3225 "token_type" => "Bearer"
3226 } = json_response(conn, 200)
3227
3228 token_from_db = Repo.get_by(Token, token: token)
3229 assert token_from_db
3230 token_from_db = Repo.preload(token_from_db, :user)
3231 assert token_from_db.user
3232
3233 assert token_from_db.user.info.confirmation_pending
3234 end
3235
3236 test "rate limit", %{conn: conn} do
3237 app_token = insert(:oauth_token, user: nil)
3238
3239 conn =
3240 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3241 |> Map.put(:remote_ip, {15, 15, 15, 15})
3242
3243 for i <- 1..5 do
3244 conn =
3245 conn
3246 |> post("/api/v1/accounts", %{
3247 username: "#{i}lain",
3248 email: "#{i}lain@example.org",
3249 password: "PlzDontHackLain",
3250 agreement: true
3251 })
3252
3253 %{
3254 "access_token" => token,
3255 "created_at" => _created_at,
3256 "scope" => _scope,
3257 "token_type" => "Bearer"
3258 } = json_response(conn, 200)
3259
3260 token_from_db = Repo.get_by(Token, token: token)
3261 assert token_from_db
3262 token_from_db = Repo.preload(token_from_db, :user)
3263 assert token_from_db.user
3264
3265 assert token_from_db.user.info.confirmation_pending
3266 end
3267
3268 conn =
3269 conn
3270 |> post("/api/v1/accounts", %{
3271 username: "6lain",
3272 email: "6lain@example.org",
3273 password: "PlzDontHackLain",
3274 agreement: true
3275 })
3276
3277 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3278 end
3279 end
3280
3281 describe "GET /api/v1/polls/:id" do
3282 test "returns poll entity for object id", %{conn: conn} do
3283 user = insert(:user)
3284
3285 {:ok, activity} =
3286 CommonAPI.post(user, %{
3287 "status" => "Pleroma does",
3288 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3289 })
3290
3291 object = Object.normalize(activity)
3292
3293 conn =
3294 conn
3295 |> assign(:user, user)
3296 |> get("/api/v1/polls/#{object.id}")
3297
3298 response = json_response(conn, 200)
3299 id = object.id
3300 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3301 end
3302
3303 test "does not expose polls for private statuses", %{conn: conn} do
3304 user = insert(:user)
3305 other_user = insert(:user)
3306
3307 {:ok, activity} =
3308 CommonAPI.post(user, %{
3309 "status" => "Pleroma does",
3310 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3311 "visibility" => "private"
3312 })
3313
3314 object = Object.normalize(activity)
3315
3316 conn =
3317 conn
3318 |> assign(:user, other_user)
3319 |> get("/api/v1/polls/#{object.id}")
3320
3321 assert json_response(conn, 404)
3322 end
3323 end
3324
3325 describe "POST /api/v1/polls/:id/votes" do
3326 test "votes are added to the poll", %{conn: conn} do
3327 user = insert(:user)
3328 other_user = insert(:user)
3329
3330 {:ok, activity} =
3331 CommonAPI.post(user, %{
3332 "status" => "A very delicious sandwich",
3333 "poll" => %{
3334 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3335 "expires_in" => 20,
3336 "multiple" => true
3337 }
3338 })
3339
3340 object = Object.normalize(activity)
3341
3342 conn =
3343 conn
3344 |> assign(:user, other_user)
3345 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3346
3347 assert json_response(conn, 200)
3348 object = Object.get_by_id(object.id)
3349
3350 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3351 total_items == 1
3352 end)
3353 end
3354
3355 test "author can't vote", %{conn: conn} do
3356 user = insert(:user)
3357
3358 {:ok, activity} =
3359 CommonAPI.post(user, %{
3360 "status" => "Am I cute?",
3361 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3362 })
3363
3364 object = Object.normalize(activity)
3365
3366 assert conn
3367 |> assign(:user, user)
3368 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3369 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3370
3371 object = Object.get_by_id(object.id)
3372
3373 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3374 end
3375
3376 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3377 user = insert(:user)
3378 other_user = insert(:user)
3379
3380 {:ok, activity} =
3381 CommonAPI.post(user, %{
3382 "status" => "The glass is",
3383 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3384 })
3385
3386 object = Object.normalize(activity)
3387
3388 assert conn
3389 |> assign(:user, other_user)
3390 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3391 |> json_response(422) == %{"error" => "Too many choices"}
3392
3393 object = Object.get_by_id(object.id)
3394
3395 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3396 total_items == 1
3397 end)
3398 end
3399 end
3400 end