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