Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms
[akkoma] / test / web / common_api / common_api_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.CommonAPITest do
6 use Pleroma.DataCase
7 alias Pleroma.Activity
8 alias Pleroma.Chat
9 alias Pleroma.Conversation.Participation
10 alias Pleroma.Object
11 alias Pleroma.User
12 alias Pleroma.Web.ActivityPub.ActivityPub
13 alias Pleroma.Web.ActivityPub.Visibility
14 alias Pleroma.Web.AdminAPI.AccountView
15 alias Pleroma.Web.CommonAPI
16
17 import Pleroma.Factory
18
19 require Pleroma.Constants
20
21 setup do: clear_config([:instance, :safe_dm_mentions])
22 setup do: clear_config([:instance, :limit])
23 setup do: clear_config([:instance, :max_pinned_statuses])
24
25 describe "posting chat messages" do
26 setup do: clear_config([:instance, :chat_limit])
27
28 test "it posts a chat message" do
29 author = insert(:user)
30 recipient = insert(:user)
31
32 {:ok, activity} =
33 CommonAPI.post_chat_message(
34 author,
35 recipient,
36 "a test message <script>alert('uuu')</script>"
37 )
38
39 assert activity.data["type"] == "Create"
40 assert activity.local
41 object = Object.normalize(activity)
42
43 assert object.data["type"] == "ChatMessage"
44 assert object.data["to"] == [recipient.ap_id]
45
46 assert object.data["content"] ==
47 "a test message &lt;script&gt;alert(&#39;uuu&#39;)&lt;/script&gt;"
48
49 assert Chat.get(author.id, recipient.ap_id)
50 assert Chat.get(recipient.id, author.ap_id)
51 end
52
53 test "it reject messages over the local limit" do
54 Pleroma.Config.put([:instance, :chat_limit], 2)
55
56 author = insert(:user)
57 recipient = insert(:user)
58
59 {:error, message} =
60 CommonAPI.post_chat_message(
61 author,
62 recipient,
63 "123"
64 )
65
66 assert message == :content_too_long
67 end
68 end
69
70 test "when replying to a conversation / participation, it will set the correct context id even if no explicit reply_to is given" do
71 user = insert(:user)
72 {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
73
74 [participation] = Participation.for_user(user)
75
76 {:ok, convo_reply} =
77 CommonAPI.post(user, %{"status" => ".", "in_reply_to_conversation_id" => participation.id})
78
79 assert Visibility.is_direct?(convo_reply)
80
81 assert activity.data["context"] == convo_reply.data["context"]
82 end
83
84 test "when replying to a conversation / participation, it only mentions the recipients explicitly declared in the participation" do
85 har = insert(:user)
86 jafnhar = insert(:user)
87 tridi = insert(:user)
88
89 {:ok, activity} =
90 CommonAPI.post(har, %{
91 "status" => "@#{jafnhar.nickname} hey",
92 "visibility" => "direct"
93 })
94
95 assert har.ap_id in activity.recipients
96 assert jafnhar.ap_id in activity.recipients
97
98 [participation] = Participation.for_user(har)
99
100 {:ok, activity} =
101 CommonAPI.post(har, %{
102 "status" => "I don't really like @#{tridi.nickname}",
103 "visibility" => "direct",
104 "in_reply_to_status_id" => activity.id,
105 "in_reply_to_conversation_id" => participation.id
106 })
107
108 assert har.ap_id in activity.recipients
109 assert jafnhar.ap_id in activity.recipients
110 refute tridi.ap_id in activity.recipients
111 end
112
113 test "with the safe_dm_mention option set, it does not mention people beyond the initial tags" do
114 har = insert(:user)
115 jafnhar = insert(:user)
116 tridi = insert(:user)
117
118 Pleroma.Config.put([:instance, :safe_dm_mentions], true)
119
120 {:ok, activity} =
121 CommonAPI.post(har, %{
122 "status" => "@#{jafnhar.nickname} hey, i never want to see @#{tridi.nickname} again",
123 "visibility" => "direct"
124 })
125
126 refute tridi.ap_id in activity.recipients
127 assert jafnhar.ap_id in activity.recipients
128 end
129
130 test "it de-duplicates tags" do
131 user = insert(:user)
132 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu #2HU"})
133
134 object = Object.normalize(activity)
135
136 assert object.data["tag"] == ["2hu"]
137 end
138
139 test "it adds emoji in the object" do
140 user = insert(:user)
141 {:ok, activity} = CommonAPI.post(user, %{"status" => ":firefox:"})
142
143 assert Object.normalize(activity).data["emoji"]["firefox"]
144 end
145
146 describe "posting" do
147 test "it supports explicit addressing" do
148 user = insert(:user)
149 user_two = insert(:user)
150 user_three = insert(:user)
151 user_four = insert(:user)
152
153 {:ok, activity} =
154 CommonAPI.post(user, %{
155 "status" =>
156 "Hey, I think @#{user_three.nickname} is ugly. @#{user_four.nickname} is alright though.",
157 "to" => [user_two.nickname, user_four.nickname, "nonexistent"]
158 })
159
160 assert user.ap_id in activity.recipients
161 assert user_two.ap_id in activity.recipients
162 assert user_four.ap_id in activity.recipients
163 refute user_three.ap_id in activity.recipients
164 end
165
166 test "it filters out obviously bad tags when accepting a post as HTML" do
167 user = insert(:user)
168
169 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
170
171 {:ok, activity} =
172 CommonAPI.post(user, %{
173 "status" => post,
174 "content_type" => "text/html"
175 })
176
177 object = Object.normalize(activity)
178
179 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
180 end
181
182 test "it filters out obviously bad tags when accepting a post as Markdown" do
183 user = insert(:user)
184
185 post = "<p><b>2hu</b></p><script>alert('xss')</script>"
186
187 {:ok, activity} =
188 CommonAPI.post(user, %{
189 "status" => post,
190 "content_type" => "text/markdown"
191 })
192
193 object = Object.normalize(activity)
194
195 assert object.data["content"] == "<p><b>2hu</b></p>alert(&#39;xss&#39;)"
196 end
197
198 test "it does not allow replies to direct messages that are not direct messages themselves" do
199 user = insert(:user)
200
201 {:ok, activity} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
202
203 assert {:ok, _} =
204 CommonAPI.post(user, %{
205 "status" => "suya..",
206 "visibility" => "direct",
207 "in_reply_to_status_id" => activity.id
208 })
209
210 Enum.each(["public", "private", "unlisted"], fn visibility ->
211 assert {:error, "The message visibility must be direct"} =
212 CommonAPI.post(user, %{
213 "status" => "suya..",
214 "visibility" => visibility,
215 "in_reply_to_status_id" => activity.id
216 })
217 end)
218 end
219
220 test "it allows to address a list" do
221 user = insert(:user)
222 {:ok, list} = Pleroma.List.create("foo", user)
223
224 {:ok, activity} =
225 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
226
227 assert activity.data["bcc"] == [list.ap_id]
228 assert activity.recipients == [list.ap_id, user.ap_id]
229 assert activity.data["listMessage"] == list.ap_id
230 end
231
232 test "it returns error when status is empty and no attachments" do
233 user = insert(:user)
234
235 assert {:error, "Cannot post an empty status without attachments"} =
236 CommonAPI.post(user, %{"status" => ""})
237 end
238
239 test "it validates character limits are correctly enforced" do
240 Pleroma.Config.put([:instance, :limit], 5)
241
242 user = insert(:user)
243
244 assert {:error, "The status is over the character limit"} =
245 CommonAPI.post(user, %{"status" => "foobar"})
246
247 assert {:ok, activity} = CommonAPI.post(user, %{"status" => "12345"})
248 end
249
250 test "it can handle activities that expire" do
251 user = insert(:user)
252
253 expires_at =
254 NaiveDateTime.utc_now()
255 |> NaiveDateTime.truncate(:second)
256 |> NaiveDateTime.add(1_000_000, :second)
257
258 assert {:ok, activity} =
259 CommonAPI.post(user, %{"status" => "chai", "expires_in" => 1_000_000})
260
261 assert expiration = Pleroma.ActivityExpiration.get_by_activity_id(activity.id)
262 assert expiration.scheduled_at == expires_at
263 end
264 end
265
266 describe "reactions" do
267 test "reacting to a status with an emoji" do
268 user = insert(:user)
269 other_user = insert(:user)
270
271 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
272
273 {:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "👍")
274
275 assert reaction.data["actor"] == user.ap_id
276 assert reaction.data["content"] == "👍"
277
278 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
279
280 {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
281 end
282
283 test "unreacting to a status with an emoji" do
284 user = insert(:user)
285 other_user = insert(:user)
286
287 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
288 {:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "👍")
289
290 {:ok, unreaction, _} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
291
292 assert unreaction.data["type"] == "Undo"
293 assert unreaction.data["object"] == reaction.data["id"]
294 end
295
296 test "repeating a status" do
297 user = insert(:user)
298 other_user = insert(:user)
299
300 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
301
302 {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, user)
303 end
304
305 test "repeating a status privately" do
306 user = insert(:user)
307 other_user = insert(:user)
308
309 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
310
311 {:ok, %Activity{} = announce_activity, _} =
312 CommonAPI.repeat(activity.id, user, %{"visibility" => "private"})
313
314 assert Visibility.is_private?(announce_activity)
315 end
316
317 test "favoriting a status" do
318 user = insert(:user)
319 other_user = insert(:user)
320
321 {:ok, post_activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
322
323 {:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
324 assert data["type"] == "Like"
325 assert data["actor"] == user.ap_id
326 assert data["object"] == post_activity.data["object"]
327 end
328
329 test "retweeting a status twice returns the status" do
330 user = insert(:user)
331 other_user = insert(:user)
332
333 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
334 {:ok, %Activity{} = activity, object} = CommonAPI.repeat(activity.id, user)
335 {:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user)
336 end
337
338 test "favoriting a status twice returns ok, but without the like activity" do
339 user = insert(:user)
340 other_user = insert(:user)
341
342 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
343 {:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
344 assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
345 end
346 end
347
348 describe "pinned statuses" do
349 setup do
350 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
351
352 user = insert(:user)
353 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
354
355 [user: user, activity: activity]
356 end
357
358 test "pin status", %{user: user, activity: activity} do
359 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
360
361 id = activity.id
362 user = refresh_record(user)
363
364 assert %User{pinned_activities: [^id]} = user
365 end
366
367 test "pin poll", %{user: user} do
368 {:ok, activity} =
369 CommonAPI.post(user, %{
370 "status" => "How is fediverse today?",
371 "poll" => %{"options" => ["Absolutely outstanding", "Not good"], "expires_in" => 20}
372 })
373
374 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
375
376 id = activity.id
377 user = refresh_record(user)
378
379 assert %User{pinned_activities: [^id]} = user
380 end
381
382 test "unlisted statuses can be pinned", %{user: user} do
383 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!", "visibility" => "unlisted"})
384 assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
385 end
386
387 test "only self-authored can be pinned", %{activity: activity} do
388 user = insert(:user)
389
390 assert {:error, "Could not pin"} = CommonAPI.pin(activity.id, user)
391 end
392
393 test "max pinned statuses", %{user: user, activity: activity_one} do
394 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
395
396 assert {:ok, ^activity_one} = CommonAPI.pin(activity_one.id, user)
397
398 user = refresh_record(user)
399
400 assert {:error, "You have already pinned the maximum number of statuses"} =
401 CommonAPI.pin(activity_two.id, user)
402 end
403
404 test "unpin status", %{user: user, activity: activity} do
405 {:ok, activity} = CommonAPI.pin(activity.id, user)
406
407 user = refresh_record(user)
408
409 assert {:ok, ^activity} = CommonAPI.unpin(activity.id, user)
410
411 user = refresh_record(user)
412
413 assert %User{pinned_activities: []} = user
414 end
415
416 test "should unpin when deleting a status", %{user: user, activity: activity} do
417 {:ok, activity} = CommonAPI.pin(activity.id, user)
418
419 user = refresh_record(user)
420
421 assert {:ok, _} = CommonAPI.delete(activity.id, user)
422
423 user = refresh_record(user)
424
425 assert %User{pinned_activities: []} = user
426 end
427 end
428
429 describe "mute tests" do
430 setup do
431 user = insert(:user)
432
433 activity = insert(:note_activity)
434
435 [user: user, activity: activity]
436 end
437
438 test "add mute", %{user: user, activity: activity} do
439 {:ok, _} = CommonAPI.add_mute(user, activity)
440 assert CommonAPI.thread_muted?(user, activity)
441 end
442
443 test "remove mute", %{user: user, activity: activity} do
444 CommonAPI.add_mute(user, activity)
445 {:ok, _} = CommonAPI.remove_mute(user, activity)
446 refute CommonAPI.thread_muted?(user, activity)
447 end
448
449 test "check that mutes can't be duplicate", %{user: user, activity: activity} do
450 CommonAPI.add_mute(user, activity)
451 {:error, _} = CommonAPI.add_mute(user, activity)
452 end
453 end
454
455 describe "reports" do
456 test "creates a report" do
457 reporter = insert(:user)
458 target_user = insert(:user)
459
460 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
461
462 reporter_ap_id = reporter.ap_id
463 target_ap_id = target_user.ap_id
464 activity_ap_id = activity.data["id"]
465 comment = "foobar"
466
467 report_data = %{
468 "account_id" => target_user.id,
469 "comment" => comment,
470 "status_ids" => [activity.id]
471 }
472
473 note_obj = %{
474 "type" => "Note",
475 "id" => activity_ap_id,
476 "content" => "foobar",
477 "published" => activity.object.data["published"],
478 "actor" => AccountView.render("show.json", %{user: target_user})
479 }
480
481 assert {:ok, flag_activity} = CommonAPI.report(reporter, report_data)
482
483 assert %Activity{
484 actor: ^reporter_ap_id,
485 data: %{
486 "type" => "Flag",
487 "content" => ^comment,
488 "object" => [^target_ap_id, ^note_obj],
489 "state" => "open"
490 }
491 } = flag_activity
492 end
493
494 test "updates report state" do
495 [reporter, target_user] = insert_pair(:user)
496 activity = insert(:note_activity, user: target_user)
497
498 {:ok, %Activity{id: report_id}} =
499 CommonAPI.report(reporter, %{
500 "account_id" => target_user.id,
501 "comment" => "I feel offended",
502 "status_ids" => [activity.id]
503 })
504
505 {:ok, report} = CommonAPI.update_report_state(report_id, "resolved")
506
507 assert report.data["state"] == "resolved"
508
509 [reported_user, activity_id] = report.data["object"]
510
511 assert reported_user == target_user.ap_id
512 assert activity_id == activity.data["id"]
513 end
514
515 test "does not update report state when state is unsupported" do
516 [reporter, target_user] = insert_pair(:user)
517 activity = insert(:note_activity, user: target_user)
518
519 {:ok, %Activity{id: report_id}} =
520 CommonAPI.report(reporter, %{
521 "account_id" => target_user.id,
522 "comment" => "I feel offended",
523 "status_ids" => [activity.id]
524 })
525
526 assert CommonAPI.update_report_state(report_id, "test") == {:error, "Unsupported state"}
527 end
528
529 test "updates state of multiple reports" do
530 [reporter, target_user] = insert_pair(:user)
531 activity = insert(:note_activity, user: target_user)
532
533 {:ok, %Activity{id: first_report_id}} =
534 CommonAPI.report(reporter, %{
535 "account_id" => target_user.id,
536 "comment" => "I feel offended",
537 "status_ids" => [activity.id]
538 })
539
540 {:ok, %Activity{id: second_report_id}} =
541 CommonAPI.report(reporter, %{
542 "account_id" => target_user.id,
543 "comment" => "I feel very offended!",
544 "status_ids" => [activity.id]
545 })
546
547 {:ok, report_ids} =
548 CommonAPI.update_report_state([first_report_id, second_report_id], "resolved")
549
550 first_report = Activity.get_by_id(first_report_id)
551 second_report = Activity.get_by_id(second_report_id)
552
553 assert report_ids -- [first_report_id, second_report_id] == []
554 assert first_report.data["state"] == "resolved"
555 assert second_report.data["state"] == "resolved"
556 end
557 end
558
559 describe "reblog muting" do
560 setup do
561 muter = insert(:user)
562
563 muted = insert(:user)
564
565 [muter: muter, muted: muted]
566 end
567
568 test "add a reblog mute", %{muter: muter, muted: muted} do
569 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
570
571 assert User.showing_reblogs?(muter, muted) == false
572 end
573
574 test "remove a reblog mute", %{muter: muter, muted: muted} do
575 {:ok, _reblog_mute} = CommonAPI.hide_reblogs(muter, muted)
576 {:ok, _reblog_mute} = CommonAPI.show_reblogs(muter, muted)
577
578 assert User.showing_reblogs?(muter, muted) == true
579 end
580 end
581
582 describe "unfollow/2" do
583 test "also unsubscribes a user" do
584 [follower, followed] = insert_pair(:user)
585 {:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
586 {:ok, _subscription} = User.subscribe(follower, followed)
587
588 assert User.subscribed_to?(follower, followed)
589
590 {:ok, follower} = CommonAPI.unfollow(follower, followed)
591
592 refute User.subscribed_to?(follower, followed)
593 end
594
595 test "cancels a pending follow for a local user" do
596 follower = insert(:user)
597 followed = insert(:user, locked: true)
598
599 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
600 CommonAPI.follow(follower, followed)
601
602 assert User.get_follow_state(follower, followed) == :follow_pending
603 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
604 assert User.get_follow_state(follower, followed) == nil
605
606 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
607 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
608
609 assert %{
610 data: %{
611 "type" => "Undo",
612 "object" => %{"type" => "Follow", "state" => "cancelled"}
613 }
614 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
615 end
616
617 test "cancels a pending follow for a remote user" do
618 follower = insert(:user)
619 followed = insert(:user, locked: true, local: false, ap_enabled: true)
620
621 assert {:ok, follower, followed, %{id: activity_id, data: %{"state" => "pending"}}} =
622 CommonAPI.follow(follower, followed)
623
624 assert User.get_follow_state(follower, followed) == :follow_pending
625 assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
626 assert User.get_follow_state(follower, followed) == nil
627
628 assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
629 Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
630
631 assert %{
632 data: %{
633 "type" => "Undo",
634 "object" => %{"type" => "Follow", "state" => "cancelled"}
635 }
636 } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
637 end
638 end
639
640 describe "accept_follow_request/2" do
641 test "after acceptance, it sets all existing pending follow request states to 'accept'" do
642 user = insert(:user, locked: true)
643 follower = insert(:user)
644 follower_two = insert(:user)
645
646 {:ok, follow_activity} = ActivityPub.follow(follower, user)
647 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
648 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
649
650 assert follow_activity.data["state"] == "pending"
651 assert follow_activity_two.data["state"] == "pending"
652 assert follow_activity_three.data["state"] == "pending"
653
654 {:ok, _follower} = CommonAPI.accept_follow_request(follower, user)
655
656 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
657 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
658 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
659 end
660
661 test "after rejection, it sets all existing pending follow request states to 'reject'" do
662 user = insert(:user, locked: true)
663 follower = insert(:user)
664 follower_two = insert(:user)
665
666 {:ok, follow_activity} = ActivityPub.follow(follower, user)
667 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
668 {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user)
669
670 assert follow_activity.data["state"] == "pending"
671 assert follow_activity_two.data["state"] == "pending"
672 assert follow_activity_three.data["state"] == "pending"
673
674 {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
675
676 assert Repo.get(Activity, follow_activity.id).data["state"] == "reject"
677 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
678 assert Repo.get(Activity, follow_activity_three.id).data["state"] == "pending"
679 end
680 end
681
682 describe "vote/3" do
683 test "does not allow to vote twice" do
684 user = insert(:user)
685 other_user = insert(:user)
686
687 {:ok, activity} =
688 CommonAPI.post(user, %{
689 "status" => "Am I cute?",
690 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
691 })
692
693 object = Object.normalize(activity)
694
695 {:ok, _, object} = CommonAPI.vote(other_user, object, [0])
696
697 assert {:error, "Already voted"} == CommonAPI.vote(other_user, object, [1])
698 end
699 end
700
701 describe "listen/2" do
702 test "returns a valid activity" do
703 user = insert(:user)
704
705 {:ok, activity} =
706 CommonAPI.listen(user, %{
707 "title" => "lain radio episode 1",
708 "album" => "lain radio",
709 "artist" => "lain",
710 "length" => 180_000
711 })
712
713 object = Object.normalize(activity)
714
715 assert object.data["title"] == "lain radio episode 1"
716
717 assert Visibility.get_visibility(activity) == "public"
718 end
719
720 test "respects visibility=private" do
721 user = insert(:user)
722
723 {:ok, activity} =
724 CommonAPI.listen(user, %{
725 "title" => "lain radio episode 1",
726 "album" => "lain radio",
727 "artist" => "lain",
728 "length" => 180_000,
729 "visibility" => "private"
730 })
731
732 object = Object.normalize(activity)
733
734 assert object.data["title"] == "lain radio episode 1"
735
736 assert Visibility.get_visibility(activity) == "private"
737 end
738 end
739 end