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