Add poll votes
[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 "hashtag timeline", %{conn: conn} do
1556 following = insert(:user)
1557
1558 capture_log(fn ->
1559 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1560
1561 {:ok, [_activity]} =
1562 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1563
1564 nconn =
1565 conn
1566 |> get("/api/v1/timelines/tag/2hu")
1567
1568 assert [%{"id" => id}] = json_response(nconn, 200)
1569
1570 assert id == to_string(activity.id)
1571
1572 # works for different capitalization too
1573 nconn =
1574 conn
1575 |> get("/api/v1/timelines/tag/2HU")
1576
1577 assert [%{"id" => id}] = json_response(nconn, 200)
1578
1579 assert id == to_string(activity.id)
1580 end)
1581 end
1582
1583 test "multi-hashtag timeline", %{conn: conn} do
1584 user = insert(:user)
1585
1586 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1587 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1588 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1589
1590 any_test =
1591 conn
1592 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1593
1594 [status_none, status_test1, status_test] = json_response(any_test, 200)
1595
1596 assert to_string(activity_test.id) == status_test["id"]
1597 assert to_string(activity_test1.id) == status_test1["id"]
1598 assert to_string(activity_none.id) == status_none["id"]
1599
1600 restricted_test =
1601 conn
1602 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1603
1604 assert [status_test1] == json_response(restricted_test, 200)
1605
1606 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1607
1608 assert [status_none] == json_response(all_test, 200)
1609 end
1610
1611 test "getting followers", %{conn: conn} do
1612 user = insert(:user)
1613 other_user = insert(:user)
1614 {:ok, user} = User.follow(user, other_user)
1615
1616 conn =
1617 conn
1618 |> get("/api/v1/accounts/#{other_user.id}/followers")
1619
1620 assert [%{"id" => id}] = json_response(conn, 200)
1621 assert id == to_string(user.id)
1622 end
1623
1624 test "getting followers, hide_followers", %{conn: conn} do
1625 user = insert(:user)
1626 other_user = insert(:user, %{info: %{hide_followers: true}})
1627 {:ok, _user} = User.follow(user, other_user)
1628
1629 conn =
1630 conn
1631 |> get("/api/v1/accounts/#{other_user.id}/followers")
1632
1633 assert [] == json_response(conn, 200)
1634 end
1635
1636 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1637 user = insert(:user)
1638 other_user = insert(:user, %{info: %{hide_followers: true}})
1639 {:ok, _user} = User.follow(user, other_user)
1640
1641 conn =
1642 conn
1643 |> assign(:user, other_user)
1644 |> get("/api/v1/accounts/#{other_user.id}/followers")
1645
1646 refute [] == json_response(conn, 200)
1647 end
1648
1649 test "getting followers, pagination", %{conn: conn} do
1650 user = insert(:user)
1651 follower1 = insert(:user)
1652 follower2 = insert(:user)
1653 follower3 = insert(:user)
1654 {:ok, _} = User.follow(follower1, user)
1655 {:ok, _} = User.follow(follower2, user)
1656 {:ok, _} = User.follow(follower3, user)
1657
1658 conn =
1659 conn
1660 |> assign(:user, user)
1661
1662 res_conn =
1663 conn
1664 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1665
1666 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1667 assert id3 == follower3.id
1668 assert id2 == follower2.id
1669
1670 res_conn =
1671 conn
1672 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1673
1674 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1675 assert id2 == follower2.id
1676 assert id1 == follower1.id
1677
1678 res_conn =
1679 conn
1680 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1681
1682 assert [%{"id" => id2}] = json_response(res_conn, 200)
1683 assert id2 == follower2.id
1684
1685 assert [link_header] = get_resp_header(res_conn, "link")
1686 assert link_header =~ ~r/min_id=#{follower2.id}/
1687 assert link_header =~ ~r/max_id=#{follower2.id}/
1688 end
1689
1690 test "getting following", %{conn: conn} do
1691 user = insert(:user)
1692 other_user = insert(:user)
1693 {:ok, user} = User.follow(user, other_user)
1694
1695 conn =
1696 conn
1697 |> get("/api/v1/accounts/#{user.id}/following")
1698
1699 assert [%{"id" => id}] = json_response(conn, 200)
1700 assert id == to_string(other_user.id)
1701 end
1702
1703 test "getting following, hide_follows", %{conn: conn} do
1704 user = insert(:user, %{info: %{hide_follows: true}})
1705 other_user = insert(:user)
1706 {:ok, user} = User.follow(user, other_user)
1707
1708 conn =
1709 conn
1710 |> get("/api/v1/accounts/#{user.id}/following")
1711
1712 assert [] == json_response(conn, 200)
1713 end
1714
1715 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1716 user = insert(:user, %{info: %{hide_follows: true}})
1717 other_user = insert(:user)
1718 {:ok, user} = User.follow(user, other_user)
1719
1720 conn =
1721 conn
1722 |> assign(:user, user)
1723 |> get("/api/v1/accounts/#{user.id}/following")
1724
1725 refute [] == json_response(conn, 200)
1726 end
1727
1728 test "getting following, pagination", %{conn: conn} do
1729 user = insert(:user)
1730 following1 = insert(:user)
1731 following2 = insert(:user)
1732 following3 = insert(:user)
1733 {:ok, _} = User.follow(user, following1)
1734 {:ok, _} = User.follow(user, following2)
1735 {:ok, _} = User.follow(user, following3)
1736
1737 conn =
1738 conn
1739 |> assign(:user, user)
1740
1741 res_conn =
1742 conn
1743 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1744
1745 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1746 assert id3 == following3.id
1747 assert id2 == following2.id
1748
1749 res_conn =
1750 conn
1751 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1752
1753 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1754 assert id2 == following2.id
1755 assert id1 == following1.id
1756
1757 res_conn =
1758 conn
1759 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1760
1761 assert [%{"id" => id2}] = json_response(res_conn, 200)
1762 assert id2 == following2.id
1763
1764 assert [link_header] = get_resp_header(res_conn, "link")
1765 assert link_header =~ ~r/min_id=#{following2.id}/
1766 assert link_header =~ ~r/max_id=#{following2.id}/
1767 end
1768
1769 test "following / unfollowing a user", %{conn: conn} do
1770 user = insert(:user)
1771 other_user = insert(:user)
1772
1773 conn =
1774 conn
1775 |> assign(:user, user)
1776 |> post("/api/v1/accounts/#{other_user.id}/follow")
1777
1778 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1779
1780 user = User.get_cached_by_id(user.id)
1781
1782 conn =
1783 build_conn()
1784 |> assign(:user, user)
1785 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1786
1787 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1788
1789 user = User.get_cached_by_id(user.id)
1790
1791 conn =
1792 build_conn()
1793 |> assign(:user, user)
1794 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1795
1796 assert %{"id" => id} = json_response(conn, 200)
1797 assert id == to_string(other_user.id)
1798 end
1799
1800 test "following without reblogs" do
1801 follower = insert(:user)
1802 followed = insert(:user)
1803 other_user = insert(:user)
1804
1805 conn =
1806 build_conn()
1807 |> assign(:user, follower)
1808 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1809
1810 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1811
1812 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1813 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1814
1815 conn =
1816 build_conn()
1817 |> assign(:user, User.get_cached_by_id(follower.id))
1818 |> get("/api/v1/timelines/home")
1819
1820 assert [] == json_response(conn, 200)
1821
1822 conn =
1823 build_conn()
1824 |> assign(:user, follower)
1825 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1826
1827 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1828
1829 conn =
1830 build_conn()
1831 |> assign(:user, User.get_cached_by_id(follower.id))
1832 |> get("/api/v1/timelines/home")
1833
1834 expected_activity_id = reblog.id
1835 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1836 end
1837
1838 test "following / unfollowing errors" do
1839 user = insert(:user)
1840
1841 conn =
1842 build_conn()
1843 |> assign(:user, user)
1844
1845 # self follow
1846 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1847 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1848
1849 # self unfollow
1850 user = User.get_cached_by_id(user.id)
1851 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1852 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1853
1854 # self follow via uri
1855 user = User.get_cached_by_id(user.id)
1856 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1857 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1858
1859 # follow non existing user
1860 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1861 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1862
1863 # follow non existing user via uri
1864 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1865 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1866
1867 # unfollow non existing user
1868 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1869 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1870 end
1871
1872 test "muting / unmuting a user", %{conn: conn} do
1873 user = insert(:user)
1874 other_user = insert(:user)
1875
1876 conn =
1877 conn
1878 |> assign(:user, user)
1879 |> post("/api/v1/accounts/#{other_user.id}/mute")
1880
1881 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1882
1883 user = User.get_cached_by_id(user.id)
1884
1885 conn =
1886 build_conn()
1887 |> assign(:user, user)
1888 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1889
1890 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1891 end
1892
1893 test "subscribing / unsubscribing to a user", %{conn: conn} do
1894 user = insert(:user)
1895 subscription_target = insert(:user)
1896
1897 conn =
1898 conn
1899 |> assign(:user, user)
1900 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1901
1902 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1903
1904 conn =
1905 build_conn()
1906 |> assign(:user, user)
1907 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1908
1909 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1910 end
1911
1912 test "getting a list of mutes", %{conn: conn} do
1913 user = insert(:user)
1914 other_user = insert(:user)
1915
1916 {:ok, user} = User.mute(user, other_user)
1917
1918 conn =
1919 conn
1920 |> assign(:user, user)
1921 |> get("/api/v1/mutes")
1922
1923 other_user_id = to_string(other_user.id)
1924 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1925 end
1926
1927 test "blocking / unblocking a user", %{conn: conn} do
1928 user = insert(:user)
1929 other_user = insert(:user)
1930
1931 conn =
1932 conn
1933 |> assign(:user, user)
1934 |> post("/api/v1/accounts/#{other_user.id}/block")
1935
1936 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1937
1938 user = User.get_cached_by_id(user.id)
1939
1940 conn =
1941 build_conn()
1942 |> assign(:user, user)
1943 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1944
1945 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1946 end
1947
1948 test "getting a list of blocks", %{conn: conn} do
1949 user = insert(:user)
1950 other_user = insert(:user)
1951
1952 {:ok, user} = User.block(user, other_user)
1953
1954 conn =
1955 conn
1956 |> assign(:user, user)
1957 |> get("/api/v1/blocks")
1958
1959 other_user_id = to_string(other_user.id)
1960 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1961 end
1962
1963 test "blocking / unblocking a domain", %{conn: conn} do
1964 user = insert(:user)
1965 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1966
1967 conn =
1968 conn
1969 |> assign(:user, user)
1970 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1971
1972 assert %{} = json_response(conn, 200)
1973 user = User.get_cached_by_ap_id(user.ap_id)
1974 assert User.blocks?(user, other_user)
1975
1976 conn =
1977 build_conn()
1978 |> assign(:user, user)
1979 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1980
1981 assert %{} = json_response(conn, 200)
1982 user = User.get_cached_by_ap_id(user.ap_id)
1983 refute User.blocks?(user, other_user)
1984 end
1985
1986 test "getting a list of domain blocks", %{conn: conn} do
1987 user = insert(:user)
1988
1989 {:ok, user} = User.block_domain(user, "bad.site")
1990 {:ok, user} = User.block_domain(user, "even.worse.site")
1991
1992 conn =
1993 conn
1994 |> assign(:user, user)
1995 |> get("/api/v1/domain_blocks")
1996
1997 domain_blocks = json_response(conn, 200)
1998
1999 assert "bad.site" in domain_blocks
2000 assert "even.worse.site" in domain_blocks
2001 end
2002
2003 test "unimplemented follow_requests, blocks, domain blocks" do
2004 user = insert(:user)
2005
2006 ["blocks", "domain_blocks", "follow_requests"]
2007 |> Enum.each(fn endpoint ->
2008 conn =
2009 build_conn()
2010 |> assign(:user, user)
2011 |> get("/api/v1/#{endpoint}")
2012
2013 assert [] = json_response(conn, 200)
2014 end)
2015 end
2016
2017 test "account search", %{conn: conn} do
2018 user = insert(:user)
2019 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2020 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2021
2022 results =
2023 conn
2024 |> assign(:user, user)
2025 |> get("/api/v1/accounts/search", %{"q" => "shp"})
2026 |> json_response(200)
2027
2028 result_ids = for result <- results, do: result["acct"]
2029
2030 assert user_two.nickname in result_ids
2031 assert user_three.nickname in result_ids
2032
2033 results =
2034 conn
2035 |> assign(:user, user)
2036 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
2037 |> json_response(200)
2038
2039 result_ids = for result <- results, do: result["acct"]
2040
2041 assert user_three.nickname in result_ids
2042 end
2043
2044 test "search", %{conn: conn} do
2045 user = insert(:user)
2046 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
2047 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
2048
2049 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
2050
2051 {:ok, _activity} =
2052 CommonAPI.post(user, %{
2053 "status" => "This is about 2hu, but private",
2054 "visibility" => "private"
2055 })
2056
2057 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
2058
2059 conn =
2060 conn
2061 |> get("/api/v1/search", %{"q" => "2hu"})
2062
2063 assert results = json_response(conn, 200)
2064
2065 [account | _] = results["accounts"]
2066 assert account["id"] == to_string(user_three.id)
2067
2068 assert results["hashtags"] == []
2069
2070 [status] = results["statuses"]
2071 assert status["id"] == to_string(activity.id)
2072 end
2073
2074 test "search fetches remote statuses", %{conn: conn} do
2075 capture_log(fn ->
2076 conn =
2077 conn
2078 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
2079
2080 assert results = json_response(conn, 200)
2081
2082 [status] = results["statuses"]
2083 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
2084 end)
2085 end
2086
2087 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
2088 {:ok, activity} =
2089 CommonAPI.post(insert(:user), %{
2090 "status" => "This is about 2hu, but private",
2091 "visibility" => "private"
2092 })
2093
2094 capture_log(fn ->
2095 conn =
2096 conn
2097 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
2098
2099 assert results = json_response(conn, 200)
2100
2101 [] = results["statuses"]
2102 end)
2103 end
2104
2105 test "search fetches remote accounts", %{conn: conn} do
2106 conn =
2107 conn
2108 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
2109
2110 assert results = json_response(conn, 200)
2111 [account] = results["accounts"]
2112 assert account["acct"] == "shp@social.heldscal.la"
2113 end
2114
2115 test "returns the favorites of a user", %{conn: conn} do
2116 user = insert(:user)
2117 other_user = insert(:user)
2118
2119 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2120 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2121
2122 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2123
2124 first_conn =
2125 conn
2126 |> assign(:user, user)
2127 |> get("/api/v1/favourites")
2128
2129 assert [status] = json_response(first_conn, 200)
2130 assert status["id"] == to_string(activity.id)
2131
2132 assert [{"link", _link_header}] =
2133 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2134
2135 # Honours query params
2136 {:ok, second_activity} =
2137 CommonAPI.post(other_user, %{
2138 "status" =>
2139 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2140 })
2141
2142 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2143
2144 last_like = status["id"]
2145
2146 second_conn =
2147 conn
2148 |> assign(:user, user)
2149 |> get("/api/v1/favourites?since_id=#{last_like}")
2150
2151 assert [second_status] = json_response(second_conn, 200)
2152 assert second_status["id"] == to_string(second_activity.id)
2153
2154 third_conn =
2155 conn
2156 |> assign(:user, user)
2157 |> get("/api/v1/favourites?limit=0")
2158
2159 assert [] = json_response(third_conn, 200)
2160 end
2161
2162 describe "getting favorites timeline of specified user" do
2163 setup do
2164 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2165 [current_user: current_user, user: user]
2166 end
2167
2168 test "returns list of statuses favorited by specified user", %{
2169 conn: conn,
2170 current_user: current_user,
2171 user: user
2172 } do
2173 [activity | _] = insert_pair(:note_activity)
2174 CommonAPI.favorite(activity.id, user)
2175
2176 response =
2177 conn
2178 |> assign(:user, current_user)
2179 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2180 |> json_response(:ok)
2181
2182 [like] = response
2183
2184 assert length(response) == 1
2185 assert like["id"] == activity.id
2186 end
2187
2188 test "returns favorites for specified user_id when user is not logged in", %{
2189 conn: conn,
2190 user: user
2191 } do
2192 activity = insert(:note_activity)
2193 CommonAPI.favorite(activity.id, user)
2194
2195 response =
2196 conn
2197 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2198 |> json_response(:ok)
2199
2200 assert length(response) == 1
2201 end
2202
2203 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2204 conn: conn,
2205 current_user: current_user,
2206 user: user
2207 } do
2208 {:ok, direct} =
2209 CommonAPI.post(current_user, %{
2210 "status" => "Hi @#{user.nickname}!",
2211 "visibility" => "direct"
2212 })
2213
2214 CommonAPI.favorite(direct.id, user)
2215
2216 response =
2217 conn
2218 |> assign(:user, current_user)
2219 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2220 |> json_response(:ok)
2221
2222 assert length(response) == 1
2223
2224 anonymous_response =
2225 conn
2226 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2227 |> json_response(:ok)
2228
2229 assert Enum.empty?(anonymous_response)
2230 end
2231
2232 test "does not return others' favorited DM when user is not one of recipients", %{
2233 conn: conn,
2234 current_user: current_user,
2235 user: user
2236 } do
2237 user_two = insert(:user)
2238
2239 {:ok, direct} =
2240 CommonAPI.post(user_two, %{
2241 "status" => "Hi @#{user.nickname}!",
2242 "visibility" => "direct"
2243 })
2244
2245 CommonAPI.favorite(direct.id, user)
2246
2247 response =
2248 conn
2249 |> assign(:user, current_user)
2250 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2251 |> json_response(:ok)
2252
2253 assert Enum.empty?(response)
2254 end
2255
2256 test "paginates favorites using since_id and max_id", %{
2257 conn: conn,
2258 current_user: current_user,
2259 user: user
2260 } do
2261 activities = insert_list(10, :note_activity)
2262
2263 Enum.each(activities, fn activity ->
2264 CommonAPI.favorite(activity.id, user)
2265 end)
2266
2267 third_activity = Enum.at(activities, 2)
2268 seventh_activity = Enum.at(activities, 6)
2269
2270 response =
2271 conn
2272 |> assign(:user, current_user)
2273 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2274 since_id: third_activity.id,
2275 max_id: seventh_activity.id
2276 })
2277 |> json_response(:ok)
2278
2279 assert length(response) == 3
2280 refute third_activity in response
2281 refute seventh_activity in response
2282 end
2283
2284 test "limits favorites using limit parameter", %{
2285 conn: conn,
2286 current_user: current_user,
2287 user: user
2288 } do
2289 7
2290 |> insert_list(:note_activity)
2291 |> Enum.each(fn activity ->
2292 CommonAPI.favorite(activity.id, user)
2293 end)
2294
2295 response =
2296 conn
2297 |> assign(:user, current_user)
2298 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2299 |> json_response(:ok)
2300
2301 assert length(response) == 3
2302 end
2303
2304 test "returns empty response when user does not have any favorited statuses", %{
2305 conn: conn,
2306 current_user: current_user,
2307 user: user
2308 } do
2309 response =
2310 conn
2311 |> assign(:user, current_user)
2312 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2313 |> json_response(:ok)
2314
2315 assert Enum.empty?(response)
2316 end
2317
2318 test "returns 404 error when specified user is not exist", %{conn: conn} do
2319 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2320
2321 assert json_response(conn, 404) == %{"error" => "Record not found"}
2322 end
2323
2324 test "returns 403 error when user has hidden own favorites", %{
2325 conn: conn,
2326 current_user: current_user
2327 } do
2328 user = insert(:user, %{info: %{hide_favorites: true}})
2329 activity = insert(:note_activity)
2330 CommonAPI.favorite(activity.id, user)
2331
2332 conn =
2333 conn
2334 |> assign(:user, current_user)
2335 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2336
2337 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2338 end
2339
2340 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2341 user = insert(:user)
2342 activity = insert(:note_activity)
2343 CommonAPI.favorite(activity.id, user)
2344
2345 conn =
2346 conn
2347 |> assign(:user, current_user)
2348 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2349
2350 assert user.info.hide_favorites
2351 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2352 end
2353 end
2354
2355 describe "updating credentials" do
2356 test "updates the user's bio", %{conn: conn} do
2357 user = insert(:user)
2358 user2 = insert(:user)
2359
2360 conn =
2361 conn
2362 |> assign(:user, user)
2363 |> patch("/api/v1/accounts/update_credentials", %{
2364 "note" => "I drink #cofe with @#{user2.nickname}"
2365 })
2366
2367 assert user = json_response(conn, 200)
2368
2369 assert user["note"] ==
2370 ~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=") <>
2371 user2.id <>
2372 ~s(" class="u-url mention" href=") <>
2373 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2374 end
2375
2376 test "updates the user's locking status", %{conn: conn} do
2377 user = insert(:user)
2378
2379 conn =
2380 conn
2381 |> assign(:user, user)
2382 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2383
2384 assert user = json_response(conn, 200)
2385 assert user["locked"] == true
2386 end
2387
2388 test "updates the user's default scope", %{conn: conn} do
2389 user = insert(:user)
2390
2391 conn =
2392 conn
2393 |> assign(:user, user)
2394 |> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
2395
2396 assert user = json_response(conn, 200)
2397 assert user["source"]["privacy"] == "cofe"
2398 end
2399
2400 test "updates the user's hide_followers status", %{conn: conn} do
2401 user = insert(:user)
2402
2403 conn =
2404 conn
2405 |> assign(:user, user)
2406 |> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
2407
2408 assert user = json_response(conn, 200)
2409 assert user["pleroma"]["hide_followers"] == true
2410 end
2411
2412 test "updates the user's hide_follows status", %{conn: conn} do
2413 user = insert(:user)
2414
2415 conn =
2416 conn
2417 |> assign(:user, user)
2418 |> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
2419
2420 assert user = json_response(conn, 200)
2421 assert user["pleroma"]["hide_follows"] == true
2422 end
2423
2424 test "updates the user's hide_favorites status", %{conn: conn} do
2425 user = insert(:user)
2426
2427 conn =
2428 conn
2429 |> assign(:user, user)
2430 |> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
2431
2432 assert user = json_response(conn, 200)
2433 assert user["pleroma"]["hide_favorites"] == true
2434 end
2435
2436 test "updates the user's show_role status", %{conn: conn} do
2437 user = insert(:user)
2438
2439 conn =
2440 conn
2441 |> assign(:user, user)
2442 |> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
2443
2444 assert user = json_response(conn, 200)
2445 assert user["source"]["pleroma"]["show_role"] == false
2446 end
2447
2448 test "updates the user's no_rich_text status", %{conn: conn} do
2449 user = insert(:user)
2450
2451 conn =
2452 conn
2453 |> assign(:user, user)
2454 |> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
2455
2456 assert user = json_response(conn, 200)
2457 assert user["source"]["pleroma"]["no_rich_text"] == true
2458 end
2459
2460 test "updates the user's name", %{conn: conn} do
2461 user = insert(:user)
2462
2463 conn =
2464 conn
2465 |> assign(:user, user)
2466 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2467
2468 assert user = json_response(conn, 200)
2469 assert user["display_name"] == "markorepairs"
2470 end
2471
2472 test "updates the user's avatar", %{conn: conn} do
2473 user = insert(:user)
2474
2475 new_avatar = %Plug.Upload{
2476 content_type: "image/jpg",
2477 path: Path.absname("test/fixtures/image.jpg"),
2478 filename: "an_image.jpg"
2479 }
2480
2481 conn =
2482 conn
2483 |> assign(:user, user)
2484 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2485
2486 assert user_response = json_response(conn, 200)
2487 assert user_response["avatar"] != User.avatar_url(user)
2488 end
2489
2490 test "updates the user's banner", %{conn: conn} do
2491 user = insert(:user)
2492
2493 new_header = %Plug.Upload{
2494 content_type: "image/jpg",
2495 path: Path.absname("test/fixtures/image.jpg"),
2496 filename: "an_image.jpg"
2497 }
2498
2499 conn =
2500 conn
2501 |> assign(:user, user)
2502 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2503
2504 assert user_response = json_response(conn, 200)
2505 assert user_response["header"] != User.banner_url(user)
2506 end
2507
2508 test "requires 'write' permission", %{conn: conn} do
2509 token1 = insert(:oauth_token, scopes: ["read"])
2510 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2511
2512 for token <- [token1, token2] do
2513 conn =
2514 conn
2515 |> put_req_header("authorization", "Bearer #{token.token}")
2516 |> patch("/api/v1/accounts/update_credentials", %{})
2517
2518 if token == token1 do
2519 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2520 else
2521 assert json_response(conn, 200)
2522 end
2523 end
2524 end
2525
2526 test "updates profile emojos", %{conn: conn} do
2527 user = insert(:user)
2528
2529 note = "*sips :blank:*"
2530 name = "I am :firefox:"
2531
2532 conn =
2533 conn
2534 |> assign(:user, user)
2535 |> patch("/api/v1/accounts/update_credentials", %{
2536 "note" => note,
2537 "display_name" => name
2538 })
2539
2540 assert json_response(conn, 200)
2541
2542 conn =
2543 conn
2544 |> get("/api/v1/accounts/#{user.id}")
2545
2546 assert user = json_response(conn, 200)
2547
2548 assert user["note"] == note
2549 assert user["display_name"] == name
2550 assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
2551 end
2552 end
2553
2554 test "get instance information", %{conn: conn} do
2555 conn = get(conn, "/api/v1/instance")
2556 assert result = json_response(conn, 200)
2557
2558 email = Pleroma.Config.get([:instance, :email])
2559 # Note: not checking for "max_toot_chars" since it's optional
2560 assert %{
2561 "uri" => _,
2562 "title" => _,
2563 "description" => _,
2564 "version" => _,
2565 "email" => from_config_email,
2566 "urls" => %{
2567 "streaming_api" => _
2568 },
2569 "stats" => _,
2570 "thumbnail" => _,
2571 "languages" => _,
2572 "registrations" => _,
2573 "poll_limits" => _
2574 } = result
2575
2576 assert email == from_config_email
2577 end
2578
2579 test "get instance stats", %{conn: conn} do
2580 user = insert(:user, %{local: true})
2581
2582 user2 = insert(:user, %{local: true})
2583 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2584
2585 insert(:user, %{local: false, nickname: "u@peer1.com"})
2586 insert(:user, %{local: false, nickname: "u@peer2.com"})
2587
2588 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2589
2590 # Stats should count users with missing or nil `info.deactivated` value
2591 user = User.get_cached_by_id(user.id)
2592 info_change = Changeset.change(user.info, %{deactivated: nil})
2593
2594 {:ok, _user} =
2595 user
2596 |> Changeset.change()
2597 |> Changeset.put_embed(:info, info_change)
2598 |> User.update_and_set_cache()
2599
2600 Pleroma.Stats.update_stats()
2601
2602 conn = get(conn, "/api/v1/instance")
2603
2604 assert result = json_response(conn, 200)
2605
2606 stats = result["stats"]
2607
2608 assert stats
2609 assert stats["user_count"] == 1
2610 assert stats["status_count"] == 1
2611 assert stats["domain_count"] == 2
2612 end
2613
2614 test "get peers", %{conn: conn} do
2615 insert(:user, %{local: false, nickname: "u@peer1.com"})
2616 insert(:user, %{local: false, nickname: "u@peer2.com"})
2617
2618 Pleroma.Stats.update_stats()
2619
2620 conn = get(conn, "/api/v1/instance/peers")
2621
2622 assert result = json_response(conn, 200)
2623
2624 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2625 end
2626
2627 test "put settings", %{conn: conn} do
2628 user = insert(:user)
2629
2630 conn =
2631 conn
2632 |> assign(:user, user)
2633 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2634
2635 assert _result = json_response(conn, 200)
2636
2637 user = User.get_cached_by_ap_id(user.ap_id)
2638 assert user.info.settings == %{"programming" => "socks"}
2639 end
2640
2641 describe "pinned statuses" do
2642 setup do
2643 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2644
2645 user = insert(:user)
2646 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2647
2648 [user: user, activity: activity]
2649 end
2650
2651 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2652 {:ok, _} = CommonAPI.pin(activity.id, user)
2653
2654 result =
2655 conn
2656 |> assign(:user, user)
2657 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2658 |> json_response(200)
2659
2660 id_str = to_string(activity.id)
2661
2662 assert [%{"id" => ^id_str, "pinned" => true}] = result
2663 end
2664
2665 test "pin status", %{conn: conn, user: user, activity: activity} do
2666 id_str = to_string(activity.id)
2667
2668 assert %{"id" => ^id_str, "pinned" => true} =
2669 conn
2670 |> assign(:user, user)
2671 |> post("/api/v1/statuses/#{activity.id}/pin")
2672 |> json_response(200)
2673
2674 assert [%{"id" => ^id_str, "pinned" => true}] =
2675 conn
2676 |> assign(:user, user)
2677 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2678 |> json_response(200)
2679 end
2680
2681 test "unpin status", %{conn: conn, user: user, activity: activity} do
2682 {:ok, _} = CommonAPI.pin(activity.id, user)
2683
2684 id_str = to_string(activity.id)
2685 user = refresh_record(user)
2686
2687 assert %{"id" => ^id_str, "pinned" => false} =
2688 conn
2689 |> assign(:user, user)
2690 |> post("/api/v1/statuses/#{activity.id}/unpin")
2691 |> json_response(200)
2692
2693 assert [] =
2694 conn
2695 |> assign(:user, user)
2696 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2697 |> json_response(200)
2698 end
2699
2700 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2701 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2702
2703 id_str_one = to_string(activity_one.id)
2704
2705 assert %{"id" => ^id_str_one, "pinned" => true} =
2706 conn
2707 |> assign(:user, user)
2708 |> post("/api/v1/statuses/#{id_str_one}/pin")
2709 |> json_response(200)
2710
2711 user = refresh_record(user)
2712
2713 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2714 conn
2715 |> assign(:user, user)
2716 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2717 |> json_response(400)
2718 end
2719
2720 test "Status rich-media Card", %{conn: conn, user: user} do
2721 Pleroma.Config.put([:rich_media, :enabled], true)
2722 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2723
2724 response =
2725 conn
2726 |> get("/api/v1/statuses/#{activity.id}/card")
2727 |> json_response(200)
2728
2729 assert response == %{
2730 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2731 "provider_name" => "www.imdb.com",
2732 "provider_url" => "http://www.imdb.com",
2733 "title" => "The Rock",
2734 "type" => "link",
2735 "url" => "http://www.imdb.com/title/tt0117500/",
2736 "description" => nil,
2737 "pleroma" => %{
2738 "opengraph" => %{
2739 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2740 "title" => "The Rock",
2741 "type" => "video.movie",
2742 "url" => "http://www.imdb.com/title/tt0117500/"
2743 }
2744 }
2745 }
2746
2747 # works with private posts
2748 {:ok, activity} =
2749 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2750
2751 response_two =
2752 conn
2753 |> assign(:user, user)
2754 |> get("/api/v1/statuses/#{activity.id}/card")
2755 |> json_response(200)
2756
2757 assert response_two == response
2758
2759 Pleroma.Config.put([:rich_media, :enabled], false)
2760 end
2761 end
2762
2763 test "bookmarks" do
2764 user = insert(:user)
2765 for_user = insert(:user)
2766
2767 {:ok, activity1} =
2768 CommonAPI.post(user, %{
2769 "status" => "heweoo?"
2770 })
2771
2772 {:ok, activity2} =
2773 CommonAPI.post(user, %{
2774 "status" => "heweoo!"
2775 })
2776
2777 response1 =
2778 build_conn()
2779 |> assign(:user, for_user)
2780 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2781
2782 assert json_response(response1, 200)["bookmarked"] == true
2783
2784 response2 =
2785 build_conn()
2786 |> assign(:user, for_user)
2787 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2788
2789 assert json_response(response2, 200)["bookmarked"] == true
2790
2791 bookmarks =
2792 build_conn()
2793 |> assign(:user, for_user)
2794 |> get("/api/v1/bookmarks")
2795
2796 assert [json_response(response2, 200), json_response(response1, 200)] ==
2797 json_response(bookmarks, 200)
2798
2799 response1 =
2800 build_conn()
2801 |> assign(:user, for_user)
2802 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2803
2804 assert json_response(response1, 200)["bookmarked"] == false
2805
2806 bookmarks =
2807 build_conn()
2808 |> assign(:user, for_user)
2809 |> get("/api/v1/bookmarks")
2810
2811 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2812 end
2813
2814 describe "conversation muting" do
2815 setup do
2816 user = insert(:user)
2817 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2818
2819 [user: user, activity: activity]
2820 end
2821
2822 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2823 id_str = to_string(activity.id)
2824
2825 assert %{"id" => ^id_str, "muted" => true} =
2826 conn
2827 |> assign(:user, user)
2828 |> post("/api/v1/statuses/#{activity.id}/mute")
2829 |> json_response(200)
2830 end
2831
2832 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2833 {:ok, _} = CommonAPI.add_mute(user, activity)
2834
2835 id_str = to_string(activity.id)
2836 user = refresh_record(user)
2837
2838 assert %{"id" => ^id_str, "muted" => false} =
2839 conn
2840 |> assign(:user, user)
2841 |> post("/api/v1/statuses/#{activity.id}/unmute")
2842 |> json_response(200)
2843 end
2844 end
2845
2846 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2847 user = insert(:user)
2848
2849 get_old_flavour =
2850 conn
2851 |> assign(:user, user)
2852 |> get("/api/v1/pleroma/flavour")
2853
2854 assert "glitch" == json_response(get_old_flavour, 200)
2855
2856 set_flavour =
2857 conn
2858 |> assign(:user, user)
2859 |> post("/api/v1/pleroma/flavour/vanilla")
2860
2861 assert "vanilla" == json_response(set_flavour, 200)
2862
2863 get_new_flavour =
2864 conn
2865 |> assign(:user, user)
2866 |> post("/api/v1/pleroma/flavour/vanilla")
2867
2868 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2869 end
2870
2871 describe "reports" do
2872 setup do
2873 reporter = insert(:user)
2874 target_user = insert(:user)
2875
2876 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2877
2878 [reporter: reporter, target_user: target_user, activity: activity]
2879 end
2880
2881 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2882 assert %{"action_taken" => false, "id" => _} =
2883 conn
2884 |> assign(:user, reporter)
2885 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2886 |> json_response(200)
2887 end
2888
2889 test "submit a report with statuses and comment", %{
2890 conn: conn,
2891 reporter: reporter,
2892 target_user: target_user,
2893 activity: activity
2894 } do
2895 assert %{"action_taken" => false, "id" => _} =
2896 conn
2897 |> assign(:user, reporter)
2898 |> post("/api/v1/reports", %{
2899 "account_id" => target_user.id,
2900 "status_ids" => [activity.id],
2901 "comment" => "bad status!"
2902 })
2903 |> json_response(200)
2904 end
2905
2906 test "account_id is required", %{
2907 conn: conn,
2908 reporter: reporter,
2909 activity: activity
2910 } do
2911 assert %{"error" => "Valid `account_id` required"} =
2912 conn
2913 |> assign(:user, reporter)
2914 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2915 |> json_response(400)
2916 end
2917
2918 test "comment must be up to the size specified in the config", %{
2919 conn: conn,
2920 reporter: reporter,
2921 target_user: target_user
2922 } do
2923 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2924 comment = String.pad_trailing("a", max_size + 1, "a")
2925
2926 error = %{"error" => "Comment must be up to #{max_size} characters"}
2927
2928 assert ^error =
2929 conn
2930 |> assign(:user, reporter)
2931 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2932 |> json_response(400)
2933 end
2934 end
2935
2936 describe "link headers" do
2937 test "preserves parameters in link headers", %{conn: conn} do
2938 user = insert(:user)
2939 other_user = insert(:user)
2940
2941 {:ok, activity1} =
2942 CommonAPI.post(other_user, %{
2943 "status" => "hi @#{user.nickname}",
2944 "visibility" => "public"
2945 })
2946
2947 {:ok, activity2} =
2948 CommonAPI.post(other_user, %{
2949 "status" => "hi @#{user.nickname}",
2950 "visibility" => "public"
2951 })
2952
2953 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2954 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2955
2956 conn =
2957 conn
2958 |> assign(:user, user)
2959 |> get("/api/v1/notifications", %{media_only: true})
2960
2961 assert [link_header] = get_resp_header(conn, "link")
2962 assert link_header =~ ~r/media_only=true/
2963 assert link_header =~ ~r/min_id=#{notification2.id}/
2964 assert link_header =~ ~r/max_id=#{notification1.id}/
2965 end
2966 end
2967
2968 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2969 # Need to set an old-style integer ID to reproduce the problem
2970 # (these are no longer assigned to new accounts but were preserved
2971 # for existing accounts during the migration to flakeIDs)
2972 user_one = insert(:user, %{id: 1212})
2973 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2974
2975 resp_one =
2976 conn
2977 |> get("/api/v1/accounts/#{user_one.id}")
2978
2979 resp_two =
2980 conn
2981 |> get("/api/v1/accounts/#{user_two.nickname}")
2982
2983 resp_three =
2984 conn
2985 |> get("/api/v1/accounts/#{user_two.id}")
2986
2987 acc_one = json_response(resp_one, 200)
2988 acc_two = json_response(resp_two, 200)
2989 acc_three = json_response(resp_three, 200)
2990 refute acc_one == acc_two
2991 assert acc_two == acc_three
2992 end
2993
2994 describe "custom emoji" do
2995 test "with tags", %{conn: conn} do
2996 [emoji | _body] =
2997 conn
2998 |> get("/api/v1/custom_emojis")
2999 |> json_response(200)
3000
3001 assert Map.has_key?(emoji, "shortcode")
3002 assert Map.has_key?(emoji, "static_url")
3003 assert Map.has_key?(emoji, "tags")
3004 assert is_list(emoji["tags"])
3005 assert Map.has_key?(emoji, "url")
3006 assert Map.has_key?(emoji, "visible_in_picker")
3007 end
3008 end
3009
3010 describe "index/2 redirections" do
3011 setup %{conn: conn} do
3012 session_opts = [
3013 store: :cookie,
3014 key: "_test",
3015 signing_salt: "cooldude"
3016 ]
3017
3018 conn =
3019 conn
3020 |> Plug.Session.call(Plug.Session.init(session_opts))
3021 |> fetch_session()
3022
3023 test_path = "/web/statuses/test"
3024 %{conn: conn, path: test_path}
3025 end
3026
3027 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3028 conn = get(conn, path)
3029
3030 assert conn.status == 302
3031 assert redirected_to(conn) == "/web/login"
3032 end
3033
3034 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3035 token = insert(:oauth_token)
3036
3037 conn =
3038 conn
3039 |> assign(:user, token.user)
3040 |> put_session(:oauth_token, token.token)
3041 |> get(path)
3042
3043 assert conn.status == 200
3044 end
3045
3046 test "saves referer path to session", %{conn: conn, path: path} do
3047 conn = get(conn, path)
3048 return_to = Plug.Conn.get_session(conn, :return_to)
3049
3050 assert return_to == path
3051 end
3052
3053 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3054 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3055 auth = insert(:oauth_authorization, app: app)
3056
3057 conn =
3058 conn
3059 |> put_session(:return_to, path)
3060 |> get("/web/login", %{code: auth.token})
3061
3062 assert conn.status == 302
3063 assert redirected_to(conn) == path
3064 end
3065
3066 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3067 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3068 auth = insert(:oauth_authorization, app: app)
3069
3070 conn = get(conn, "/web/login", %{code: auth.token})
3071
3072 assert conn.status == 302
3073 assert redirected_to(conn) == "/web/getting-started"
3074 end
3075 end
3076
3077 describe "scheduled activities" do
3078 test "creates a scheduled activity", %{conn: conn} do
3079 user = insert(:user)
3080 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3081
3082 conn =
3083 conn
3084 |> assign(:user, user)
3085 |> post("/api/v1/statuses", %{
3086 "status" => "scheduled",
3087 "scheduled_at" => scheduled_at
3088 })
3089
3090 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3091 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3092 assert [] == Repo.all(Activity)
3093 end
3094
3095 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3096 user = insert(:user)
3097 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3098
3099 file = %Plug.Upload{
3100 content_type: "image/jpg",
3101 path: Path.absname("test/fixtures/image.jpg"),
3102 filename: "an_image.jpg"
3103 }
3104
3105 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3106
3107 conn =
3108 conn
3109 |> assign(:user, user)
3110 |> post("/api/v1/statuses", %{
3111 "media_ids" => [to_string(upload.id)],
3112 "status" => "scheduled",
3113 "scheduled_at" => scheduled_at
3114 })
3115
3116 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3117 assert %{"type" => "image"} = media_attachment
3118 end
3119
3120 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3121 %{conn: conn} do
3122 user = insert(:user)
3123
3124 scheduled_at =
3125 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3126
3127 conn =
3128 conn
3129 |> assign(:user, user)
3130 |> post("/api/v1/statuses", %{
3131 "status" => "not scheduled",
3132 "scheduled_at" => scheduled_at
3133 })
3134
3135 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3136 assert [] == Repo.all(ScheduledActivity)
3137 end
3138
3139 test "returns error when daily user limit is exceeded", %{conn: conn} do
3140 user = insert(:user)
3141
3142 today =
3143 NaiveDateTime.utc_now()
3144 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3145 |> NaiveDateTime.to_iso8601()
3146
3147 attrs = %{params: %{}, scheduled_at: today}
3148 {:ok, _} = ScheduledActivity.create(user, attrs)
3149 {:ok, _} = ScheduledActivity.create(user, attrs)
3150
3151 conn =
3152 conn
3153 |> assign(:user, user)
3154 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3155
3156 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3157 end
3158
3159 test "returns error when total user limit is exceeded", %{conn: conn} do
3160 user = insert(:user)
3161
3162 today =
3163 NaiveDateTime.utc_now()
3164 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3165 |> NaiveDateTime.to_iso8601()
3166
3167 tomorrow =
3168 NaiveDateTime.utc_now()
3169 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3170 |> NaiveDateTime.to_iso8601()
3171
3172 attrs = %{params: %{}, scheduled_at: today}
3173 {:ok, _} = ScheduledActivity.create(user, attrs)
3174 {:ok, _} = ScheduledActivity.create(user, attrs)
3175 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3176
3177 conn =
3178 conn
3179 |> assign(:user, user)
3180 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3181
3182 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3183 end
3184
3185 test "shows scheduled activities", %{conn: conn} do
3186 user = insert(:user)
3187 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3188 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3189 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3190 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3191
3192 conn =
3193 conn
3194 |> assign(:user, user)
3195
3196 # min_id
3197 conn_res =
3198 conn
3199 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3200
3201 result = json_response(conn_res, 200)
3202 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3203
3204 # since_id
3205 conn_res =
3206 conn
3207 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3208
3209 result = json_response(conn_res, 200)
3210 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3211
3212 # max_id
3213 conn_res =
3214 conn
3215 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3216
3217 result = json_response(conn_res, 200)
3218 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3219 end
3220
3221 test "shows a scheduled activity", %{conn: conn} do
3222 user = insert(:user)
3223 scheduled_activity = insert(:scheduled_activity, user: user)
3224
3225 res_conn =
3226 conn
3227 |> assign(:user, user)
3228 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3229
3230 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3231 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3232
3233 res_conn =
3234 conn
3235 |> assign(:user, user)
3236 |> get("/api/v1/scheduled_statuses/404")
3237
3238 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3239 end
3240
3241 test "updates a scheduled activity", %{conn: conn} do
3242 user = insert(:user)
3243 scheduled_activity = insert(:scheduled_activity, user: user)
3244
3245 new_scheduled_at =
3246 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3247
3248 res_conn =
3249 conn
3250 |> assign(:user, user)
3251 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3252 scheduled_at: new_scheduled_at
3253 })
3254
3255 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3256 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3257
3258 res_conn =
3259 conn
3260 |> assign(:user, user)
3261 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3262
3263 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3264 end
3265
3266 test "deletes a scheduled activity", %{conn: conn} do
3267 user = insert(:user)
3268 scheduled_activity = insert(:scheduled_activity, user: user)
3269
3270 res_conn =
3271 conn
3272 |> assign(:user, user)
3273 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3274
3275 assert %{} = json_response(res_conn, 200)
3276 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3277
3278 res_conn =
3279 conn
3280 |> assign(:user, user)
3281 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3282
3283 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3284 end
3285 end
3286
3287 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3288 user1 = insert(:user)
3289 user2 = insert(:user)
3290 user3 = insert(:user)
3291
3292 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
3293
3294 # Reply to status from another user
3295 conn1 =
3296 conn
3297 |> assign(:user, user2)
3298 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3299
3300 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3301
3302 activity = Activity.get_by_id_with_object(id)
3303
3304 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3305 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3306
3307 # Reblog from the third user
3308 conn2 =
3309 conn
3310 |> assign(:user, user3)
3311 |> post("/api/v1/statuses/#{activity.id}/reblog")
3312
3313 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3314 json_response(conn2, 200)
3315
3316 assert to_string(activity.id) == id
3317
3318 # Getting third user status
3319 conn3 =
3320 conn
3321 |> assign(:user, user3)
3322 |> get("api/v1/timelines/home")
3323
3324 [reblogged_activity] = json_response(conn3, 200)
3325
3326 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3327
3328 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3329 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3330 end
3331
3332 describe "create account by app" do
3333 setup do
3334 enabled = Pleroma.Config.get([:app_account_creation, :enabled])
3335 max_requests = Pleroma.Config.get([:app_account_creation, :max_requests])
3336 interval = Pleroma.Config.get([:app_account_creation, :interval])
3337
3338 Pleroma.Config.put([:app_account_creation, :enabled], true)
3339 Pleroma.Config.put([:app_account_creation, :max_requests], 5)
3340 Pleroma.Config.put([:app_account_creation, :interval], 1)
3341
3342 on_exit(fn ->
3343 Pleroma.Config.put([:app_account_creation, :enabled], enabled)
3344 Pleroma.Config.put([:app_account_creation, :max_requests], max_requests)
3345 Pleroma.Config.put([:app_account_creation, :interval], interval)
3346 end)
3347
3348 :ok
3349 end
3350
3351 test "Account registration via Application", %{conn: conn} do
3352 conn =
3353 conn
3354 |> post("/api/v1/apps", %{
3355 client_name: "client_name",
3356 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3357 scopes: "read, write, follow"
3358 })
3359
3360 %{
3361 "client_id" => client_id,
3362 "client_secret" => client_secret,
3363 "id" => _,
3364 "name" => "client_name",
3365 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3366 "vapid_key" => _,
3367 "website" => nil
3368 } = json_response(conn, 200)
3369
3370 conn =
3371 conn
3372 |> post("/oauth/token", %{
3373 grant_type: "client_credentials",
3374 client_id: client_id,
3375 client_secret: client_secret
3376 })
3377
3378 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3379 json_response(conn, 200)
3380
3381 assert token
3382 token_from_db = Repo.get_by(Token, token: token)
3383 assert token_from_db
3384 assert refresh
3385 assert scope == "read write follow"
3386
3387 conn =
3388 build_conn()
3389 |> put_req_header("authorization", "Bearer " <> token)
3390 |> post("/api/v1/accounts", %{
3391 username: "lain",
3392 email: "lain@example.org",
3393 password: "PlzDontHackLain",
3394 agreement: true
3395 })
3396
3397 %{
3398 "access_token" => token,
3399 "created_at" => _created_at,
3400 "scope" => _scope,
3401 "token_type" => "Bearer"
3402 } = json_response(conn, 200)
3403
3404 token_from_db = Repo.get_by(Token, token: token)
3405 assert token_from_db
3406 token_from_db = Repo.preload(token_from_db, :user)
3407 assert token_from_db.user
3408
3409 assert token_from_db.user.info.confirmation_pending
3410 end
3411
3412 test "rate limit", %{conn: conn} do
3413 app_token = insert(:oauth_token, user: nil)
3414
3415 conn =
3416 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3417 |> Map.put(:remote_ip, {15, 15, 15, 15})
3418
3419 for i <- 1..5 do
3420 conn =
3421 conn
3422 |> post("/api/v1/accounts", %{
3423 username: "#{i}lain",
3424 email: "#{i}lain@example.org",
3425 password: "PlzDontHackLain",
3426 agreement: true
3427 })
3428
3429 %{
3430 "access_token" => token,
3431 "created_at" => _created_at,
3432 "scope" => _scope,
3433 "token_type" => "Bearer"
3434 } = json_response(conn, 200)
3435
3436 token_from_db = Repo.get_by(Token, token: token)
3437 assert token_from_db
3438 token_from_db = Repo.preload(token_from_db, :user)
3439 assert token_from_db.user
3440
3441 assert token_from_db.user.info.confirmation_pending
3442 end
3443
3444 conn =
3445 conn
3446 |> post("/api/v1/accounts", %{
3447 username: "6lain",
3448 email: "6lain@example.org",
3449 password: "PlzDontHackLain",
3450 agreement: true
3451 })
3452
3453 assert json_response(conn, 403) == %{"error" => "Rate limit exceeded."}
3454 end
3455 end
3456
3457 describe "GET /api/v1/polls/:id" do
3458 test "returns poll entity for object id", %{conn: conn} do
3459 user = insert(:user)
3460
3461 {:ok, activity} =
3462 CommonAPI.post(user, %{
3463 "status" => "Pleroma does",
3464 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3465 })
3466
3467 object = Object.normalize(activity)
3468
3469 conn =
3470 conn
3471 |> assign(:user, user)
3472 |> get("/api/v1/polls/#{object.id}")
3473
3474 response = json_response(conn, 200)
3475 id = object.id
3476 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3477 end
3478
3479 test "does not expose polls for private statuses", %{conn: conn} do
3480 user = insert(:user)
3481 other_user = insert(:user)
3482
3483 {:ok, activity} =
3484 CommonAPI.post(user, %{
3485 "status" => "Pleroma does",
3486 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3487 "visibility" => "private"
3488 })
3489
3490 object = Object.normalize(activity)
3491
3492 conn =
3493 conn
3494 |> assign(:user, other_user)
3495 |> get("/api/v1/polls/#{object.id}")
3496
3497 assert json_response(conn, 404)
3498 end
3499 end
3500
3501 describe "POST /api/v1/polls/:id/votes" do
3502 test "votes are added to the poll", %{conn: conn} do
3503 user = insert(:user)
3504 other_user = insert(:user)
3505
3506 {:ok, activity} =
3507 CommonAPI.post(user, %{
3508 "status" => "A very delicious sandwich",
3509 "poll" => %{
3510 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3511 "expires_in" => 20,
3512 "multiple" => true
3513 }
3514 })
3515
3516 object = Object.normalize(activity)
3517
3518 conn =
3519 conn
3520 |> assign(:user, other_user)
3521 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3522
3523 assert json_response(conn, 200)
3524 object = Object.get_by_id(object.id)
3525
3526 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => totalItems}} ->
3527 totalItems == 1
3528 end)
3529 end
3530
3531 test "author can't vote", %{conn: conn} do
3532 user = insert(:user)
3533
3534 {:ok, activity} =
3535 CommonAPI.post(user, %{
3536 "status" => "Am I cute?",
3537 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3538 })
3539
3540 object = Object.normalize(activity)
3541
3542 assert conn
3543 |> assign(:user, user)
3544 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3545 |> json_response(422) == %{"error" => "Already voted"}
3546
3547 object = Object.get_by_id(object.id)
3548
3549 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3550 end
3551
3552 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3553 user = insert(:user)
3554 other_user = insert(:user)
3555
3556 {:ok, activity} =
3557 CommonAPI.post(user, %{
3558 "status" => "The glass is",
3559 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3560 })
3561
3562 object = Object.normalize(activity)
3563
3564 assert conn
3565 |> assign(:user, other_user)
3566 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3567 |> json_response(422) == %{"error" => "Too many choices"}
3568
3569 object = Object.get_by_id(object.id)
3570
3571 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => totalItems}} ->
3572 totalItems == 1
3573 end)
3574 end
3575 end
3576 end