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