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