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