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