User: Add raw_bio, storing unformatted bio
[akkoma] / test / web / mastodon_api / controllers / status_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
6 use Pleroma.Web.ConnCase
7
8 alias Pleroma.Activity
9 alias Pleroma.ActivityExpiration
10 alias Pleroma.Config
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Object
13 alias Pleroma.Repo
14 alias Pleroma.ScheduledActivity
15 alias Pleroma.Tests.ObanHelpers
16 alias Pleroma.User
17 alias Pleroma.Web.ActivityPub.ActivityPub
18 alias Pleroma.Web.CommonAPI
19
20 import Pleroma.Factory
21
22 setup do: clear_config([:instance, :federating])
23 setup do: clear_config([:instance, :allow_relay])
24 setup do: clear_config([:rich_media, :enabled])
25
26 describe "posting statuses" do
27 setup do: oauth_access(["write:statuses"])
28
29 test "posting a status does not increment reblog_count when relaying", %{conn: conn} do
30 Pleroma.Config.put([:instance, :federating], true)
31 Pleroma.Config.get([:instance, :allow_relay], true)
32
33 response =
34 conn
35 |> put_req_header("content-type", "application/json")
36 |> post("api/v1/statuses", %{
37 "content_type" => "text/plain",
38 "source" => "Pleroma FE",
39 "status" => "Hello world",
40 "visibility" => "public"
41 })
42 |> json_response_and_validate_schema(200)
43
44 assert response["reblogs_count"] == 0
45 ObanHelpers.perform_all()
46
47 response =
48 conn
49 |> get("api/v1/statuses/#{response["id"]}", %{})
50 |> json_response_and_validate_schema(200)
51
52 assert response["reblogs_count"] == 0
53 end
54
55 test "posting a status", %{conn: conn} do
56 idempotency_key = "Pikachu rocks!"
57
58 conn_one =
59 conn
60 |> put_req_header("content-type", "application/json")
61 |> put_req_header("idempotency-key", idempotency_key)
62 |> post("/api/v1/statuses", %{
63 "status" => "cofe",
64 "spoiler_text" => "2hu",
65 "sensitive" => "0"
66 })
67
68 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
69 # Six hours
70 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
71
72 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
73 json_response_and_validate_schema(conn_one, 200)
74
75 assert Activity.get_by_id(id)
76
77 conn_two =
78 conn
79 |> put_req_header("content-type", "application/json")
80 |> put_req_header("idempotency-key", idempotency_key)
81 |> post("/api/v1/statuses", %{
82 "status" => "cofe",
83 "spoiler_text" => "2hu",
84 "sensitive" => 0
85 })
86
87 assert %{"id" => second_id} = json_response(conn_two, 200)
88 assert id == second_id
89
90 conn_three =
91 conn
92 |> put_req_header("content-type", "application/json")
93 |> post("/api/v1/statuses", %{
94 "status" => "cofe",
95 "spoiler_text" => "2hu",
96 "sensitive" => "False"
97 })
98
99 assert %{"id" => third_id} = json_response_and_validate_schema(conn_three, 200)
100 refute id == third_id
101
102 # An activity that will expire:
103 # 2 hours
104 expires_in = 120 * 60
105
106 conn_four =
107 conn
108 |> put_req_header("content-type", "application/json")
109 |> post("api/v1/statuses", %{
110 "status" => "oolong",
111 "expires_in" => expires_in
112 })
113
114 assert fourth_response =
115 %{"id" => fourth_id} = json_response_and_validate_schema(conn_four, 200)
116
117 assert activity = Activity.get_by_id(fourth_id)
118 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
119
120 estimated_expires_at =
121 NaiveDateTime.utc_now()
122 |> NaiveDateTime.add(expires_in)
123 |> NaiveDateTime.truncate(:second)
124
125 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
126 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
127
128 assert fourth_response["pleroma"]["expires_at"] ==
129 NaiveDateTime.to_iso8601(expiration.scheduled_at)
130 end
131
132 test "it fails to create a status if `expires_in` is less or equal than an hour", %{
133 conn: conn
134 } do
135 # 1 hour
136 expires_in = 60 * 60
137
138 assert %{"error" => "Expiry date is too soon"} =
139 conn
140 |> put_req_header("content-type", "application/json")
141 |> post("api/v1/statuses", %{
142 "status" => "oolong",
143 "expires_in" => expires_in
144 })
145 |> json_response_and_validate_schema(422)
146
147 # 30 minutes
148 expires_in = 30 * 60
149
150 assert %{"error" => "Expiry date is too soon"} =
151 conn
152 |> put_req_header("content-type", "application/json")
153 |> post("api/v1/statuses", %{
154 "status" => "oolong",
155 "expires_in" => expires_in
156 })
157 |> json_response_and_validate_schema(422)
158 end
159
160 test "posting an undefined status with an attachment", %{user: user, conn: conn} do
161 file = %Plug.Upload{
162 content_type: "image/jpg",
163 path: Path.absname("test/fixtures/image.jpg"),
164 filename: "an_image.jpg"
165 }
166
167 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
168
169 conn =
170 conn
171 |> put_req_header("content-type", "application/json")
172 |> post("/api/v1/statuses", %{
173 "media_ids" => [to_string(upload.id)]
174 })
175
176 assert json_response_and_validate_schema(conn, 200)
177 end
178
179 test "replying to a status", %{user: user, conn: conn} do
180 {:ok, replied_to} = CommonAPI.post(user, %{status: "cofe"})
181
182 conn =
183 conn
184 |> put_req_header("content-type", "application/json")
185 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
186
187 assert %{"content" => "xD", "id" => id} = json_response_and_validate_schema(conn, 200)
188
189 activity = Activity.get_by_id(id)
190
191 assert activity.data["context"] == replied_to.data["context"]
192 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
193 end
194
195 test "replying to a direct message with visibility other than direct", %{
196 user: user,
197 conn: conn
198 } do
199 {:ok, replied_to} = CommonAPI.post(user, %{status: "suya..", visibility: "direct"})
200
201 Enum.each(["public", "private", "unlisted"], fn visibility ->
202 conn =
203 conn
204 |> put_req_header("content-type", "application/json")
205 |> post("/api/v1/statuses", %{
206 "status" => "@#{user.nickname} hey",
207 "in_reply_to_id" => replied_to.id,
208 "visibility" => visibility
209 })
210
211 assert json_response_and_validate_schema(conn, 422) == %{
212 "error" => "The message visibility must be direct"
213 }
214 end)
215 end
216
217 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
218 conn =
219 conn
220 |> put_req_header("content-type", "application/json")
221 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
222
223 assert %{"content" => "xD", "id" => id} = json_response_and_validate_schema(conn, 200)
224 assert Activity.get_by_id(id)
225 end
226
227 test "posting a sensitive status", %{conn: conn} do
228 conn =
229 conn
230 |> put_req_header("content-type", "application/json")
231 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
232
233 assert %{"content" => "cofe", "id" => id, "sensitive" => true} =
234 json_response_and_validate_schema(conn, 200)
235
236 assert Activity.get_by_id(id)
237 end
238
239 test "posting a fake status", %{conn: conn} do
240 real_conn =
241 conn
242 |> put_req_header("content-type", "application/json")
243 |> post("/api/v1/statuses", %{
244 "status" =>
245 "\"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"
246 })
247
248 real_status = json_response_and_validate_schema(real_conn, 200)
249
250 assert real_status
251 assert Object.get_by_ap_id(real_status["uri"])
252
253 real_status =
254 real_status
255 |> Map.put("id", nil)
256 |> Map.put("url", nil)
257 |> Map.put("uri", nil)
258 |> Map.put("created_at", nil)
259 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
260
261 fake_conn =
262 conn
263 |> put_req_header("content-type", "application/json")
264 |> post("/api/v1/statuses", %{
265 "status" =>
266 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
267 "preview" => true
268 })
269
270 fake_status = json_response_and_validate_schema(fake_conn, 200)
271
272 assert fake_status
273 refute Object.get_by_ap_id(fake_status["uri"])
274
275 fake_status =
276 fake_status
277 |> Map.put("id", nil)
278 |> Map.put("url", nil)
279 |> Map.put("uri", nil)
280 |> Map.put("created_at", nil)
281 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
282
283 assert real_status == fake_status
284 end
285
286 test "posting a status with OGP link preview", %{conn: conn} do
287 Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
288 Config.put([:rich_media, :enabled], true)
289
290 conn =
291 conn
292 |> put_req_header("content-type", "application/json")
293 |> post("/api/v1/statuses", %{
294 "status" => "https://example.com/ogp"
295 })
296
297 assert %{"id" => id, "card" => %{"title" => "The Rock"}} =
298 json_response_and_validate_schema(conn, 200)
299
300 assert Activity.get_by_id(id)
301 end
302
303 test "posting a direct status", %{conn: conn} do
304 user2 = insert(:user)
305 content = "direct cofe @#{user2.nickname}"
306
307 conn =
308 conn
309 |> put_req_header("content-type", "application/json")
310 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
311
312 assert %{"id" => id} = response = json_response_and_validate_schema(conn, 200)
313 assert response["visibility"] == "direct"
314 assert response["pleroma"]["direct_conversation_id"]
315 assert activity = Activity.get_by_id(id)
316 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
317 assert activity.data["to"] == [user2.ap_id]
318 assert activity.data["cc"] == []
319 end
320 end
321
322 describe "posting scheduled statuses" do
323 setup do: oauth_access(["write:statuses"])
324
325 test "creates a scheduled activity", %{conn: conn} do
326 scheduled_at =
327 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
328 |> NaiveDateTime.to_iso8601()
329 |> Kernel.<>("Z")
330
331 conn =
332 conn
333 |> put_req_header("content-type", "application/json")
334 |> post("/api/v1/statuses", %{
335 "status" => "scheduled",
336 "scheduled_at" => scheduled_at
337 })
338
339 assert %{"scheduled_at" => expected_scheduled_at} =
340 json_response_and_validate_schema(conn, 200)
341
342 assert expected_scheduled_at == CommonAPI.Utils.to_masto_date(scheduled_at)
343 assert [] == Repo.all(Activity)
344 end
345
346 test "ignores nil values", %{conn: conn} do
347 conn =
348 conn
349 |> put_req_header("content-type", "application/json")
350 |> post("/api/v1/statuses", %{
351 "status" => "not scheduled",
352 "scheduled_at" => nil
353 })
354
355 assert result = json_response_and_validate_schema(conn, 200)
356 assert Activity.get_by_id(result["id"])
357 end
358
359 test "creates a scheduled activity with a media attachment", %{user: user, conn: conn} do
360 scheduled_at =
361 NaiveDateTime.utc_now()
362 |> NaiveDateTime.add(:timer.minutes(120), :millisecond)
363 |> NaiveDateTime.to_iso8601()
364 |> Kernel.<>("Z")
365
366 file = %Plug.Upload{
367 content_type: "image/jpg",
368 path: Path.absname("test/fixtures/image.jpg"),
369 filename: "an_image.jpg"
370 }
371
372 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
373
374 conn =
375 conn
376 |> put_req_header("content-type", "application/json")
377 |> post("/api/v1/statuses", %{
378 "media_ids" => [to_string(upload.id)],
379 "status" => "scheduled",
380 "scheduled_at" => scheduled_at
381 })
382
383 assert %{"media_attachments" => [media_attachment]} =
384 json_response_and_validate_schema(conn, 200)
385
386 assert %{"type" => "image"} = media_attachment
387 end
388
389 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
390 %{conn: conn} do
391 scheduled_at =
392 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
393 |> NaiveDateTime.to_iso8601()
394 |> Kernel.<>("Z")
395
396 conn =
397 conn
398 |> put_req_header("content-type", "application/json")
399 |> post("/api/v1/statuses", %{
400 "status" => "not scheduled",
401 "scheduled_at" => scheduled_at
402 })
403
404 assert %{"content" => "not scheduled"} = json_response_and_validate_schema(conn, 200)
405 assert [] == Repo.all(ScheduledActivity)
406 end
407
408 test "returns error when daily user limit is exceeded", %{user: user, conn: conn} do
409 today =
410 NaiveDateTime.utc_now()
411 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
412 |> NaiveDateTime.to_iso8601()
413 # TODO
414 |> Kernel.<>("Z")
415
416 attrs = %{params: %{}, scheduled_at: today}
417 {:ok, _} = ScheduledActivity.create(user, attrs)
418 {:ok, _} = ScheduledActivity.create(user, attrs)
419
420 conn =
421 conn
422 |> put_req_header("content-type", "application/json")
423 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
424
425 assert %{"error" => "daily limit exceeded"} == json_response_and_validate_schema(conn, 422)
426 end
427
428 test "returns error when total user limit is exceeded", %{user: user, conn: conn} do
429 today =
430 NaiveDateTime.utc_now()
431 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
432 |> NaiveDateTime.to_iso8601()
433 |> Kernel.<>("Z")
434
435 tomorrow =
436 NaiveDateTime.utc_now()
437 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
438 |> NaiveDateTime.to_iso8601()
439 |> Kernel.<>("Z")
440
441 attrs = %{params: %{}, scheduled_at: today}
442 {:ok, _} = ScheduledActivity.create(user, attrs)
443 {:ok, _} = ScheduledActivity.create(user, attrs)
444 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
445
446 conn =
447 conn
448 |> put_req_header("content-type", "application/json")
449 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
450
451 assert %{"error" => "total limit exceeded"} == json_response_and_validate_schema(conn, 422)
452 end
453 end
454
455 describe "posting polls" do
456 setup do: oauth_access(["write:statuses"])
457
458 test "posting a poll", %{conn: conn} do
459 time = NaiveDateTime.utc_now()
460
461 conn =
462 conn
463 |> put_req_header("content-type", "application/json")
464 |> post("/api/v1/statuses", %{
465 "status" => "Who is the #bestgrill?",
466 "poll" => %{
467 "options" => ["Rei", "Asuka", "Misato"],
468 "expires_in" => 420
469 }
470 })
471
472 response = json_response_and_validate_schema(conn, 200)
473
474 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
475 title in ["Rei", "Asuka", "Misato"]
476 end)
477
478 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
479 refute response["poll"]["expred"]
480
481 question = Object.get_by_id(response["poll"]["id"])
482
483 # closed contains utc timezone
484 assert question.data["closed"] =~ "Z"
485 end
486
487 test "option limit is enforced", %{conn: conn} do
488 limit = Config.get([:instance, :poll_limits, :max_options])
489
490 conn =
491 conn
492 |> put_req_header("content-type", "application/json")
493 |> post("/api/v1/statuses", %{
494 "status" => "desu~",
495 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
496 })
497
498 %{"error" => error} = json_response_and_validate_schema(conn, 422)
499 assert error == "Poll can't contain more than #{limit} options"
500 end
501
502 test "option character limit is enforced", %{conn: conn} do
503 limit = Config.get([:instance, :poll_limits, :max_option_chars])
504
505 conn =
506 conn
507 |> put_req_header("content-type", "application/json")
508 |> post("/api/v1/statuses", %{
509 "status" => "...",
510 "poll" => %{
511 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
512 "expires_in" => 1
513 }
514 })
515
516 %{"error" => error} = json_response_and_validate_schema(conn, 422)
517 assert error == "Poll options cannot be longer than #{limit} characters each"
518 end
519
520 test "minimal date limit is enforced", %{conn: conn} do
521 limit = Config.get([:instance, :poll_limits, :min_expiration])
522
523 conn =
524 conn
525 |> put_req_header("content-type", "application/json")
526 |> post("/api/v1/statuses", %{
527 "status" => "imagine arbitrary limits",
528 "poll" => %{
529 "options" => ["this post was made by pleroma gang"],
530 "expires_in" => limit - 1
531 }
532 })
533
534 %{"error" => error} = json_response_and_validate_schema(conn, 422)
535 assert error == "Expiration date is too soon"
536 end
537
538 test "maximum date limit is enforced", %{conn: conn} do
539 limit = Config.get([:instance, :poll_limits, :max_expiration])
540
541 conn =
542 conn
543 |> put_req_header("content-type", "application/json")
544 |> post("/api/v1/statuses", %{
545 "status" => "imagine arbitrary limits",
546 "poll" => %{
547 "options" => ["this post was made by pleroma gang"],
548 "expires_in" => limit + 1
549 }
550 })
551
552 %{"error" => error} = json_response_and_validate_schema(conn, 422)
553 assert error == "Expiration date is too far in the future"
554 end
555 end
556
557 test "get a status" do
558 %{conn: conn} = oauth_access(["read:statuses"])
559 activity = insert(:note_activity)
560
561 conn = get(conn, "/api/v1/statuses/#{activity.id}")
562
563 assert %{"id" => id} = json_response_and_validate_schema(conn, 200)
564 assert id == to_string(activity.id)
565 end
566
567 defp local_and_remote_activities do
568 local = insert(:note_activity)
569 remote = insert(:note_activity, local: false)
570 {:ok, local: local, remote: remote}
571 end
572
573 describe "status with restrict unauthenticated activities for local and remote" do
574 setup do: local_and_remote_activities()
575
576 setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
577
578 setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
579
580 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
581 res_conn = get(conn, "/api/v1/statuses/#{local.id}")
582
583 assert json_response_and_validate_schema(res_conn, :not_found) == %{
584 "error" => "Record not found"
585 }
586
587 res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
588
589 assert json_response_and_validate_schema(res_conn, :not_found) == %{
590 "error" => "Record not found"
591 }
592 end
593
594 test "if user is authenticated", %{local: local, remote: remote} do
595 %{conn: conn} = oauth_access(["read"])
596 res_conn = get(conn, "/api/v1/statuses/#{local.id}")
597 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
598
599 res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
600 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
601 end
602 end
603
604 describe "status with restrict unauthenticated activities for local" do
605 setup do: local_and_remote_activities()
606
607 setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
608
609 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
610 res_conn = get(conn, "/api/v1/statuses/#{local.id}")
611
612 assert json_response_and_validate_schema(res_conn, :not_found) == %{
613 "error" => "Record not found"
614 }
615
616 res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
617 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
618 end
619
620 test "if user is authenticated", %{local: local, remote: remote} do
621 %{conn: conn} = oauth_access(["read"])
622 res_conn = get(conn, "/api/v1/statuses/#{local.id}")
623 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
624
625 res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
626 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
627 end
628 end
629
630 describe "status with restrict unauthenticated activities for remote" do
631 setup do: local_and_remote_activities()
632
633 setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
634
635 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
636 res_conn = get(conn, "/api/v1/statuses/#{local.id}")
637 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
638
639 res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
640
641 assert json_response_and_validate_schema(res_conn, :not_found) == %{
642 "error" => "Record not found"
643 }
644 end
645
646 test "if user is authenticated", %{local: local, remote: remote} do
647 %{conn: conn} = oauth_access(["read"])
648 res_conn = get(conn, "/api/v1/statuses/#{local.id}")
649 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
650
651 res_conn = get(conn, "/api/v1/statuses/#{remote.id}")
652 assert %{"id" => _} = json_response_and_validate_schema(res_conn, 200)
653 end
654 end
655
656 test "getting a status that doesn't exist returns 404" do
657 %{conn: conn} = oauth_access(["read:statuses"])
658 activity = insert(:note_activity)
659
660 conn = get(conn, "/api/v1/statuses/#{String.downcase(activity.id)}")
661
662 assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
663 end
664
665 test "get a direct status" do
666 %{user: user, conn: conn} = oauth_access(["read:statuses"])
667 other_user = insert(:user)
668
669 {:ok, activity} =
670 CommonAPI.post(user, %{status: "@#{other_user.nickname}", visibility: "direct"})
671
672 conn =
673 conn
674 |> assign(:user, user)
675 |> get("/api/v1/statuses/#{activity.id}")
676
677 [participation] = Participation.for_user(user)
678
679 res = json_response_and_validate_schema(conn, 200)
680 assert res["pleroma"]["direct_conversation_id"] == participation.id
681 end
682
683 test "get statuses by IDs" do
684 %{conn: conn} = oauth_access(["read:statuses"])
685 %{id: id1} = insert(:note_activity)
686 %{id: id2} = insert(:note_activity)
687
688 query_string = "ids[]=#{id1}&ids[]=#{id2}"
689 conn = get(conn, "/api/v1/statuses/?#{query_string}")
690
691 assert [%{"id" => ^id1}, %{"id" => ^id2}] =
692 Enum.sort_by(json_response_and_validate_schema(conn, :ok), & &1["id"])
693 end
694
695 describe "getting statuses by ids with restricted unauthenticated for local and remote" do
696 setup do: local_and_remote_activities()
697
698 setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
699
700 setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
701
702 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
703 res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
704
705 assert json_response_and_validate_schema(res_conn, 200) == []
706 end
707
708 test "if user is authenticated", %{local: local, remote: remote} do
709 %{conn: conn} = oauth_access(["read"])
710
711 res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
712
713 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
714 end
715 end
716
717 describe "getting statuses by ids with restricted unauthenticated for local" do
718 setup do: local_and_remote_activities()
719
720 setup do: clear_config([:restrict_unauthenticated, :activities, :local], true)
721
722 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
723 res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
724
725 remote_id = remote.id
726 assert [%{"id" => ^remote_id}] = json_response_and_validate_schema(res_conn, 200)
727 end
728
729 test "if user is authenticated", %{local: local, remote: remote} do
730 %{conn: conn} = oauth_access(["read"])
731
732 res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
733
734 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
735 end
736 end
737
738 describe "getting statuses by ids with restricted unauthenticated for remote" do
739 setup do: local_and_remote_activities()
740
741 setup do: clear_config([:restrict_unauthenticated, :activities, :remote], true)
742
743 test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
744 res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
745
746 local_id = local.id
747 assert [%{"id" => ^local_id}] = json_response_and_validate_schema(res_conn, 200)
748 end
749
750 test "if user is authenticated", %{local: local, remote: remote} do
751 %{conn: conn} = oauth_access(["read"])
752
753 res_conn = get(conn, "/api/v1/statuses?ids[]=#{local.id}&ids[]=#{remote.id}")
754
755 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
756 end
757 end
758
759 describe "deleting a status" do
760 test "when you created it" do
761 %{user: author, conn: conn} = oauth_access(["write:statuses"])
762 activity = insert(:note_activity, user: author)
763
764 conn =
765 conn
766 |> assign(:user, author)
767 |> delete("/api/v1/statuses/#{activity.id}")
768
769 assert %{} = json_response_and_validate_schema(conn, 200)
770
771 refute Activity.get_by_id(activity.id)
772 end
773
774 test "when it doesn't exist" do
775 %{user: author, conn: conn} = oauth_access(["write:statuses"])
776 activity = insert(:note_activity, user: author)
777
778 conn =
779 conn
780 |> assign(:user, author)
781 |> delete("/api/v1/statuses/#{String.downcase(activity.id)}")
782
783 assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404)
784 end
785
786 test "when you didn't create it" do
787 %{conn: conn} = oauth_access(["write:statuses"])
788 activity = insert(:note_activity)
789
790 conn = delete(conn, "/api/v1/statuses/#{activity.id}")
791
792 assert %{"error" => _} = json_response_and_validate_schema(conn, 403)
793
794 assert Activity.get_by_id(activity.id) == activity
795 end
796
797 test "when you're an admin or moderator", %{conn: conn} do
798 activity1 = insert(:note_activity)
799 activity2 = insert(:note_activity)
800 admin = insert(:user, is_admin: true)
801 moderator = insert(:user, is_moderator: true)
802
803 res_conn =
804 conn
805 |> assign(:user, admin)
806 |> assign(:token, insert(:oauth_token, user: admin, scopes: ["write:statuses"]))
807 |> delete("/api/v1/statuses/#{activity1.id}")
808
809 assert %{} = json_response_and_validate_schema(res_conn, 200)
810
811 res_conn =
812 conn
813 |> assign(:user, moderator)
814 |> assign(:token, insert(:oauth_token, user: moderator, scopes: ["write:statuses"]))
815 |> delete("/api/v1/statuses/#{activity2.id}")
816
817 assert %{} = json_response_and_validate_schema(res_conn, 200)
818
819 refute Activity.get_by_id(activity1.id)
820 refute Activity.get_by_id(activity2.id)
821 end
822 end
823
824 describe "reblogging" do
825 setup do: oauth_access(["write:statuses"])
826
827 test "reblogs and returns the reblogged status", %{conn: conn} do
828 activity = insert(:note_activity)
829
830 conn =
831 conn
832 |> put_req_header("content-type", "application/json")
833 |> post("/api/v1/statuses/#{activity.id}/reblog")
834
835 assert %{
836 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
837 "reblogged" => true
838 } = json_response_and_validate_schema(conn, 200)
839
840 assert to_string(activity.id) == id
841 end
842
843 test "returns 404 if the reblogged status doesn't exist", %{conn: conn} do
844 activity = insert(:note_activity)
845
846 conn =
847 conn
848 |> put_req_header("content-type", "application/json")
849 |> post("/api/v1/statuses/#{String.downcase(activity.id)}/reblog")
850
851 assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
852 end
853
854 test "reblogs privately and returns the reblogged status", %{conn: conn} do
855 activity = insert(:note_activity)
856
857 conn =
858 conn
859 |> put_req_header("content-type", "application/json")
860 |> post(
861 "/api/v1/statuses/#{activity.id}/reblog",
862 %{"visibility" => "private"}
863 )
864
865 assert %{
866 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
867 "reblogged" => true,
868 "visibility" => "private"
869 } = json_response_and_validate_schema(conn, 200)
870
871 assert to_string(activity.id) == id
872 end
873
874 test "reblogged status for another user" do
875 activity = insert(:note_activity)
876 user1 = insert(:user)
877 user2 = insert(:user)
878 user3 = insert(:user)
879 {:ok, _} = CommonAPI.favorite(user2, activity.id)
880 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
881 {:ok, reblog_activity1} = CommonAPI.repeat(activity.id, user1)
882 {:ok, _} = CommonAPI.repeat(activity.id, user2)
883
884 conn_res =
885 build_conn()
886 |> assign(:user, user3)
887 |> assign(:token, insert(:oauth_token, user: user3, scopes: ["read:statuses"]))
888 |> get("/api/v1/statuses/#{reblog_activity1.id}")
889
890 assert %{
891 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
892 "reblogged" => false,
893 "favourited" => false,
894 "bookmarked" => false
895 } = json_response_and_validate_schema(conn_res, 200)
896
897 conn_res =
898 build_conn()
899 |> assign(:user, user2)
900 |> assign(:token, insert(:oauth_token, user: user2, scopes: ["read:statuses"]))
901 |> get("/api/v1/statuses/#{reblog_activity1.id}")
902
903 assert %{
904 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
905 "reblogged" => true,
906 "favourited" => true,
907 "bookmarked" => true
908 } = json_response_and_validate_schema(conn_res, 200)
909
910 assert to_string(activity.id) == id
911 end
912 end
913
914 describe "unreblogging" do
915 setup do: oauth_access(["write:statuses"])
916
917 test "unreblogs and returns the unreblogged status", %{user: user, conn: conn} do
918 activity = insert(:note_activity)
919
920 {:ok, _} = CommonAPI.repeat(activity.id, user)
921
922 conn =
923 conn
924 |> put_req_header("content-type", "application/json")
925 |> post("/api/v1/statuses/#{activity.id}/unreblog")
926
927 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} =
928 json_response_and_validate_schema(conn, 200)
929
930 assert to_string(activity.id) == id
931 end
932
933 test "returns 404 error when activity does not exist", %{conn: conn} do
934 conn =
935 conn
936 |> put_req_header("content-type", "application/json")
937 |> post("/api/v1/statuses/foo/unreblog")
938
939 assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
940 end
941 end
942
943 describe "favoriting" do
944 setup do: oauth_access(["write:favourites"])
945
946 test "favs a status and returns it", %{conn: conn} do
947 activity = insert(:note_activity)
948
949 conn =
950 conn
951 |> put_req_header("content-type", "application/json")
952 |> post("/api/v1/statuses/#{activity.id}/favourite")
953
954 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
955 json_response_and_validate_schema(conn, 200)
956
957 assert to_string(activity.id) == id
958 end
959
960 test "favoriting twice will just return 200", %{conn: conn} do
961 activity = insert(:note_activity)
962
963 conn
964 |> put_req_header("content-type", "application/json")
965 |> post("/api/v1/statuses/#{activity.id}/favourite")
966
967 assert conn
968 |> put_req_header("content-type", "application/json")
969 |> post("/api/v1/statuses/#{activity.id}/favourite")
970 |> json_response_and_validate_schema(200)
971 end
972
973 test "returns 404 error for a wrong id", %{conn: conn} do
974 conn =
975 conn
976 |> put_req_header("content-type", "application/json")
977 |> post("/api/v1/statuses/1/favourite")
978
979 assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
980 end
981 end
982
983 describe "unfavoriting" do
984 setup do: oauth_access(["write:favourites"])
985
986 test "unfavorites a status and returns it", %{user: user, conn: conn} do
987 activity = insert(:note_activity)
988
989 {:ok, _} = CommonAPI.favorite(user, activity.id)
990
991 conn =
992 conn
993 |> put_req_header("content-type", "application/json")
994 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
995
996 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
997 json_response_and_validate_schema(conn, 200)
998
999 assert to_string(activity.id) == id
1000 end
1001
1002 test "returns 404 error for a wrong id", %{conn: conn} do
1003 conn =
1004 conn
1005 |> put_req_header("content-type", "application/json")
1006 |> post("/api/v1/statuses/1/unfavourite")
1007
1008 assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
1009 end
1010 end
1011
1012 describe "pinned statuses" do
1013 setup do: oauth_access(["write:accounts"])
1014
1015 setup %{user: user} do
1016 {:ok, activity} = CommonAPI.post(user, %{status: "HI!!!"})
1017
1018 %{activity: activity}
1019 end
1020
1021 setup do: clear_config([:instance, :max_pinned_statuses], 1)
1022
1023 test "pin status", %{conn: conn, user: user, activity: activity} do
1024 id_str = to_string(activity.id)
1025
1026 assert %{"id" => ^id_str, "pinned" => true} =
1027 conn
1028 |> put_req_header("content-type", "application/json")
1029 |> post("/api/v1/statuses/#{activity.id}/pin")
1030 |> json_response_and_validate_schema(200)
1031
1032 assert [%{"id" => ^id_str, "pinned" => true}] =
1033 conn
1034 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1035 |> json_response_and_validate_schema(200)
1036 end
1037
1038 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
1039 {:ok, dm} = CommonAPI.post(user, %{status: "test", visibility: "direct"})
1040
1041 conn =
1042 conn
1043 |> put_req_header("content-type", "application/json")
1044 |> post("/api/v1/statuses/#{dm.id}/pin")
1045
1046 assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not pin"}
1047 end
1048
1049 test "unpin status", %{conn: conn, user: user, activity: activity} do
1050 {:ok, _} = CommonAPI.pin(activity.id, user)
1051 user = refresh_record(user)
1052
1053 id_str = to_string(activity.id)
1054
1055 assert %{"id" => ^id_str, "pinned" => false} =
1056 conn
1057 |> assign(:user, user)
1058 |> post("/api/v1/statuses/#{activity.id}/unpin")
1059 |> json_response_and_validate_schema(200)
1060
1061 assert [] =
1062 conn
1063 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1064 |> json_response_and_validate_schema(200)
1065 end
1066
1067 test "/unpin: returns 400 error when activity is not exist", %{conn: conn} do
1068 conn =
1069 conn
1070 |> put_req_header("content-type", "application/json")
1071 |> post("/api/v1/statuses/1/unpin")
1072
1073 assert json_response_and_validate_schema(conn, 400) == %{"error" => "Could not unpin"}
1074 end
1075
1076 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
1077 {:ok, activity_two} = CommonAPI.post(user, %{status: "HI!!!"})
1078
1079 id_str_one = to_string(activity_one.id)
1080
1081 assert %{"id" => ^id_str_one, "pinned" => true} =
1082 conn
1083 |> put_req_header("content-type", "application/json")
1084 |> post("/api/v1/statuses/#{id_str_one}/pin")
1085 |> json_response_and_validate_schema(200)
1086
1087 user = refresh_record(user)
1088
1089 assert %{"error" => "You have already pinned the maximum number of statuses"} =
1090 conn
1091 |> assign(:user, user)
1092 |> post("/api/v1/statuses/#{activity_two.id}/pin")
1093 |> json_response_and_validate_schema(400)
1094 end
1095 end
1096
1097 describe "cards" do
1098 setup do
1099 Config.put([:rich_media, :enabled], true)
1100
1101 oauth_access(["read:statuses"])
1102 end
1103
1104 test "returns rich-media card", %{conn: conn, user: user} do
1105 Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
1106
1107 {:ok, activity} = CommonAPI.post(user, %{status: "https://example.com/ogp"})
1108
1109 card_data = %{
1110 "image" => "http://ia.media-imdb.com/images/rock.jpg",
1111 "provider_name" => "example.com",
1112 "provider_url" => "https://example.com",
1113 "title" => "The Rock",
1114 "type" => "link",
1115 "url" => "https://example.com/ogp",
1116 "description" =>
1117 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
1118 "pleroma" => %{
1119 "opengraph" => %{
1120 "image" => "http://ia.media-imdb.com/images/rock.jpg",
1121 "title" => "The Rock",
1122 "type" => "video.movie",
1123 "url" => "https://example.com/ogp",
1124 "description" =>
1125 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
1126 }
1127 }
1128 }
1129
1130 response =
1131 conn
1132 |> get("/api/v1/statuses/#{activity.id}/card")
1133 |> json_response_and_validate_schema(200)
1134
1135 assert response == card_data
1136
1137 # works with private posts
1138 {:ok, activity} =
1139 CommonAPI.post(user, %{status: "https://example.com/ogp", visibility: "direct"})
1140
1141 response_two =
1142 conn
1143 |> get("/api/v1/statuses/#{activity.id}/card")
1144 |> json_response_and_validate_schema(200)
1145
1146 assert response_two == card_data
1147 end
1148
1149 test "replaces missing description with an empty string", %{conn: conn, user: user} do
1150 Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
1151
1152 {:ok, activity} = CommonAPI.post(user, %{status: "https://example.com/ogp-missing-data"})
1153
1154 response =
1155 conn
1156 |> get("/api/v1/statuses/#{activity.id}/card")
1157 |> json_response_and_validate_schema(:ok)
1158
1159 assert response == %{
1160 "type" => "link",
1161 "title" => "Pleroma",
1162 "description" => "",
1163 "image" => nil,
1164 "provider_name" => "example.com",
1165 "provider_url" => "https://example.com",
1166 "url" => "https://example.com/ogp-missing-data",
1167 "pleroma" => %{
1168 "opengraph" => %{
1169 "title" => "Pleroma",
1170 "type" => "website",
1171 "url" => "https://example.com/ogp-missing-data"
1172 }
1173 }
1174 }
1175 end
1176 end
1177
1178 test "bookmarks" do
1179 bookmarks_uri = "/api/v1/bookmarks"
1180
1181 %{conn: conn} = oauth_access(["write:bookmarks", "read:bookmarks"])
1182 author = insert(:user)
1183
1184 {:ok, activity1} = CommonAPI.post(author, %{status: "heweoo?"})
1185 {:ok, activity2} = CommonAPI.post(author, %{status: "heweoo!"})
1186
1187 response1 =
1188 conn
1189 |> put_req_header("content-type", "application/json")
1190 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
1191
1192 assert json_response_and_validate_schema(response1, 200)["bookmarked"] == true
1193
1194 response2 =
1195 conn
1196 |> put_req_header("content-type", "application/json")
1197 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
1198
1199 assert json_response_and_validate_schema(response2, 200)["bookmarked"] == true
1200
1201 bookmarks = get(conn, bookmarks_uri)
1202
1203 assert [
1204 json_response_and_validate_schema(response2, 200),
1205 json_response_and_validate_schema(response1, 200)
1206 ] ==
1207 json_response_and_validate_schema(bookmarks, 200)
1208
1209 response1 =
1210 conn
1211 |> put_req_header("content-type", "application/json")
1212 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
1213
1214 assert json_response_and_validate_schema(response1, 200)["bookmarked"] == false
1215
1216 bookmarks = get(conn, bookmarks_uri)
1217
1218 assert [json_response_and_validate_schema(response2, 200)] ==
1219 json_response_and_validate_schema(bookmarks, 200)
1220 end
1221
1222 describe "conversation muting" do
1223 setup do: oauth_access(["write:mutes"])
1224
1225 setup do
1226 post_user = insert(:user)
1227 {:ok, activity} = CommonAPI.post(post_user, %{status: "HIE"})
1228 %{activity: activity}
1229 end
1230
1231 test "mute conversation", %{conn: conn, activity: activity} do
1232 id_str = to_string(activity.id)
1233
1234 assert %{"id" => ^id_str, "muted" => true} =
1235 conn
1236 |> put_req_header("content-type", "application/json")
1237 |> post("/api/v1/statuses/#{activity.id}/mute")
1238 |> json_response_and_validate_schema(200)
1239 end
1240
1241 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
1242 {:ok, _} = CommonAPI.add_mute(user, activity)
1243
1244 conn =
1245 conn
1246 |> put_req_header("content-type", "application/json")
1247 |> post("/api/v1/statuses/#{activity.id}/mute")
1248
1249 assert json_response_and_validate_schema(conn, 400) == %{
1250 "error" => "conversation is already muted"
1251 }
1252 end
1253
1254 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
1255 {:ok, _} = CommonAPI.add_mute(user, activity)
1256
1257 id_str = to_string(activity.id)
1258
1259 assert %{"id" => ^id_str, "muted" => false} =
1260 conn
1261 # |> assign(:user, user)
1262 |> post("/api/v1/statuses/#{activity.id}/unmute")
1263 |> json_response_and_validate_schema(200)
1264 end
1265 end
1266
1267 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
1268 user1 = insert(:user)
1269 user2 = insert(:user)
1270 user3 = insert(:user)
1271
1272 {:ok, replied_to} = CommonAPI.post(user1, %{status: "cofe"})
1273
1274 # Reply to status from another user
1275 conn1 =
1276 conn
1277 |> assign(:user, user2)
1278 |> assign(:token, insert(:oauth_token, user: user2, scopes: ["write:statuses"]))
1279 |> put_req_header("content-type", "application/json")
1280 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
1281
1282 assert %{"content" => "xD", "id" => id} = json_response_and_validate_schema(conn1, 200)
1283
1284 activity = Activity.get_by_id_with_object(id)
1285
1286 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
1287 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
1288
1289 # Reblog from the third user
1290 conn2 =
1291 conn
1292 |> assign(:user, user3)
1293 |> assign(:token, insert(:oauth_token, user: user3, scopes: ["write:statuses"]))
1294 |> put_req_header("content-type", "application/json")
1295 |> post("/api/v1/statuses/#{activity.id}/reblog")
1296
1297 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
1298 json_response_and_validate_schema(conn2, 200)
1299
1300 assert to_string(activity.id) == id
1301
1302 # Getting third user status
1303 conn3 =
1304 conn
1305 |> assign(:user, user3)
1306 |> assign(:token, insert(:oauth_token, user: user3, scopes: ["read:statuses"]))
1307 |> get("api/v1/timelines/home")
1308
1309 [reblogged_activity] = json_response(conn3, 200)
1310
1311 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
1312
1313 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
1314 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
1315 end
1316
1317 describe "GET /api/v1/statuses/:id/favourited_by" do
1318 setup do: oauth_access(["read:accounts"])
1319
1320 setup %{user: user} do
1321 {:ok, activity} = CommonAPI.post(user, %{status: "test"})
1322
1323 %{activity: activity}
1324 end
1325
1326 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
1327 other_user = insert(:user)
1328 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
1329
1330 response =
1331 conn
1332 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1333 |> json_response_and_validate_schema(:ok)
1334
1335 [%{"id" => id}] = response
1336
1337 assert id == other_user.id
1338 end
1339
1340 test "returns empty array when status has not been favorited yet", %{
1341 conn: conn,
1342 activity: activity
1343 } do
1344 response =
1345 conn
1346 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1347 |> json_response_and_validate_schema(:ok)
1348
1349 assert Enum.empty?(response)
1350 end
1351
1352 test "does not return users who have favorited the status but are blocked", %{
1353 conn: %{assigns: %{user: user}} = conn,
1354 activity: activity
1355 } do
1356 other_user = insert(:user)
1357 {:ok, _user_relationship} = User.block(user, other_user)
1358
1359 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
1360
1361 response =
1362 conn
1363 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1364 |> json_response_and_validate_schema(:ok)
1365
1366 assert Enum.empty?(response)
1367 end
1368
1369 test "does not fail on an unauthenticated request", %{activity: activity} do
1370 other_user = insert(:user)
1371 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
1372
1373 response =
1374 build_conn()
1375 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
1376 |> json_response_and_validate_schema(:ok)
1377
1378 [%{"id" => id}] = response
1379 assert id == other_user.id
1380 end
1381
1382 test "requires authentication for private posts", %{user: user} do
1383 other_user = insert(:user)
1384
1385 {:ok, activity} =
1386 CommonAPI.post(user, %{
1387 status: "@#{other_user.nickname} wanna get some #cofe together?",
1388 visibility: "direct"
1389 })
1390
1391 {:ok, _} = CommonAPI.favorite(other_user, activity.id)
1392
1393 favourited_by_url = "/api/v1/statuses/#{activity.id}/favourited_by"
1394
1395 build_conn()
1396 |> get(favourited_by_url)
1397 |> json_response_and_validate_schema(404)
1398
1399 conn =
1400 build_conn()
1401 |> assign(:user, other_user)
1402 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
1403
1404 conn
1405 |> assign(:token, nil)
1406 |> get(favourited_by_url)
1407 |> json_response_and_validate_schema(404)
1408
1409 response =
1410 conn
1411 |> get(favourited_by_url)
1412 |> json_response_and_validate_schema(200)
1413
1414 [%{"id" => id}] = response
1415 assert id == other_user.id
1416 end
1417 end
1418
1419 describe "GET /api/v1/statuses/:id/reblogged_by" do
1420 setup do: oauth_access(["read:accounts"])
1421
1422 setup %{user: user} do
1423 {:ok, activity} = CommonAPI.post(user, %{status: "test"})
1424
1425 %{activity: activity}
1426 end
1427
1428 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
1429 other_user = insert(:user)
1430 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
1431
1432 response =
1433 conn
1434 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1435 |> json_response_and_validate_schema(:ok)
1436
1437 [%{"id" => id}] = response
1438
1439 assert id == other_user.id
1440 end
1441
1442 test "returns empty array when status has not been reblogged yet", %{
1443 conn: conn,
1444 activity: activity
1445 } do
1446 response =
1447 conn
1448 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1449 |> json_response_and_validate_schema(:ok)
1450
1451 assert Enum.empty?(response)
1452 end
1453
1454 test "does not return users who have reblogged the status but are blocked", %{
1455 conn: %{assigns: %{user: user}} = conn,
1456 activity: activity
1457 } do
1458 other_user = insert(:user)
1459 {:ok, _user_relationship} = User.block(user, other_user)
1460
1461 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
1462
1463 response =
1464 conn
1465 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1466 |> json_response_and_validate_schema(:ok)
1467
1468 assert Enum.empty?(response)
1469 end
1470
1471 test "does not return users who have reblogged the status privately", %{
1472 conn: conn
1473 } do
1474 other_user = insert(:user)
1475 {:ok, activity} = CommonAPI.post(other_user, %{status: "my secret post"})
1476
1477 {:ok, _} = CommonAPI.repeat(activity.id, other_user, %{visibility: "private"})
1478
1479 response =
1480 conn
1481 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1482 |> json_response_and_validate_schema(:ok)
1483
1484 assert Enum.empty?(response)
1485 end
1486
1487 test "does not fail on an unauthenticated request", %{activity: activity} do
1488 other_user = insert(:user)
1489 {:ok, _} = CommonAPI.repeat(activity.id, other_user)
1490
1491 response =
1492 build_conn()
1493 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1494 |> json_response_and_validate_schema(:ok)
1495
1496 [%{"id" => id}] = response
1497 assert id == other_user.id
1498 end
1499
1500 test "requires authentication for private posts", %{user: user} do
1501 other_user = insert(:user)
1502
1503 {:ok, activity} =
1504 CommonAPI.post(user, %{
1505 status: "@#{other_user.nickname} wanna get some #cofe together?",
1506 visibility: "direct"
1507 })
1508
1509 build_conn()
1510 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1511 |> json_response_and_validate_schema(404)
1512
1513 response =
1514 build_conn()
1515 |> assign(:user, other_user)
1516 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:accounts"]))
1517 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
1518 |> json_response_and_validate_schema(200)
1519
1520 assert [] == response
1521 end
1522 end
1523
1524 test "context" do
1525 user = insert(:user)
1526
1527 {:ok, %{id: id1}} = CommonAPI.post(user, %{status: "1"})
1528 {:ok, %{id: id2}} = CommonAPI.post(user, %{status: "2", in_reply_to_status_id: id1})
1529 {:ok, %{id: id3}} = CommonAPI.post(user, %{status: "3", in_reply_to_status_id: id2})
1530 {:ok, %{id: id4}} = CommonAPI.post(user, %{status: "4", in_reply_to_status_id: id3})
1531 {:ok, %{id: id5}} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4})
1532
1533 response =
1534 build_conn()
1535 |> get("/api/v1/statuses/#{id3}/context")
1536 |> json_response_and_validate_schema(:ok)
1537
1538 assert %{
1539 "ancestors" => [%{"id" => ^id1}, %{"id" => ^id2}],
1540 "descendants" => [%{"id" => ^id4}, %{"id" => ^id5}]
1541 } = response
1542 end
1543
1544 test "returns the favorites of a user" do
1545 %{user: user, conn: conn} = oauth_access(["read:favourites"])
1546 other_user = insert(:user)
1547
1548 {:ok, _} = CommonAPI.post(other_user, %{status: "bla"})
1549 {:ok, activity} = CommonAPI.post(other_user, %{status: "traps are happy"})
1550
1551 {:ok, _} = CommonAPI.favorite(user, activity.id)
1552
1553 first_conn = get(conn, "/api/v1/favourites")
1554
1555 assert [status] = json_response_and_validate_schema(first_conn, 200)
1556 assert status["id"] == to_string(activity.id)
1557
1558 assert [{"link", _link_header}] =
1559 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1560
1561 # Honours query params
1562 {:ok, second_activity} =
1563 CommonAPI.post(other_user, %{
1564 status: "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1565 })
1566
1567 {:ok, _} = CommonAPI.favorite(user, second_activity.id)
1568
1569 last_like = status["id"]
1570
1571 second_conn = get(conn, "/api/v1/favourites?since_id=#{last_like}")
1572
1573 assert [second_status] = json_response_and_validate_schema(second_conn, 200)
1574 assert second_status["id"] == to_string(second_activity.id)
1575
1576 third_conn = get(conn, "/api/v1/favourites?limit=0")
1577
1578 assert [] = json_response_and_validate_schema(third_conn, 200)
1579 end
1580
1581 test "expires_at is nil for another user" do
1582 %{conn: conn, user: user} = oauth_access(["read:statuses"])
1583 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", expires_in: 1_000_000})
1584
1585 expires_at =
1586 activity.id
1587 |> ActivityExpiration.get_by_activity_id()
1588 |> Map.get(:scheduled_at)
1589 |> NaiveDateTime.to_iso8601()
1590
1591 assert %{"pleroma" => %{"expires_at" => ^expires_at}} =
1592 conn
1593 |> get("/api/v1/statuses/#{activity.id}")
1594 |> json_response_and_validate_schema(:ok)
1595
1596 %{conn: conn} = oauth_access(["read:statuses"])
1597
1598 assert %{"pleroma" => %{"expires_at" => nil}} =
1599 conn
1600 |> get("/api/v1/statuses/#{activity.id}")
1601 |> json_response_and_validate_schema(:ok)
1602 end
1603 end