Merge branch 'fix/mastoapi-status-view' 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 CommonAPI.favorite(activity.id, user2)
1025 {:ok, user2} = User.bookmark(user2, activity.data["object"]["id"])
1026 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1027 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1028
1029 conn_res =
1030 conn
1031 |> assign(:user, user3)
1032 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1033
1034 assert %{
1035 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1036 "reblogged" => false,
1037 "favourited" => false,
1038 "bookmarked" => false
1039 } = json_response(conn_res, 200)
1040
1041 conn_res =
1042 conn
1043 |> assign(:user, user2)
1044 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1045
1046 assert %{
1047 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1048 "reblogged" => true,
1049 "favourited" => true,
1050 "bookmarked" => true
1051 } = json_response(conn_res, 200)
1052
1053 assert to_string(activity.id) == id
1054 end
1055 end
1056
1057 describe "unreblogging" do
1058 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1059 activity = insert(:note_activity)
1060 user = insert(:user)
1061
1062 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1063
1064 conn =
1065 conn
1066 |> assign(:user, user)
1067 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1068
1069 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1070
1071 assert to_string(activity.id) == id
1072 end
1073 end
1074
1075 describe "favoriting" do
1076 test "favs a status and returns it", %{conn: conn} do
1077 activity = insert(:note_activity)
1078 user = insert(:user)
1079
1080 conn =
1081 conn
1082 |> assign(:user, user)
1083 |> post("/api/v1/statuses/#{activity.id}/favourite")
1084
1085 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1086 json_response(conn, 200)
1087
1088 assert to_string(activity.id) == id
1089 end
1090
1091 test "returns 500 for a wrong id", %{conn: conn} do
1092 user = insert(:user)
1093
1094 resp =
1095 conn
1096 |> assign(:user, user)
1097 |> post("/api/v1/statuses/1/favourite")
1098 |> json_response(500)
1099
1100 assert resp == "Something went wrong"
1101 end
1102 end
1103
1104 describe "unfavoriting" do
1105 test "unfavorites a status and returns it", %{conn: conn} do
1106 activity = insert(:note_activity)
1107 user = insert(:user)
1108
1109 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1110
1111 conn =
1112 conn
1113 |> assign(:user, user)
1114 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1115
1116 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1117 json_response(conn, 200)
1118
1119 assert to_string(activity.id) == id
1120 end
1121 end
1122
1123 describe "user timelines" do
1124 test "gets a users statuses", %{conn: conn} do
1125 user_one = insert(:user)
1126 user_two = insert(:user)
1127 user_three = insert(:user)
1128
1129 {:ok, user_three} = User.follow(user_three, user_one)
1130
1131 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1132
1133 {:ok, direct_activity} =
1134 CommonAPI.post(user_one, %{
1135 "status" => "Hi, @#{user_two.nickname}.",
1136 "visibility" => "direct"
1137 })
1138
1139 {:ok, private_activity} =
1140 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1141
1142 resp =
1143 conn
1144 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1145
1146 assert [%{"id" => id}] = json_response(resp, 200)
1147 assert id == to_string(activity.id)
1148
1149 resp =
1150 conn
1151 |> assign(:user, user_two)
1152 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1153
1154 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1155 assert id_one == to_string(direct_activity.id)
1156 assert id_two == to_string(activity.id)
1157
1158 resp =
1159 conn
1160 |> assign(:user, user_three)
1161 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1162
1163 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1164 assert id_one == to_string(private_activity.id)
1165 assert id_two == to_string(activity.id)
1166 end
1167
1168 test "unimplemented pinned statuses feature", %{conn: conn} do
1169 note = insert(:note_activity)
1170 user = User.get_by_ap_id(note.data["actor"])
1171
1172 conn =
1173 conn
1174 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1175
1176 assert json_response(conn, 200) == []
1177 end
1178
1179 test "gets an users media", %{conn: conn} do
1180 note = insert(:note_activity)
1181 user = User.get_by_ap_id(note.data["actor"])
1182
1183 file = %Plug.Upload{
1184 content_type: "image/jpg",
1185 path: Path.absname("test/fixtures/image.jpg"),
1186 filename: "an_image.jpg"
1187 }
1188
1189 media =
1190 TwitterAPI.upload(file, user, "json")
1191 |> Poison.decode!()
1192
1193 {:ok, image_post} =
1194 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1195
1196 conn =
1197 conn
1198 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1199
1200 assert [%{"id" => id}] = json_response(conn, 200)
1201 assert id == to_string(image_post.id)
1202
1203 conn =
1204 build_conn()
1205 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1206
1207 assert [%{"id" => id}] = json_response(conn, 200)
1208 assert id == to_string(image_post.id)
1209 end
1210
1211 test "gets a user's statuses without reblogs", %{conn: conn} do
1212 user = insert(:user)
1213 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1214 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1215
1216 conn =
1217 conn
1218 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1219
1220 assert [%{"id" => id}] = json_response(conn, 200)
1221 assert id == to_string(post.id)
1222
1223 conn =
1224 conn
1225 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1226
1227 assert [%{"id" => id}] = json_response(conn, 200)
1228 assert id == to_string(post.id)
1229 end
1230 end
1231
1232 describe "user relationships" do
1233 test "returns the relationships for the current user", %{conn: conn} do
1234 user = insert(:user)
1235 other_user = insert(:user)
1236 {:ok, user} = User.follow(user, other_user)
1237
1238 conn =
1239 conn
1240 |> assign(:user, user)
1241 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1242
1243 assert [relationship] = json_response(conn, 200)
1244
1245 assert to_string(other_user.id) == relationship["id"]
1246 end
1247 end
1248
1249 describe "locked accounts" do
1250 test "/api/v1/follow_requests works" do
1251 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1252 other_user = insert(:user)
1253
1254 {:ok, _activity} = ActivityPub.follow(other_user, user)
1255
1256 user = User.get_by_id(user.id)
1257 other_user = User.get_by_id(other_user.id)
1258
1259 assert User.following?(other_user, user) == false
1260
1261 conn =
1262 build_conn()
1263 |> assign(:user, user)
1264 |> get("/api/v1/follow_requests")
1265
1266 assert [relationship] = json_response(conn, 200)
1267 assert to_string(other_user.id) == relationship["id"]
1268 end
1269
1270 test "/api/v1/follow_requests/:id/authorize works" do
1271 user = insert(:user, %{info: %User.Info{locked: true}})
1272 other_user = insert(:user)
1273
1274 {:ok, _activity} = ActivityPub.follow(other_user, user)
1275
1276 user = User.get_by_id(user.id)
1277 other_user = User.get_by_id(other_user.id)
1278
1279 assert User.following?(other_user, user) == false
1280
1281 conn =
1282 build_conn()
1283 |> assign(:user, user)
1284 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1285
1286 assert relationship = json_response(conn, 200)
1287 assert to_string(other_user.id) == relationship["id"]
1288
1289 user = User.get_by_id(user.id)
1290 other_user = User.get_by_id(other_user.id)
1291
1292 assert User.following?(other_user, user) == true
1293 end
1294
1295 test "verify_credentials", %{conn: conn} do
1296 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "private"}})
1297
1298 conn =
1299 conn
1300 |> assign(:user, user)
1301 |> get("/api/v1/accounts/verify_credentials")
1302
1303 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1304 assert id == to_string(user.id)
1305 end
1306
1307 test "/api/v1/follow_requests/:id/reject works" do
1308 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1309 other_user = insert(:user)
1310
1311 {:ok, _activity} = ActivityPub.follow(other_user, user)
1312
1313 user = User.get_by_id(user.id)
1314
1315 conn =
1316 build_conn()
1317 |> assign(:user, user)
1318 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1319
1320 assert relationship = json_response(conn, 200)
1321 assert to_string(other_user.id) == relationship["id"]
1322
1323 user = User.get_by_id(user.id)
1324 other_user = User.get_by_id(other_user.id)
1325
1326 assert User.following?(other_user, user) == false
1327 end
1328 end
1329
1330 test "account fetching", %{conn: conn} do
1331 user = insert(:user)
1332
1333 conn =
1334 conn
1335 |> get("/api/v1/accounts/#{user.id}")
1336
1337 assert %{"id" => id} = json_response(conn, 200)
1338 assert id == to_string(user.id)
1339
1340 conn =
1341 build_conn()
1342 |> get("/api/v1/accounts/-1")
1343
1344 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1345 end
1346
1347 test "account fetching also works nickname", %{conn: conn} do
1348 user = insert(:user)
1349
1350 conn =
1351 conn
1352 |> get("/api/v1/accounts/#{user.nickname}")
1353
1354 assert %{"id" => id} = json_response(conn, 200)
1355 assert id == user.id
1356 end
1357
1358 test "media upload", %{conn: conn} do
1359 file = %Plug.Upload{
1360 content_type: "image/jpg",
1361 path: Path.absname("test/fixtures/image.jpg"),
1362 filename: "an_image.jpg"
1363 }
1364
1365 desc = "Description of the image"
1366
1367 user = insert(:user)
1368
1369 conn =
1370 conn
1371 |> assign(:user, user)
1372 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1373
1374 assert media = json_response(conn, 200)
1375
1376 assert media["type"] == "image"
1377 assert media["description"] == desc
1378 assert media["id"]
1379
1380 object = Repo.get(Object, media["id"])
1381 assert object.data["actor"] == User.ap_id(user)
1382 end
1383
1384 test "hashtag timeline", %{conn: conn} do
1385 following = insert(:user)
1386
1387 capture_log(fn ->
1388 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1389
1390 {:ok, [_activity]} =
1391 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1392
1393 nconn =
1394 conn
1395 |> get("/api/v1/timelines/tag/2hu")
1396
1397 assert [%{"id" => id}] = json_response(nconn, 200)
1398
1399 assert id == to_string(activity.id)
1400
1401 # works for different capitalization too
1402 nconn =
1403 conn
1404 |> get("/api/v1/timelines/tag/2HU")
1405
1406 assert [%{"id" => id}] = json_response(nconn, 200)
1407
1408 assert id == to_string(activity.id)
1409 end)
1410 end
1411
1412 test "multi-hashtag timeline", %{conn: conn} do
1413 user = insert(:user)
1414
1415 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1416 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1417 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1418
1419 any_test =
1420 conn
1421 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1422
1423 [status_none, status_test1, status_test] = json_response(any_test, 200)
1424
1425 assert to_string(activity_test.id) == status_test["id"]
1426 assert to_string(activity_test1.id) == status_test1["id"]
1427 assert to_string(activity_none.id) == status_none["id"]
1428
1429 restricted_test =
1430 conn
1431 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1432
1433 assert [status_test1] == json_response(restricted_test, 200)
1434
1435 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1436
1437 assert [status_none] == json_response(all_test, 200)
1438 end
1439
1440 test "getting followers", %{conn: conn} do
1441 user = insert(:user)
1442 other_user = insert(:user)
1443 {:ok, user} = User.follow(user, other_user)
1444
1445 conn =
1446 conn
1447 |> get("/api/v1/accounts/#{other_user.id}/followers")
1448
1449 assert [%{"id" => id}] = json_response(conn, 200)
1450 assert id == to_string(user.id)
1451 end
1452
1453 test "getting followers, hide_followers", %{conn: conn} do
1454 user = insert(:user)
1455 other_user = insert(:user, %{info: %{hide_followers: true}})
1456 {:ok, _user} = User.follow(user, other_user)
1457
1458 conn =
1459 conn
1460 |> get("/api/v1/accounts/#{other_user.id}/followers")
1461
1462 assert [] == json_response(conn, 200)
1463 end
1464
1465 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1466 user = insert(:user)
1467 other_user = insert(:user, %{info: %{hide_followers: true}})
1468 {:ok, _user} = User.follow(user, other_user)
1469
1470 conn =
1471 conn
1472 |> assign(:user, other_user)
1473 |> get("/api/v1/accounts/#{other_user.id}/followers")
1474
1475 refute [] == json_response(conn, 200)
1476 end
1477
1478 test "getting followers, pagination", %{conn: conn} do
1479 user = insert(:user)
1480 follower1 = insert(:user)
1481 follower2 = insert(:user)
1482 follower3 = insert(:user)
1483 {:ok, _} = User.follow(follower1, user)
1484 {:ok, _} = User.follow(follower2, user)
1485 {:ok, _} = User.follow(follower3, user)
1486
1487 conn =
1488 conn
1489 |> assign(:user, user)
1490
1491 res_conn =
1492 conn
1493 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1494
1495 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1496 assert id3 == follower3.id
1497 assert id2 == follower2.id
1498
1499 res_conn =
1500 conn
1501 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1502
1503 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1504 assert id2 == follower2.id
1505 assert id1 == follower1.id
1506
1507 res_conn =
1508 conn
1509 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1510
1511 assert [%{"id" => id2}] = json_response(res_conn, 200)
1512 assert id2 == follower2.id
1513
1514 assert [link_header] = get_resp_header(res_conn, "link")
1515 assert link_header =~ ~r/min_id=#{follower2.id}/
1516 assert link_header =~ ~r/max_id=#{follower2.id}/
1517 end
1518
1519 test "getting following", %{conn: conn} do
1520 user = insert(:user)
1521 other_user = insert(:user)
1522 {:ok, user} = User.follow(user, other_user)
1523
1524 conn =
1525 conn
1526 |> get("/api/v1/accounts/#{user.id}/following")
1527
1528 assert [%{"id" => id}] = json_response(conn, 200)
1529 assert id == to_string(other_user.id)
1530 end
1531
1532 test "getting following, hide_follows", %{conn: conn} do
1533 user = insert(:user, %{info: %{hide_follows: true}})
1534 other_user = insert(:user)
1535 {:ok, user} = User.follow(user, other_user)
1536
1537 conn =
1538 conn
1539 |> get("/api/v1/accounts/#{user.id}/following")
1540
1541 assert [] == json_response(conn, 200)
1542 end
1543
1544 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1545 user = insert(:user, %{info: %{hide_follows: true}})
1546 other_user = insert(:user)
1547 {:ok, user} = User.follow(user, other_user)
1548
1549 conn =
1550 conn
1551 |> assign(:user, user)
1552 |> get("/api/v1/accounts/#{user.id}/following")
1553
1554 refute [] == json_response(conn, 200)
1555 end
1556
1557 test "getting following, pagination", %{conn: conn} do
1558 user = insert(:user)
1559 following1 = insert(:user)
1560 following2 = insert(:user)
1561 following3 = insert(:user)
1562 {:ok, _} = User.follow(user, following1)
1563 {:ok, _} = User.follow(user, following2)
1564 {:ok, _} = User.follow(user, following3)
1565
1566 conn =
1567 conn
1568 |> assign(:user, user)
1569
1570 res_conn =
1571 conn
1572 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1573
1574 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1575 assert id3 == following3.id
1576 assert id2 == following2.id
1577
1578 res_conn =
1579 conn
1580 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1581
1582 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1583 assert id2 == following2.id
1584 assert id1 == following1.id
1585
1586 res_conn =
1587 conn
1588 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1589
1590 assert [%{"id" => id2}] = json_response(res_conn, 200)
1591 assert id2 == following2.id
1592
1593 assert [link_header] = get_resp_header(res_conn, "link")
1594 assert link_header =~ ~r/min_id=#{following2.id}/
1595 assert link_header =~ ~r/max_id=#{following2.id}/
1596 end
1597
1598 test "following / unfollowing a user", %{conn: conn} do
1599 user = insert(:user)
1600 other_user = insert(:user)
1601
1602 conn =
1603 conn
1604 |> assign(:user, user)
1605 |> post("/api/v1/accounts/#{other_user.id}/follow")
1606
1607 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1608
1609 user = User.get_by_id(user.id)
1610
1611 conn =
1612 build_conn()
1613 |> assign(:user, user)
1614 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1615
1616 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1617
1618 user = User.get_by_id(user.id)
1619
1620 conn =
1621 build_conn()
1622 |> assign(:user, user)
1623 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1624
1625 assert %{"id" => id} = json_response(conn, 200)
1626 assert id == to_string(other_user.id)
1627 end
1628
1629 test "following without reblogs" do
1630 follower = insert(:user)
1631 followed = insert(:user)
1632 other_user = insert(:user)
1633
1634 conn =
1635 build_conn()
1636 |> assign(:user, follower)
1637 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
1638
1639 assert %{"showing_reblogs" => false} = json_response(conn, 200)
1640
1641 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
1642 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
1643
1644 conn =
1645 build_conn()
1646 |> assign(:user, User.get_cached_by_id(follower.id))
1647 |> get("/api/v1/timelines/home")
1648
1649 assert [] == json_response(conn, 200)
1650
1651 conn =
1652 build_conn()
1653 |> assign(:user, follower)
1654 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
1655
1656 assert %{"showing_reblogs" => true} = json_response(conn, 200)
1657
1658 conn =
1659 build_conn()
1660 |> assign(:user, User.get_cached_by_id(follower.id))
1661 |> get("/api/v1/timelines/home")
1662
1663 expected_activity_id = reblog.id
1664 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
1665 end
1666
1667 test "following / unfollowing errors" do
1668 user = insert(:user)
1669
1670 conn =
1671 build_conn()
1672 |> assign(:user, user)
1673
1674 # self follow
1675 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
1676 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1677
1678 # self unfollow
1679 user = User.get_cached_by_id(user.id)
1680 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
1681 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1682
1683 # self follow via uri
1684 user = User.get_cached_by_id(user.id)
1685 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
1686 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1687
1688 # follow non existing user
1689 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
1690 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1691
1692 # follow non existing user via uri
1693 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
1694 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1695
1696 # unfollow non existing user
1697 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
1698 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
1699 end
1700
1701 test "muting / unmuting a user", %{conn: conn} do
1702 user = insert(:user)
1703 other_user = insert(:user)
1704
1705 conn =
1706 conn
1707 |> assign(:user, user)
1708 |> post("/api/v1/accounts/#{other_user.id}/mute")
1709
1710 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1711
1712 user = User.get_by_id(user.id)
1713
1714 conn =
1715 build_conn()
1716 |> assign(:user, user)
1717 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1718
1719 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1720 end
1721
1722 test "subscribing / unsubscribing to a user", %{conn: conn} do
1723 user = insert(:user)
1724 subscription_target = insert(:user)
1725
1726 conn =
1727 conn
1728 |> assign(:user, user)
1729 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
1730
1731 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
1732
1733 conn =
1734 build_conn()
1735 |> assign(:user, user)
1736 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
1737
1738 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
1739 end
1740
1741 test "getting a list of mutes", %{conn: conn} do
1742 user = insert(:user)
1743 other_user = insert(:user)
1744
1745 {:ok, user} = User.mute(user, other_user)
1746
1747 conn =
1748 conn
1749 |> assign(:user, user)
1750 |> get("/api/v1/mutes")
1751
1752 other_user_id = to_string(other_user.id)
1753 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1754 end
1755
1756 test "blocking / unblocking a user", %{conn: conn} do
1757 user = insert(:user)
1758 other_user = insert(:user)
1759
1760 conn =
1761 conn
1762 |> assign(:user, user)
1763 |> post("/api/v1/accounts/#{other_user.id}/block")
1764
1765 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1766
1767 user = User.get_by_id(user.id)
1768
1769 conn =
1770 build_conn()
1771 |> assign(:user, user)
1772 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1773
1774 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1775 end
1776
1777 test "getting a list of blocks", %{conn: conn} do
1778 user = insert(:user)
1779 other_user = insert(:user)
1780
1781 {:ok, user} = User.block(user, other_user)
1782
1783 conn =
1784 conn
1785 |> assign(:user, user)
1786 |> get("/api/v1/blocks")
1787
1788 other_user_id = to_string(other_user.id)
1789 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1790 end
1791
1792 test "blocking / unblocking a domain", %{conn: conn} do
1793 user = insert(:user)
1794 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1795
1796 conn =
1797 conn
1798 |> assign(:user, user)
1799 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1800
1801 assert %{} = json_response(conn, 200)
1802 user = User.get_cached_by_ap_id(user.ap_id)
1803 assert User.blocks?(user, other_user)
1804
1805 conn =
1806 build_conn()
1807 |> assign(:user, user)
1808 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1809
1810 assert %{} = json_response(conn, 200)
1811 user = User.get_cached_by_ap_id(user.ap_id)
1812 refute User.blocks?(user, other_user)
1813 end
1814
1815 test "getting a list of domain blocks", %{conn: conn} do
1816 user = insert(:user)
1817
1818 {:ok, user} = User.block_domain(user, "bad.site")
1819 {:ok, user} = User.block_domain(user, "even.worse.site")
1820
1821 conn =
1822 conn
1823 |> assign(:user, user)
1824 |> get("/api/v1/domain_blocks")
1825
1826 domain_blocks = json_response(conn, 200)
1827
1828 assert "bad.site" in domain_blocks
1829 assert "even.worse.site" in domain_blocks
1830 end
1831
1832 test "unimplemented follow_requests, blocks, domain blocks" do
1833 user = insert(:user)
1834
1835 ["blocks", "domain_blocks", "follow_requests"]
1836 |> Enum.each(fn endpoint ->
1837 conn =
1838 build_conn()
1839 |> assign(:user, user)
1840 |> get("/api/v1/#{endpoint}")
1841
1842 assert [] = json_response(conn, 200)
1843 end)
1844 end
1845
1846 test "account search", %{conn: conn} do
1847 user = insert(:user)
1848 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1849 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1850
1851 results =
1852 conn
1853 |> assign(:user, user)
1854 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1855 |> json_response(200)
1856
1857 result_ids = for result <- results, do: result["acct"]
1858
1859 assert user_two.nickname in result_ids
1860 assert user_three.nickname in result_ids
1861
1862 results =
1863 conn
1864 |> assign(:user, user)
1865 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
1866 |> json_response(200)
1867
1868 result_ids = for result <- results, do: result["acct"]
1869
1870 assert user_three.nickname in result_ids
1871 end
1872
1873 test "search", %{conn: conn} do
1874 user = insert(:user)
1875 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1876 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1877
1878 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
1879
1880 {:ok, _activity} =
1881 CommonAPI.post(user, %{
1882 "status" => "This is about 2hu, but private",
1883 "visibility" => "private"
1884 })
1885
1886 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
1887
1888 conn =
1889 conn
1890 |> get("/api/v1/search", %{"q" => "2hu"})
1891
1892 assert results = json_response(conn, 200)
1893
1894 [account | _] = results["accounts"]
1895 assert account["id"] == to_string(user_three.id)
1896
1897 assert results["hashtags"] == []
1898
1899 [status] = results["statuses"]
1900 assert status["id"] == to_string(activity.id)
1901 end
1902
1903 test "search fetches remote statuses", %{conn: conn} do
1904 capture_log(fn ->
1905 conn =
1906 conn
1907 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
1908
1909 assert results = json_response(conn, 200)
1910
1911 [status] = results["statuses"]
1912 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1913 end)
1914 end
1915
1916 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
1917 {:ok, activity} =
1918 CommonAPI.post(insert(:user), %{
1919 "status" => "This is about 2hu, but private",
1920 "visibility" => "private"
1921 })
1922
1923 capture_log(fn ->
1924 conn =
1925 conn
1926 |> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
1927
1928 assert results = json_response(conn, 200)
1929
1930 [] = results["statuses"]
1931 end)
1932 end
1933
1934 test "search fetches remote accounts", %{conn: conn} do
1935 conn =
1936 conn
1937 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
1938
1939 assert results = json_response(conn, 200)
1940 [account] = results["accounts"]
1941 assert account["acct"] == "shp@social.heldscal.la"
1942 end
1943
1944 test "returns the favorites of a user", %{conn: conn} do
1945 user = insert(:user)
1946 other_user = insert(:user)
1947
1948 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1949 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1950
1951 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1952
1953 first_conn =
1954 conn
1955 |> assign(:user, user)
1956 |> get("/api/v1/favourites")
1957
1958 assert [status] = json_response(first_conn, 200)
1959 assert status["id"] == to_string(activity.id)
1960
1961 assert [{"link", _link_header}] =
1962 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1963
1964 # Honours query params
1965 {:ok, second_activity} =
1966 CommonAPI.post(other_user, %{
1967 "status" =>
1968 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1969 })
1970
1971 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1972
1973 last_like = status["id"]
1974
1975 second_conn =
1976 conn
1977 |> assign(:user, user)
1978 |> get("/api/v1/favourites?since_id=#{last_like}")
1979
1980 assert [second_status] = json_response(second_conn, 200)
1981 assert second_status["id"] == to_string(second_activity.id)
1982
1983 third_conn =
1984 conn
1985 |> assign(:user, user)
1986 |> get("/api/v1/favourites?limit=0")
1987
1988 assert [] = json_response(third_conn, 200)
1989 end
1990
1991 describe "updating credentials" do
1992 test "updates the user's bio", %{conn: conn} do
1993 user = insert(:user)
1994 user2 = insert(:user)
1995
1996 conn =
1997 conn
1998 |> assign(:user, user)
1999 |> patch("/api/v1/accounts/update_credentials", %{
2000 "note" => "I drink #cofe with @#{user2.nickname}"
2001 })
2002
2003 assert user = json_response(conn, 200)
2004
2005 assert user["note"] ==
2006 ~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=") <>
2007 user2.id <>
2008 ~s(" class="u-url mention" href=") <>
2009 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
2010 end
2011
2012 test "updates the user's locking status", %{conn: conn} do
2013 user = insert(:user)
2014
2015 conn =
2016 conn
2017 |> assign(:user, user)
2018 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
2019
2020 assert user = json_response(conn, 200)
2021 assert user["locked"] == true
2022 end
2023
2024 test "updates the user's name", %{conn: conn} do
2025 user = insert(:user)
2026
2027 conn =
2028 conn
2029 |> assign(:user, user)
2030 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
2031
2032 assert user = json_response(conn, 200)
2033 assert user["display_name"] == "markorepairs"
2034 end
2035
2036 test "updates the user's avatar", %{conn: conn} do
2037 user = insert(:user)
2038
2039 new_avatar = %Plug.Upload{
2040 content_type: "image/jpg",
2041 path: Path.absname("test/fixtures/image.jpg"),
2042 filename: "an_image.jpg"
2043 }
2044
2045 conn =
2046 conn
2047 |> assign(:user, user)
2048 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
2049
2050 assert user_response = json_response(conn, 200)
2051 assert user_response["avatar"] != User.avatar_url(user)
2052 end
2053
2054 test "updates the user's banner", %{conn: conn} do
2055 user = insert(:user)
2056
2057 new_header = %Plug.Upload{
2058 content_type: "image/jpg",
2059 path: Path.absname("test/fixtures/image.jpg"),
2060 filename: "an_image.jpg"
2061 }
2062
2063 conn =
2064 conn
2065 |> assign(:user, user)
2066 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
2067
2068 assert user_response = json_response(conn, 200)
2069 assert user_response["header"] != User.banner_url(user)
2070 end
2071
2072 test "requires 'write' permission", %{conn: conn} do
2073 token1 = insert(:oauth_token, scopes: ["read"])
2074 token2 = insert(:oauth_token, scopes: ["write", "follow"])
2075
2076 for token <- [token1, token2] do
2077 conn =
2078 conn
2079 |> put_req_header("authorization", "Bearer #{token.token}")
2080 |> patch("/api/v1/accounts/update_credentials", %{})
2081
2082 if token == token1 do
2083 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
2084 else
2085 assert json_response(conn, 200)
2086 end
2087 end
2088 end
2089 end
2090
2091 test "get instance information", %{conn: conn} do
2092 conn = get(conn, "/api/v1/instance")
2093 assert result = json_response(conn, 200)
2094
2095 email = Pleroma.Config.get([:instance, :email])
2096 # Note: not checking for "max_toot_chars" since it's optional
2097 assert %{
2098 "uri" => _,
2099 "title" => _,
2100 "description" => _,
2101 "version" => _,
2102 "email" => from_config_email,
2103 "urls" => %{
2104 "streaming_api" => _
2105 },
2106 "stats" => _,
2107 "thumbnail" => _,
2108 "languages" => _,
2109 "registrations" => _
2110 } = result
2111
2112 assert email == from_config_email
2113 end
2114
2115 test "get instance stats", %{conn: conn} do
2116 user = insert(:user, %{local: true})
2117
2118 user2 = insert(:user, %{local: true})
2119 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2120
2121 insert(:user, %{local: false, nickname: "u@peer1.com"})
2122 insert(:user, %{local: false, nickname: "u@peer2.com"})
2123
2124 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
2125
2126 # Stats should count users with missing or nil `info.deactivated` value
2127 user = User.get_by_id(user.id)
2128 info_change = Changeset.change(user.info, %{deactivated: nil})
2129
2130 {:ok, _user} =
2131 user
2132 |> Changeset.change()
2133 |> Changeset.put_embed(:info, info_change)
2134 |> User.update_and_set_cache()
2135
2136 Pleroma.Stats.update_stats()
2137
2138 conn = get(conn, "/api/v1/instance")
2139
2140 assert result = json_response(conn, 200)
2141
2142 stats = result["stats"]
2143
2144 assert stats
2145 assert stats["user_count"] == 1
2146 assert stats["status_count"] == 1
2147 assert stats["domain_count"] == 2
2148 end
2149
2150 test "get peers", %{conn: conn} do
2151 insert(:user, %{local: false, nickname: "u@peer1.com"})
2152 insert(:user, %{local: false, nickname: "u@peer2.com"})
2153
2154 Pleroma.Stats.update_stats()
2155
2156 conn = get(conn, "/api/v1/instance/peers")
2157
2158 assert result = json_response(conn, 200)
2159
2160 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2161 end
2162
2163 test "put settings", %{conn: conn} do
2164 user = insert(:user)
2165
2166 conn =
2167 conn
2168 |> assign(:user, user)
2169 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2170
2171 assert _result = json_response(conn, 200)
2172
2173 user = User.get_cached_by_ap_id(user.ap_id)
2174 assert user.info.settings == %{"programming" => "socks"}
2175 end
2176
2177 describe "pinned statuses" do
2178 setup do
2179 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2180
2181 user = insert(:user)
2182 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2183
2184 [user: user, activity: activity]
2185 end
2186
2187 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2188 {:ok, _} = CommonAPI.pin(activity.id, user)
2189
2190 result =
2191 conn
2192 |> assign(:user, user)
2193 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2194 |> json_response(200)
2195
2196 id_str = to_string(activity.id)
2197
2198 assert [%{"id" => ^id_str, "pinned" => true}] = result
2199 end
2200
2201 test "pin status", %{conn: conn, user: user, activity: activity} do
2202 id_str = to_string(activity.id)
2203
2204 assert %{"id" => ^id_str, "pinned" => true} =
2205 conn
2206 |> assign(:user, user)
2207 |> post("/api/v1/statuses/#{activity.id}/pin")
2208 |> json_response(200)
2209
2210 assert [%{"id" => ^id_str, "pinned" => true}] =
2211 conn
2212 |> assign(:user, user)
2213 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2214 |> json_response(200)
2215 end
2216
2217 test "unpin status", %{conn: conn, user: user, activity: activity} do
2218 {:ok, _} = CommonAPI.pin(activity.id, user)
2219
2220 id_str = to_string(activity.id)
2221 user = refresh_record(user)
2222
2223 assert %{"id" => ^id_str, "pinned" => false} =
2224 conn
2225 |> assign(:user, user)
2226 |> post("/api/v1/statuses/#{activity.id}/unpin")
2227 |> json_response(200)
2228
2229 assert [] =
2230 conn
2231 |> assign(:user, user)
2232 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2233 |> json_response(200)
2234 end
2235
2236 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2237 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2238
2239 id_str_one = to_string(activity_one.id)
2240
2241 assert %{"id" => ^id_str_one, "pinned" => true} =
2242 conn
2243 |> assign(:user, user)
2244 |> post("/api/v1/statuses/#{id_str_one}/pin")
2245 |> json_response(200)
2246
2247 user = refresh_record(user)
2248
2249 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2250 conn
2251 |> assign(:user, user)
2252 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2253 |> json_response(400)
2254 end
2255
2256 test "Status rich-media Card", %{conn: conn, user: user} do
2257 Pleroma.Config.put([:rich_media, :enabled], true)
2258 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2259
2260 response =
2261 conn
2262 |> get("/api/v1/statuses/#{activity.id}/card")
2263 |> json_response(200)
2264
2265 assert response == %{
2266 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2267 "provider_name" => "www.imdb.com",
2268 "provider_url" => "http://www.imdb.com",
2269 "title" => "The Rock",
2270 "type" => "link",
2271 "url" => "http://www.imdb.com/title/tt0117500/",
2272 "description" => nil,
2273 "pleroma" => %{
2274 "opengraph" => %{
2275 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2276 "title" => "The Rock",
2277 "type" => "video.movie",
2278 "url" => "http://www.imdb.com/title/tt0117500/"
2279 }
2280 }
2281 }
2282
2283 # works with private posts
2284 {:ok, activity} =
2285 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2286
2287 response_two =
2288 conn
2289 |> assign(:user, user)
2290 |> get("/api/v1/statuses/#{activity.id}/card")
2291 |> json_response(200)
2292
2293 assert response_two == response
2294
2295 Pleroma.Config.put([:rich_media, :enabled], false)
2296 end
2297 end
2298
2299 test "bookmarks" do
2300 user = insert(:user)
2301 for_user = insert(:user)
2302
2303 {:ok, activity1} =
2304 CommonAPI.post(user, %{
2305 "status" => "heweoo?"
2306 })
2307
2308 {:ok, activity2} =
2309 CommonAPI.post(user, %{
2310 "status" => "heweoo!"
2311 })
2312
2313 response1 =
2314 build_conn()
2315 |> assign(:user, for_user)
2316 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2317
2318 assert json_response(response1, 200)["bookmarked"] == true
2319
2320 response2 =
2321 build_conn()
2322 |> assign(:user, for_user)
2323 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2324
2325 assert json_response(response2, 200)["bookmarked"] == true
2326
2327 bookmarks =
2328 build_conn()
2329 |> assign(:user, for_user)
2330 |> get("/api/v1/bookmarks")
2331
2332 assert [json_response(response2, 200), json_response(response1, 200)] ==
2333 json_response(bookmarks, 200)
2334
2335 response1 =
2336 build_conn()
2337 |> assign(:user, for_user)
2338 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2339
2340 assert json_response(response1, 200)["bookmarked"] == false
2341
2342 bookmarks =
2343 build_conn()
2344 |> assign(:user, for_user)
2345 |> get("/api/v1/bookmarks")
2346
2347 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2348 end
2349
2350 describe "conversation muting" do
2351 setup do
2352 user = insert(:user)
2353 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2354
2355 [user: user, activity: activity]
2356 end
2357
2358 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2359 id_str = to_string(activity.id)
2360
2361 assert %{"id" => ^id_str, "muted" => true} =
2362 conn
2363 |> assign(:user, user)
2364 |> post("/api/v1/statuses/#{activity.id}/mute")
2365 |> json_response(200)
2366 end
2367
2368 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2369 {:ok, _} = CommonAPI.add_mute(user, activity)
2370
2371 id_str = to_string(activity.id)
2372 user = refresh_record(user)
2373
2374 assert %{"id" => ^id_str, "muted" => false} =
2375 conn
2376 |> assign(:user, user)
2377 |> post("/api/v1/statuses/#{activity.id}/unmute")
2378 |> json_response(200)
2379 end
2380 end
2381
2382 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2383 user = insert(:user)
2384
2385 get_old_flavour =
2386 conn
2387 |> assign(:user, user)
2388 |> get("/api/v1/pleroma/flavour")
2389
2390 assert "glitch" == json_response(get_old_flavour, 200)
2391
2392 set_flavour =
2393 conn
2394 |> assign(:user, user)
2395 |> post("/api/v1/pleroma/flavour/vanilla")
2396
2397 assert "vanilla" == json_response(set_flavour, 200)
2398
2399 get_new_flavour =
2400 conn
2401 |> assign(:user, user)
2402 |> post("/api/v1/pleroma/flavour/vanilla")
2403
2404 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2405 end
2406
2407 describe "reports" do
2408 setup do
2409 reporter = insert(:user)
2410 target_user = insert(:user)
2411
2412 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2413
2414 [reporter: reporter, target_user: target_user, activity: activity]
2415 end
2416
2417 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2418 assert %{"action_taken" => false, "id" => _} =
2419 conn
2420 |> assign(:user, reporter)
2421 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2422 |> json_response(200)
2423 end
2424
2425 test "submit a report with statuses and comment", %{
2426 conn: conn,
2427 reporter: reporter,
2428 target_user: target_user,
2429 activity: activity
2430 } do
2431 assert %{"action_taken" => false, "id" => _} =
2432 conn
2433 |> assign(:user, reporter)
2434 |> post("/api/v1/reports", %{
2435 "account_id" => target_user.id,
2436 "status_ids" => [activity.id],
2437 "comment" => "bad status!"
2438 })
2439 |> json_response(200)
2440 end
2441
2442 test "account_id is required", %{
2443 conn: conn,
2444 reporter: reporter,
2445 activity: activity
2446 } do
2447 assert %{"error" => "Valid `account_id` required"} =
2448 conn
2449 |> assign(:user, reporter)
2450 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2451 |> json_response(400)
2452 end
2453
2454 test "comment must be up to the size specified in the config", %{
2455 conn: conn,
2456 reporter: reporter,
2457 target_user: target_user
2458 } do
2459 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2460 comment = String.pad_trailing("a", max_size + 1, "a")
2461
2462 error = %{"error" => "Comment must be up to #{max_size} characters"}
2463
2464 assert ^error =
2465 conn
2466 |> assign(:user, reporter)
2467 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2468 |> json_response(400)
2469 end
2470 end
2471
2472 describe "link headers" do
2473 test "preserves parameters in link headers", %{conn: conn} do
2474 user = insert(:user)
2475 other_user = insert(:user)
2476
2477 {:ok, activity1} =
2478 CommonAPI.post(other_user, %{
2479 "status" => "hi @#{user.nickname}",
2480 "visibility" => "public"
2481 })
2482
2483 {:ok, activity2} =
2484 CommonAPI.post(other_user, %{
2485 "status" => "hi @#{user.nickname}",
2486 "visibility" => "public"
2487 })
2488
2489 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2490 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2491
2492 conn =
2493 conn
2494 |> assign(:user, user)
2495 |> get("/api/v1/notifications", %{media_only: true})
2496
2497 assert [link_header] = get_resp_header(conn, "link")
2498 assert link_header =~ ~r/media_only=true/
2499 assert link_header =~ ~r/min_id=#{notification2.id}/
2500 assert link_header =~ ~r/max_id=#{notification1.id}/
2501 end
2502 end
2503
2504 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2505 # Need to set an old-style integer ID to reproduce the problem
2506 # (these are no longer assigned to new accounts but were preserved
2507 # for existing accounts during the migration to flakeIDs)
2508 user_one = insert(:user, %{id: 1212})
2509 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2510
2511 resp_one =
2512 conn
2513 |> get("/api/v1/accounts/#{user_one.id}")
2514
2515 resp_two =
2516 conn
2517 |> get("/api/v1/accounts/#{user_two.nickname}")
2518
2519 resp_three =
2520 conn
2521 |> get("/api/v1/accounts/#{user_two.id}")
2522
2523 acc_one = json_response(resp_one, 200)
2524 acc_two = json_response(resp_two, 200)
2525 acc_three = json_response(resp_three, 200)
2526 refute acc_one == acc_two
2527 assert acc_two == acc_three
2528 end
2529
2530 describe "custom emoji" do
2531 test "with tags", %{conn: conn} do
2532 [emoji | _body] =
2533 conn
2534 |> get("/api/v1/custom_emojis")
2535 |> json_response(200)
2536
2537 assert Map.has_key?(emoji, "shortcode")
2538 assert Map.has_key?(emoji, "static_url")
2539 assert Map.has_key?(emoji, "tags")
2540 assert is_list(emoji["tags"])
2541 assert Map.has_key?(emoji, "url")
2542 assert Map.has_key?(emoji, "visible_in_picker")
2543 end
2544 end
2545
2546 describe "index/2 redirections" do
2547 setup %{conn: conn} do
2548 session_opts = [
2549 store: :cookie,
2550 key: "_test",
2551 signing_salt: "cooldude"
2552 ]
2553
2554 conn =
2555 conn
2556 |> Plug.Session.call(Plug.Session.init(session_opts))
2557 |> fetch_session()
2558
2559 test_path = "/web/statuses/test"
2560 %{conn: conn, path: test_path}
2561 end
2562
2563 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2564 conn = get(conn, path)
2565
2566 assert conn.status == 302
2567 assert redirected_to(conn) == "/web/login"
2568 end
2569
2570 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2571 token = insert(:oauth_token)
2572
2573 conn =
2574 conn
2575 |> assign(:user, token.user)
2576 |> put_session(:oauth_token, token.token)
2577 |> get(path)
2578
2579 assert conn.status == 200
2580 end
2581
2582 test "saves referer path to session", %{conn: conn, path: path} do
2583 conn = get(conn, path)
2584 return_to = Plug.Conn.get_session(conn, :return_to)
2585
2586 assert return_to == path
2587 end
2588
2589 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2590 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2591 auth = insert(:oauth_authorization, app: app)
2592
2593 conn =
2594 conn
2595 |> put_session(:return_to, path)
2596 |> get("/web/login", %{code: auth.token})
2597
2598 assert conn.status == 302
2599 assert redirected_to(conn) == path
2600 end
2601
2602 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2603 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2604 auth = insert(:oauth_authorization, app: app)
2605
2606 conn = get(conn, "/web/login", %{code: auth.token})
2607
2608 assert conn.status == 302
2609 assert redirected_to(conn) == "/web/getting-started"
2610 end
2611 end
2612
2613 describe "scheduled activities" do
2614 test "creates a scheduled activity", %{conn: conn} do
2615 user = insert(:user)
2616 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2617
2618 conn =
2619 conn
2620 |> assign(:user, user)
2621 |> post("/api/v1/statuses", %{
2622 "status" => "scheduled",
2623 "scheduled_at" => scheduled_at
2624 })
2625
2626 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2627 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2628 assert [] == Repo.all(Activity)
2629 end
2630
2631 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2632 user = insert(:user)
2633 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2634
2635 file = %Plug.Upload{
2636 content_type: "image/jpg",
2637 path: Path.absname("test/fixtures/image.jpg"),
2638 filename: "an_image.jpg"
2639 }
2640
2641 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
2642
2643 conn =
2644 conn
2645 |> assign(:user, user)
2646 |> post("/api/v1/statuses", %{
2647 "media_ids" => [to_string(upload.id)],
2648 "status" => "scheduled",
2649 "scheduled_at" => scheduled_at
2650 })
2651
2652 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
2653 assert %{"type" => "image"} = media_attachment
2654 end
2655
2656 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
2657 %{conn: conn} do
2658 user = insert(:user)
2659
2660 scheduled_at =
2661 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
2662
2663 conn =
2664 conn
2665 |> assign(:user, user)
2666 |> post("/api/v1/statuses", %{
2667 "status" => "not scheduled",
2668 "scheduled_at" => scheduled_at
2669 })
2670
2671 assert %{"content" => "not scheduled"} = json_response(conn, 200)
2672 assert [] == Repo.all(ScheduledActivity)
2673 end
2674
2675 test "returns error when daily user limit is exceeded", %{conn: conn} do
2676 user = insert(:user)
2677
2678 today =
2679 NaiveDateTime.utc_now()
2680 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2681 |> NaiveDateTime.to_iso8601()
2682
2683 attrs = %{params: %{}, scheduled_at: today}
2684 {:ok, _} = ScheduledActivity.create(user, attrs)
2685 {:ok, _} = ScheduledActivity.create(user, attrs)
2686
2687 conn =
2688 conn
2689 |> assign(:user, user)
2690 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
2691
2692 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
2693 end
2694
2695 test "returns error when total user limit is exceeded", %{conn: conn} do
2696 user = insert(:user)
2697
2698 today =
2699 NaiveDateTime.utc_now()
2700 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2701 |> NaiveDateTime.to_iso8601()
2702
2703 tomorrow =
2704 NaiveDateTime.utc_now()
2705 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
2706 |> NaiveDateTime.to_iso8601()
2707
2708 attrs = %{params: %{}, scheduled_at: today}
2709 {:ok, _} = ScheduledActivity.create(user, attrs)
2710 {:ok, _} = ScheduledActivity.create(user, attrs)
2711 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
2712
2713 conn =
2714 conn
2715 |> assign(:user, user)
2716 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
2717
2718 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
2719 end
2720
2721 test "shows scheduled activities", %{conn: conn} do
2722 user = insert(:user)
2723 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
2724 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
2725 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
2726 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
2727
2728 conn =
2729 conn
2730 |> assign(:user, user)
2731
2732 # min_id
2733 conn_res =
2734 conn
2735 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
2736
2737 result = json_response(conn_res, 200)
2738 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2739
2740 # since_id
2741 conn_res =
2742 conn
2743 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
2744
2745 result = json_response(conn_res, 200)
2746 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
2747
2748 # max_id
2749 conn_res =
2750 conn
2751 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
2752
2753 result = json_response(conn_res, 200)
2754 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2755 end
2756
2757 test "shows a scheduled activity", %{conn: conn} do
2758 user = insert(:user)
2759 scheduled_activity = insert(:scheduled_activity, user: user)
2760
2761 res_conn =
2762 conn
2763 |> assign(:user, user)
2764 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2765
2766 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
2767 assert scheduled_activity_id == scheduled_activity.id |> to_string()
2768
2769 res_conn =
2770 conn
2771 |> assign(:user, user)
2772 |> get("/api/v1/scheduled_statuses/404")
2773
2774 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2775 end
2776
2777 test "updates a scheduled activity", %{conn: conn} do
2778 user = insert(:user)
2779 scheduled_activity = insert(:scheduled_activity, user: user)
2780
2781 new_scheduled_at =
2782 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2783
2784 res_conn =
2785 conn
2786 |> assign(:user, user)
2787 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
2788 scheduled_at: new_scheduled_at
2789 })
2790
2791 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
2792 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
2793
2794 res_conn =
2795 conn
2796 |> assign(:user, user)
2797 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
2798
2799 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2800 end
2801
2802 test "deletes a scheduled activity", %{conn: conn} do
2803 user = insert(:user)
2804 scheduled_activity = insert(:scheduled_activity, user: user)
2805
2806 res_conn =
2807 conn
2808 |> assign(:user, user)
2809 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2810
2811 assert %{} = json_response(res_conn, 200)
2812 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
2813
2814 res_conn =
2815 conn
2816 |> assign(:user, user)
2817 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2818
2819 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2820 end
2821 end
2822
2823 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
2824 user1 = insert(:user)
2825 user2 = insert(:user)
2826 user3 = insert(:user)
2827
2828 {:ok, replied_to} = TwitterAPI.create_status(user1, %{"status" => "cofe"})
2829
2830 # Reply to status from another user
2831 conn1 =
2832 conn
2833 |> assign(:user, user2)
2834 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
2835
2836 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
2837
2838 activity = Activity.get_by_id_with_object(id)
2839
2840 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
2841 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
2842
2843 # Reblog from the third user
2844 conn2 =
2845 conn
2846 |> assign(:user, user3)
2847 |> post("/api/v1/statuses/#{activity.id}/reblog")
2848
2849 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
2850 json_response(conn2, 200)
2851
2852 assert to_string(activity.id) == id
2853
2854 # Getting third user status
2855 conn3 =
2856 conn
2857 |> assign(:user, user3)
2858 |> get("api/v1/timelines/home")
2859
2860 [reblogged_activity] = json_response(conn3, 200)
2861
2862 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
2863
2864 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
2865 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
2866 end
2867 end