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