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