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