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