701f8ee4bd6f8e148cb239006c99cda8eb7a2369
[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 describe "reports" do
2954 setup do
2955 reporter = insert(:user)
2956 target_user = insert(:user)
2957
2958 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2959
2960 [reporter: reporter, target_user: target_user, activity: activity]
2961 end
2962
2963 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2964 assert %{"action_taken" => false, "id" => _} =
2965 conn
2966 |> assign(:user, reporter)
2967 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2968 |> json_response(200)
2969 end
2970
2971 test "submit a report with statuses and comment", %{
2972 conn: conn,
2973 reporter: reporter,
2974 target_user: target_user,
2975 activity: activity
2976 } do
2977 assert %{"action_taken" => false, "id" => _} =
2978 conn
2979 |> assign(:user, reporter)
2980 |> post("/api/v1/reports", %{
2981 "account_id" => target_user.id,
2982 "status_ids" => [activity.id],
2983 "comment" => "bad status!"
2984 })
2985 |> json_response(200)
2986 end
2987
2988 test "account_id is required", %{
2989 conn: conn,
2990 reporter: reporter,
2991 activity: activity
2992 } do
2993 assert %{"error" => "Valid `account_id` required"} =
2994 conn
2995 |> assign(:user, reporter)
2996 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2997 |> json_response(400)
2998 end
2999
3000 test "comment must be up to the size specified in the config", %{
3001 conn: conn,
3002 reporter: reporter,
3003 target_user: target_user
3004 } do
3005 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
3006 comment = String.pad_trailing("a", max_size + 1, "a")
3007
3008 error = %{"error" => "Comment must be up to #{max_size} characters"}
3009
3010 assert ^error =
3011 conn
3012 |> assign(:user, reporter)
3013 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3014 |> json_response(400)
3015 end
3016 end
3017
3018 describe "link headers" do
3019 test "preserves parameters in link headers", %{conn: conn} do
3020 user = insert(:user)
3021 other_user = insert(:user)
3022
3023 {:ok, activity1} =
3024 CommonAPI.post(other_user, %{
3025 "status" => "hi @#{user.nickname}",
3026 "visibility" => "public"
3027 })
3028
3029 {:ok, activity2} =
3030 CommonAPI.post(other_user, %{
3031 "status" => "hi @#{user.nickname}",
3032 "visibility" => "public"
3033 })
3034
3035 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3036 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3037
3038 conn =
3039 conn
3040 |> assign(:user, user)
3041 |> get("/api/v1/notifications", %{media_only: true})
3042
3043 assert [link_header] = get_resp_header(conn, "link")
3044 assert link_header =~ ~r/media_only=true/
3045 assert link_header =~ ~r/min_id=#{notification2.id}/
3046 assert link_header =~ ~r/max_id=#{notification1.id}/
3047 end
3048 end
3049
3050 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3051 # Need to set an old-style integer ID to reproduce the problem
3052 # (these are no longer assigned to new accounts but were preserved
3053 # for existing accounts during the migration to flakeIDs)
3054 user_one = insert(:user, %{id: 1212})
3055 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3056
3057 resp_one =
3058 conn
3059 |> get("/api/v1/accounts/#{user_one.id}")
3060
3061 resp_two =
3062 conn
3063 |> get("/api/v1/accounts/#{user_two.nickname}")
3064
3065 resp_three =
3066 conn
3067 |> get("/api/v1/accounts/#{user_two.id}")
3068
3069 acc_one = json_response(resp_one, 200)
3070 acc_two = json_response(resp_two, 200)
3071 acc_three = json_response(resp_three, 200)
3072 refute acc_one == acc_two
3073 assert acc_two == acc_three
3074 end
3075
3076 describe "custom emoji" do
3077 test "with tags", %{conn: conn} do
3078 [emoji | _body] =
3079 conn
3080 |> get("/api/v1/custom_emojis")
3081 |> json_response(200)
3082
3083 assert Map.has_key?(emoji, "shortcode")
3084 assert Map.has_key?(emoji, "static_url")
3085 assert Map.has_key?(emoji, "tags")
3086 assert is_list(emoji["tags"])
3087 assert Map.has_key?(emoji, "url")
3088 assert Map.has_key?(emoji, "visible_in_picker")
3089 end
3090 end
3091
3092 describe "index/2 redirections" do
3093 setup %{conn: conn} do
3094 session_opts = [
3095 store: :cookie,
3096 key: "_test",
3097 signing_salt: "cooldude"
3098 ]
3099
3100 conn =
3101 conn
3102 |> Plug.Session.call(Plug.Session.init(session_opts))
3103 |> fetch_session()
3104
3105 test_path = "/web/statuses/test"
3106 %{conn: conn, path: test_path}
3107 end
3108
3109 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3110 conn = get(conn, path)
3111
3112 assert conn.status == 302
3113 assert redirected_to(conn) == "/web/login"
3114 end
3115
3116 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3117 token = insert(:oauth_token)
3118
3119 conn =
3120 conn
3121 |> assign(:user, token.user)
3122 |> put_session(:oauth_token, token.token)
3123 |> get(path)
3124
3125 assert conn.status == 200
3126 end
3127
3128 test "saves referer path to session", %{conn: conn, path: path} do
3129 conn = get(conn, path)
3130 return_to = Plug.Conn.get_session(conn, :return_to)
3131
3132 assert return_to == path
3133 end
3134
3135 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3136 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3137 auth = insert(:oauth_authorization, app: app)
3138
3139 conn =
3140 conn
3141 |> put_session(:return_to, path)
3142 |> get("/web/login", %{code: auth.token})
3143
3144 assert conn.status == 302
3145 assert redirected_to(conn) == path
3146 end
3147
3148 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3149 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3150 auth = insert(:oauth_authorization, app: app)
3151
3152 conn = get(conn, "/web/login", %{code: auth.token})
3153
3154 assert conn.status == 302
3155 assert redirected_to(conn) == "/web/getting-started"
3156 end
3157 end
3158
3159 describe "scheduled activities" do
3160 test "creates a scheduled activity", %{conn: conn} do
3161 user = insert(:user)
3162 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3163
3164 conn =
3165 conn
3166 |> assign(:user, user)
3167 |> post("/api/v1/statuses", %{
3168 "status" => "scheduled",
3169 "scheduled_at" => scheduled_at
3170 })
3171
3172 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3173 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3174 assert [] == Repo.all(Activity)
3175 end
3176
3177 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3178 user = insert(:user)
3179 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3180
3181 file = %Plug.Upload{
3182 content_type: "image/jpg",
3183 path: Path.absname("test/fixtures/image.jpg"),
3184 filename: "an_image.jpg"
3185 }
3186
3187 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3188
3189 conn =
3190 conn
3191 |> assign(:user, user)
3192 |> post("/api/v1/statuses", %{
3193 "media_ids" => [to_string(upload.id)],
3194 "status" => "scheduled",
3195 "scheduled_at" => scheduled_at
3196 })
3197
3198 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3199 assert %{"type" => "image"} = media_attachment
3200 end
3201
3202 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3203 %{conn: conn} do
3204 user = insert(:user)
3205
3206 scheduled_at =
3207 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3208
3209 conn =
3210 conn
3211 |> assign(:user, user)
3212 |> post("/api/v1/statuses", %{
3213 "status" => "not scheduled",
3214 "scheduled_at" => scheduled_at
3215 })
3216
3217 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3218 assert [] == Repo.all(ScheduledActivity)
3219 end
3220
3221 test "returns error when daily user limit is exceeded", %{conn: conn} do
3222 user = insert(:user)
3223
3224 today =
3225 NaiveDateTime.utc_now()
3226 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3227 |> NaiveDateTime.to_iso8601()
3228
3229 attrs = %{params: %{}, scheduled_at: today}
3230 {:ok, _} = ScheduledActivity.create(user, attrs)
3231 {:ok, _} = ScheduledActivity.create(user, attrs)
3232
3233 conn =
3234 conn
3235 |> assign(:user, user)
3236 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3237
3238 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3239 end
3240
3241 test "returns error when total user limit is exceeded", %{conn: conn} do
3242 user = insert(:user)
3243
3244 today =
3245 NaiveDateTime.utc_now()
3246 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3247 |> NaiveDateTime.to_iso8601()
3248
3249 tomorrow =
3250 NaiveDateTime.utc_now()
3251 |> NaiveDateTime.add(:timer.hours(36), :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 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3258
3259 conn =
3260 conn
3261 |> assign(:user, user)
3262 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3263
3264 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3265 end
3266
3267 test "shows scheduled activities", %{conn: conn} do
3268 user = insert(:user)
3269 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3270 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3271 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3272 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3273
3274 conn =
3275 conn
3276 |> assign(:user, user)
3277
3278 # min_id
3279 conn_res =
3280 conn
3281 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3282
3283 result = json_response(conn_res, 200)
3284 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3285
3286 # since_id
3287 conn_res =
3288 conn
3289 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3290
3291 result = json_response(conn_res, 200)
3292 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3293
3294 # max_id
3295 conn_res =
3296 conn
3297 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3298
3299 result = json_response(conn_res, 200)
3300 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3301 end
3302
3303 test "shows a scheduled activity", %{conn: conn} do
3304 user = insert(:user)
3305 scheduled_activity = insert(:scheduled_activity, user: user)
3306
3307 res_conn =
3308 conn
3309 |> assign(:user, user)
3310 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3311
3312 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3313 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3314
3315 res_conn =
3316 conn
3317 |> assign(:user, user)
3318 |> get("/api/v1/scheduled_statuses/404")
3319
3320 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3321 end
3322
3323 test "updates a scheduled activity", %{conn: conn} do
3324 user = insert(:user)
3325 scheduled_activity = insert(:scheduled_activity, user: user)
3326
3327 new_scheduled_at =
3328 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3329
3330 res_conn =
3331 conn
3332 |> assign(:user, user)
3333 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3334 scheduled_at: new_scheduled_at
3335 })
3336
3337 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3338 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3339
3340 res_conn =
3341 conn
3342 |> assign(:user, user)
3343 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3344
3345 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3346 end
3347
3348 test "deletes a scheduled activity", %{conn: conn} do
3349 user = insert(:user)
3350 scheduled_activity = insert(:scheduled_activity, user: user)
3351
3352 res_conn =
3353 conn
3354 |> assign(:user, user)
3355 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3356
3357 assert %{} = json_response(res_conn, 200)
3358 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3359
3360 res_conn =
3361 conn
3362 |> assign(:user, user)
3363 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3364
3365 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3366 end
3367 end
3368
3369 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3370 user1 = insert(:user)
3371 user2 = insert(:user)
3372 user3 = insert(:user)
3373
3374 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3375
3376 # Reply to status from another user
3377 conn1 =
3378 conn
3379 |> assign(:user, user2)
3380 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3381
3382 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3383
3384 activity = Activity.get_by_id_with_object(id)
3385
3386 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3387 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3388
3389 # Reblog from the third user
3390 conn2 =
3391 conn
3392 |> assign(:user, user3)
3393 |> post("/api/v1/statuses/#{activity.id}/reblog")
3394
3395 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3396 json_response(conn2, 200)
3397
3398 assert to_string(activity.id) == id
3399
3400 # Getting third user status
3401 conn3 =
3402 conn
3403 |> assign(:user, user3)
3404 |> get("api/v1/timelines/home")
3405
3406 [reblogged_activity] = json_response(conn3, 200)
3407
3408 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3409
3410 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3411 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3412 end
3413
3414 describe "create account by app" do
3415 setup do
3416 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3417 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3418 interval = Pleroma.Config.get([:app_account_creation, :interval])
3419
3420 Pleroma.Config.put([:app_account_creation, :enabled], true)
3421 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3422 Pleroma.Config.put([:app_account_creation, :interval], 1)
3423
3424 on_exit(fn ->
3425 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3426 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3427 Pleroma.Config.put([:app_account_creation, :interval], interval)
3428 end)
3429
3430 :ok
3431 end
3432
3433 test "Account registration via Application", %{conn: conn} do
3434 conn =
3435 conn
3436 |> post("/api/v1/apps", %{
3437 client_name: "client_name",
3438 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3439 scopes: "read, write, follow"
3440 })
3441
3442 %{
3443 "client_id" => client_id,
3444 "client_secret" => client_secret,
3445 "id" => _,
3446 "name" => "client_name",
3447 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3448 "vapid_key" => _,
3449 "website" => nil
3450 } = json_response(conn, 200)
3451
3452 conn =
3453 conn
3454 |> post("/oauth/token", %{
3455 grant_type: "client_credentials",
3456 client_id: client_id,
3457 client_secret: client_secret
3458 })
3459
3460 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3461 json_response(conn, 200)
3462
3463 assert token
3464 token_from_db = Repo.get_by(Token, token: token)
3465 assert token_from_db
3466 assert refresh
3467 assert scope == "read write follow"
3468
3469 conn =
3470 build_conn()
3471 |> put_req_header("authorization", "Bearer " <> token)
3472 |> post("/api/v1/accounts", %{
3473 username: "lain",
3474 email: "lain@example.org",
3475 password: "PlzDontHackLain",
3476 agreement: true
3477 })
3478
3479 %{
3480 "access_token" => token,
3481 "created_at" => _created_at,
3482 "scope" => _scope,
3483 "token_type" => "Bearer"
3484 } = json_response(conn, 200)
3485
3486 token_from_db = Repo.get_by(Token, token: token)
3487 assert token_from_db
3488 token_from_db = Repo.preload(token_from_db, :user)
3489 assert token_from_db.user
3490
3491 assert token_from_db.user.info.confirmation_pending
3492 end
3493
3494 test "rate limit", %{conn: conn} do
3495 app_token = insert(:oauth_token, user: nil)
3496
3497 conn =
3498 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3499 |> Map.put(:remote_ip, {15, 15, 15, 15})
3500
3501 for i <- 1..5 do
3502 conn =
3503 conn
3504 |> post("/api/v1/accounts", %{
3505 username: "#{i}lain",
3506 email: "#{i}lain@example.org",
3507 password: "PlzDontHackLain",
3508 agreement: true
3509 })
3510
3511 %{
3512 "access_token" => token,
3513 "created_at" => _created_at,
3514 "scope" => _scope,
3515 "token_type" => "Bearer"
3516 } = json_response(conn, 200)
3517
3518 token_from_db = Repo.get_by(Token, token: token)
3519 assert token_from_db
3520 token_from_db = Repo.preload(token_from_db, :user)
3521 assert token_from_db.user
3522
3523 assert token_from_db.user.info.confirmation_pending
3524 end
3525
3526 conn =
3527 conn
3528 |> post("/api/v1/accounts", %{
3529 username: "6lain",
3530 email: "6lain@example.org",
3531 password: "PlzDontHackLain",
3532 agreement: true
3533 })
3534
3535 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}
3536 end
3537 end
3538
3539 describe "GET /api/v1/polls/:id" do
3540 test "returns poll entity for object id", %{conn: conn} do
3541 user = insert(:user)
3542
3543 {:ok, activity} =
3544 CommonAPI.post(user, %{
3545 "status" => "Pleroma does",
3546 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3547 })
3548
3549 object = Object.normalize(activity)
3550
3551 conn =
3552 conn
3553 |> assign(:user, user)
3554 |> get("/api/v1/polls/#{object.id}")
3555
3556 response = json_response(conn, 200)
3557 id = object.id
3558 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3559 end
3560
3561 test "does not expose polls for private statuses", %{conn: conn} do
3562 user = insert(:user)
3563 other_user = insert(:user)
3564
3565 {:ok, activity} =
3566 CommonAPI.post(user, %{
3567 "status" => "Pleroma does",
3568 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3569 "visibility" => "private"
3570 })
3571
3572 object = Object.normalize(activity)
3573
3574 conn =
3575 conn
3576 |> assign(:user, other_user)
3577 |> get("/api/v1/polls/#{object.id}")
3578
3579 assert json_response(conn, 404)
3580 end
3581 end
3582
3583 describe "POST /api/v1/polls/:id/votes" do
3584 test "votes are added to the poll", %{conn: conn} do
3585 user = insert(:user)
3586 other_user = insert(:user)
3587
3588 {:ok, activity} =
3589 CommonAPI.post(user, %{
3590 "status" => "A very delicious sandwich",
3591 "poll" => %{
3592 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3593 "expires_in" => 20,
3594 "multiple" => true
3595 }
3596 })
3597
3598 object = Object.normalize(activity)
3599
3600 conn =
3601 conn
3602 |> assign(:user, other_user)
3603 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3604
3605 assert json_response(conn, 200)
3606 object = Object.get_by_id(object.id)
3607
3608 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3609 total_items == 1
3610 end)
3611 end
3612
3613 test "author can't vote", %{conn: conn} do
3614 user = insert(:user)
3615
3616 {:ok, activity} =
3617 CommonAPI.post(user, %{
3618 "status" => "Am I cute?",
3619 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3620 })
3621
3622 object = Object.normalize(activity)
3623
3624 assert conn
3625 |> assign(:user, user)
3626 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3627 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3628
3629 object = Object.get_by_id(object.id)
3630
3631 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3632 end
3633
3634 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3635 user = insert(:user)
3636 other_user = insert(:user)
3637
3638 {:ok, activity} =
3639 CommonAPI.post(user, %{
3640 "status" => "The glass is",
3641 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3642 })
3643
3644 object = Object.normalize(activity)
3645
3646 assert conn
3647 |> assign(:user, other_user)
3648 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3649 |> json_response(422) == %{"error" => "Too many choices"}
3650
3651 object = Object.get_by_id(object.id)
3652
3653 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3654 total_items == 1
3655 end)
3656 end
3657 end
3658 end