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