total rebase
[anni] / test / pleroma / web / activity_pub / side_effects_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
6   use Oban.Testing, repo: Pleroma.Repo
7   use Pleroma.DataCase
8
9   alias Pleroma.Activity
10   alias Pleroma.Chat
11   alias Pleroma.Chat.MessageReference
12   alias Pleroma.Notification
13   alias Pleroma.Object
14   alias Pleroma.Repo
15   alias Pleroma.Tests.ObanHelpers
16   alias Pleroma.User
17   alias Pleroma.Web.ActivityPub.ActivityPub
18   alias Pleroma.Web.ActivityPub.Builder
19   alias Pleroma.Web.ActivityPub.SideEffects
20   alias Pleroma.Web.ActivityPub.Utils
21   alias Pleroma.Web.CommonAPI
22   alias Pleroma.Web.CommonAPI.ActivityDraft
23
24   import Mock
25   import Pleroma.Factory
26
27   defp get_announces_of_object(%{data: %{"id" => id}} = _object) do
28     Pleroma.Activity.Queries.by_type("Announce")
29     |> Pleroma.Activity.Queries.by_object_id(id)
30     |> Pleroma.Repo.all()
31   end
32
33   describe "handle_after_transaction" do
34     test "it streams out notifications and streams" do
35       author = insert(:user, local: true)
36       recipient = insert(:user, local: true)
37
38       {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
39
40       {:ok, create_activity_data, _meta} =
41         Builder.create(author, chat_message_data["id"], [recipient.ap_id])
42
43       {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
44
45       {:ok, _create_activity, meta} =
46         SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
47
48       assert [notification] = meta[:notifications]
49
50       with_mocks([
51         {
52           Pleroma.Web.Streamer,
53           [],
54           [
55             stream: fn _, _ -> nil end
56           ]
57         },
58         {
59           Pleroma.Web.Push,
60           [],
61           [
62             send: fn _ -> nil end
63           ]
64         }
65       ]) do
66         SideEffects.handle_after_transaction(meta)
67
68         assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
69         assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
70         assert called(Pleroma.Web.Push.send(notification))
71       end
72     end
73   end
74
75   describe "blocking users" do
76     setup do
77       user = insert(:user)
78       blocked = insert(:user)
79       User.follow(blocked, user)
80       User.follow(user, blocked)
81
82       {:ok, block_data, []} = Builder.block(user, blocked)
83       {:ok, block, _meta} = ActivityPub.persist(block_data, local: true)
84
85       %{user: user, blocked: blocked, block: block}
86     end
87
88     test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do
89       assert User.following?(user, blocked)
90       assert User.following?(blocked, user)
91
92       {:ok, _, _} = SideEffects.handle(block)
93
94       refute User.following?(user, blocked)
95       refute User.following?(blocked, user)
96       assert User.blocks?(user, blocked)
97     end
98
99     test "it updates following relationship", %{user: user, blocked: blocked, block: block} do
100       {:ok, _, _} = SideEffects.handle(block)
101
102       refute Pleroma.FollowingRelationship.get(user, blocked)
103       assert User.get_follow_state(user, blocked) == nil
104       assert User.get_follow_state(blocked, user) == nil
105       assert User.get_follow_state(user, blocked, nil) == nil
106       assert User.get_follow_state(blocked, user, nil) == nil
107     end
108
109     test "it blocks but does not unfollow if the relevant setting is set", %{
110       user: user,
111       blocked: blocked,
112       block: block
113     } do
114       clear_config([:activitypub, :unfollow_blocked], false)
115       assert User.following?(user, blocked)
116       assert User.following?(blocked, user)
117
118       {:ok, _, _} = SideEffects.handle(block)
119
120       refute User.following?(user, blocked)
121       assert User.following?(blocked, user)
122       assert User.blocks?(user, blocked)
123     end
124   end
125
126   describe "update users" do
127     setup do
128       user = insert(:user, local: false)
129
130       {:ok, update_data, []} =
131         Builder.update(user, %{"id" => user.ap_id, "type" => "Person", "name" => "new name!"})
132
133       {:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
134
135       %{user: user, update_data: update_data, update: update}
136     end
137
138     test "it updates the user", %{user: user, update: update} do
139       {:ok, _, _} = SideEffects.handle(update)
140       user = User.get_by_id(user.id)
141       assert user.name == "new name!"
142     end
143
144     test "it uses a given changeset to update", %{user: user, update: update} do
145       changeset = Ecto.Changeset.change(user, %{default_scope: "direct"})
146
147       assert user.default_scope == "public"
148       {:ok, _, _} = SideEffects.handle(update, user_update_changeset: changeset)
149       user = User.get_by_id(user.id)
150       assert user.default_scope == "direct"
151     end
152   end
153
154   describe "update notes" do
155     setup do
156       make_time = fn ->
157         Pleroma.Web.ActivityPub.Utils.make_date()
158       end
159
160       user = insert(:user)
161       note = insert(:note, user: user, data: %{"published" => make_time.()})
162       _note_activity = insert(:note_activity, note: note)
163
164       updated_note =
165         note.data
166         |> Map.put("summary", "edited summary")
167         |> Map.put("content", "edited content")
168         |> Map.put("updated", make_time.())
169
170       {:ok, update_data, []} = Builder.update(user, updated_note)
171       {:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
172
173       %{
174         user: user,
175         note: note,
176         object_id: note.id,
177         update_data: update_data,
178         update: update,
179         updated_note: updated_note
180       }
181     end
182
183     test "it updates the note", %{
184       object_id: object_id,
185       update: update,
186       updated_note: updated_note
187     } do
188       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
189       updated_time = updated_note["updated"]
190
191       new_note = Pleroma.Object.get_by_id(object_id)
192
193       assert %{
194                "summary" => "edited summary",
195                "content" => "edited content",
196                "updated" => ^updated_time
197              } = new_note.data
198     end
199
200     test "it rejects updates with no updated attribute in object", %{
201       object_id: object_id,
202       update: update,
203       updated_note: updated_note
204     } do
205       old_note = Pleroma.Object.get_by_id(object_id)
206       updated_note = Map.drop(updated_note, ["updated"])
207       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
208       new_note = Pleroma.Object.get_by_id(object_id)
209       assert old_note.data == new_note.data
210     end
211
212     test "it rejects updates with updated attribute older than what we have in the original object",
213          %{
214            object_id: object_id,
215            update: update,
216            updated_note: updated_note
217          } do
218       old_note = Pleroma.Object.get_by_id(object_id)
219       {:ok, creation_time, _} = DateTime.from_iso8601(old_note.data["published"])
220
221       updated_note =
222         Map.put(updated_note, "updated", DateTime.to_iso8601(DateTime.add(creation_time, -10)))
223
224       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
225       new_note = Pleroma.Object.get_by_id(object_id)
226       assert old_note.data == new_note.data
227     end
228
229     test "it rejects updates with updated attribute older than the last Update", %{
230       object_id: object_id,
231       update: update,
232       updated_note: updated_note
233     } do
234       old_note = Pleroma.Object.get_by_id(object_id)
235       {:ok, creation_time, _} = DateTime.from_iso8601(old_note.data["published"])
236
237       updated_note =
238         Map.put(updated_note, "updated", DateTime.to_iso8601(DateTime.add(creation_time, +10)))
239
240       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
241
242       old_note = Pleroma.Object.get_by_id(object_id)
243       {:ok, update_time, _} = DateTime.from_iso8601(old_note.data["updated"])
244
245       updated_note =
246         Map.put(updated_note, "updated", DateTime.to_iso8601(DateTime.add(update_time, -5)))
247
248       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
249
250       new_note = Pleroma.Object.get_by_id(object_id)
251       assert old_note.data == new_note.data
252     end
253
254     test "it updates using object_data", %{
255       object_id: object_id,
256       update: update,
257       updated_note: updated_note
258     } do
259       updated_note = Map.put(updated_note, "summary", "mew mew")
260       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
261       new_note = Pleroma.Object.get_by_id(object_id)
262       assert %{"summary" => "mew mew", "content" => "edited content"} = new_note.data
263     end
264
265     test "it records the original note in formerRepresentations", %{
266       note: note,
267       object_id: object_id,
268       update: update,
269       updated_note: updated_note
270     } do
271       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
272       %{data: new_note} = Pleroma.Object.get_by_id(object_id)
273       assert %{"summary" => "edited summary", "content" => "edited content"} = new_note
274
275       assert [Map.drop(note.data, ["id", "formerRepresentations"])] ==
276                new_note["formerRepresentations"]["orderedItems"]
277
278       assert new_note["formerRepresentations"]["totalItems"] == 1
279     end
280
281     test "it puts the original note at the front of formerRepresentations", %{
282       user: user,
283       note: note,
284       object_id: object_id,
285       update: update,
286       updated_note: updated_note
287     } do
288       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
289       %{data: first_edit} = Pleroma.Object.get_by_id(object_id)
290
291       second_updated_note =
292         note.data
293         |> Map.put("summary", "edited summary 2")
294         |> Map.put("content", "edited content 2")
295         |> Map.put(
296           "updated",
297           first_edit["updated"]
298           |> DateTime.from_iso8601()
299           |> elem(1)
300           |> DateTime.add(10)
301           |> DateTime.to_iso8601()
302         )
303
304       {:ok, second_update_data, []} = Builder.update(user, second_updated_note)
305       {:ok, update, _meta} = ActivityPub.persist(second_update_data, local: true)
306       {:ok, _, _} = SideEffects.handle(update, object_data: second_updated_note)
307       %{data: new_note} = Pleroma.Object.get_by_id(object_id)
308       assert %{"summary" => "edited summary 2", "content" => "edited content 2"} = new_note
309
310       original_version = Map.drop(note.data, ["id", "formerRepresentations"])
311       first_edit = Map.drop(first_edit, ["id", "formerRepresentations"])
312
313       assert [first_edit, original_version] ==
314                new_note["formerRepresentations"]["orderedItems"]
315
316       assert new_note["formerRepresentations"]["totalItems"] == 2
317     end
318
319     test "it does not prepend to formerRepresentations if no actual changes are made", %{
320       note: note,
321       object_id: object_id,
322       update: update,
323       updated_note: updated_note
324     } do
325       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
326       %{data: first_edit} = Pleroma.Object.get_by_id(object_id)
327
328       updated_note =
329         updated_note
330         |> Map.put(
331           "updated",
332           first_edit["updated"]
333           |> DateTime.from_iso8601()
334           |> elem(1)
335           |> DateTime.add(10)
336           |> DateTime.to_iso8601()
337         )
338
339       {:ok, _, _} = SideEffects.handle(update, object_data: updated_note)
340       %{data: new_note} = Pleroma.Object.get_by_id(object_id)
341       assert %{"summary" => "edited summary", "content" => "edited content"} = new_note
342
343       original_version = Map.drop(note.data, ["id", "formerRepresentations"])
344
345       assert [original_version] ==
346                new_note["formerRepresentations"]["orderedItems"]
347
348       assert new_note["formerRepresentations"]["totalItems"] == 1
349     end
350   end
351
352   describe "update questions" do
353     setup do
354       user = insert(:user)
355
356       question =
357         insert(:question,
358           user: user,
359           data: %{"published" => Pleroma.Web.ActivityPub.Utils.make_date()}
360         )
361
362       %{user: user, data: question.data, id: question.id}
363     end
364
365     test "allows updating choice count without generating edit history", %{
366       user: user,
367       data: data,
368       id: id
369     } do
370       new_choices =
371         data["oneOf"]
372         |> Enum.map(fn choice -> put_in(choice, ["replies", "totalItems"], 5) end)
373
374       updated_question =
375         data
376         |> Map.put("oneOf", new_choices)
377         |> Map.put("updated", Pleroma.Web.ActivityPub.Utils.make_date())
378
379       {:ok, update_data, []} = Builder.update(user, updated_question)
380       {:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
381
382       {:ok, _, _} = SideEffects.handle(update, object_data: updated_question)
383
384       %{data: new_question} = Pleroma.Object.get_by_id(id)
385
386       assert [%{"replies" => %{"totalItems" => 5}}, %{"replies" => %{"totalItems" => 5}}] =
387                new_question["oneOf"]
388
389       refute Map.has_key?(new_question, "formerRepresentations")
390     end
391
392     test "allows updating choice count without updated field", %{
393       user: user,
394       data: data,
395       id: id
396     } do
397       new_choices =
398         data["oneOf"]
399         |> Enum.map(fn choice -> put_in(choice, ["replies", "totalItems"], 5) end)
400
401       updated_question =
402         data
403         |> Map.put("oneOf", new_choices)
404
405       {:ok, update_data, []} = Builder.update(user, updated_question)
406       {:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
407
408       {:ok, _, _} = SideEffects.handle(update, object_data: updated_question)
409
410       %{data: new_question} = Pleroma.Object.get_by_id(id)
411
412       assert [%{"replies" => %{"totalItems" => 5}}, %{"replies" => %{"totalItems" => 5}}] =
413                new_question["oneOf"]
414
415       refute Map.has_key?(new_question, "formerRepresentations")
416     end
417
418     test "allows updating choice count with updated field same as the creation date", %{
419       user: user,
420       data: data,
421       id: id
422     } do
423       new_choices =
424         data["oneOf"]
425         |> Enum.map(fn choice -> put_in(choice, ["replies", "totalItems"], 5) end)
426
427       updated_question =
428         data
429         |> Map.put("oneOf", new_choices)
430         |> Map.put("updated", data["published"])
431
432       {:ok, update_data, []} = Builder.update(user, updated_question)
433       {:ok, update, _meta} = ActivityPub.persist(update_data, local: true)
434
435       {:ok, _, _} = SideEffects.handle(update, object_data: updated_question)
436
437       %{data: new_question} = Pleroma.Object.get_by_id(id)
438
439       assert [%{"replies" => %{"totalItems" => 5}}, %{"replies" => %{"totalItems" => 5}}] =
440                new_question["oneOf"]
441
442       refute Map.has_key?(new_question, "formerRepresentations")
443     end
444   end
445
446   describe "EmojiReact objects" do
447     setup do
448       poster = insert(:user)
449       user = insert(:user)
450
451       {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
452
453       {:ok, emoji_react_data, []} = Builder.emoji_react(user, post.object, "👌")
454       {:ok, emoji_react, _meta} = ActivityPub.persist(emoji_react_data, local: true)
455
456       %{emoji_react: emoji_react, user: user, poster: poster}
457     end
458
459     test "adds the reaction to the object", %{emoji_react: emoji_react, user: user} do
460       {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
461       object = Object.get_by_ap_id(emoji_react.data["object"])
462
463       assert object.data["reaction_count"] == 1
464       assert ["👌", [user.ap_id], nil] in object.data["reactions"]
465     end
466
467     test "creates a notification", %{emoji_react: emoji_react, poster: poster} do
468       {:ok, emoji_react, _} = SideEffects.handle(emoji_react)
469       assert Repo.get_by(Notification, user_id: poster.id, activity_id: emoji_react.id)
470     end
471   end
472
473   describe "Question objects" do
474     setup do
475       user = insert(:user)
476       question = build(:question, user: user)
477       question_activity = build(:question_activity, question: question)
478       activity_data = Map.put(question_activity.data, "object", question.data["id"])
479       meta = [object_data: question.data, local: false]
480
481       {:ok, activity, meta} = ActivityPub.persist(activity_data, meta)
482
483       %{activity: activity, meta: meta}
484     end
485
486     test "enqueues the poll end", %{activity: activity, meta: meta} do
487       {:ok, activity, meta} = SideEffects.handle(activity, meta)
488
489       assert_enqueued(
490         worker: Pleroma.Workers.PollWorker,
491         args: %{op: "poll_end", activity_id: activity.id},
492         scheduled_at: NaiveDateTime.from_iso8601!(meta[:object_data]["closed"])
493       )
494     end
495   end
496
497   describe "delete users with confirmation pending" do
498     setup do
499       user = insert(:user, is_confirmed: false)
500       {:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
501       {:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
502       {:ok, delete: delete_user, user: user}
503     end
504
505     test "when activation is required", %{delete: delete, user: user} do
506       clear_config([:instance, :account_activation_required], true)
507       {:ok, _, _} = SideEffects.handle(delete)
508       ObanHelpers.perform_all()
509
510       refute User.get_cached_by_id(user.id)
511     end
512   end
513
514   describe "Undo objects" do
515     setup do
516       poster = insert(:user)
517       user = insert(:user)
518       {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
519       {:ok, like} = CommonAPI.favorite(user, post.id)
520       {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
521       {:ok, announce} = CommonAPI.repeat(post.id, user)
522       {:ok, block} = CommonAPI.block(user, poster)
523
524       {:ok, undo_data, _meta} = Builder.undo(user, like)
525       {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
526
527       {:ok, undo_data, _meta} = Builder.undo(user, reaction)
528       {:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true)
529
530       {:ok, undo_data, _meta} = Builder.undo(user, announce)
531       {:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true)
532
533       {:ok, undo_data, _meta} = Builder.undo(user, block)
534       {:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true)
535
536       %{
537         like_undo: like_undo,
538         post: post,
539         like: like,
540         reaction_undo: reaction_undo,
541         reaction: reaction,
542         announce_undo: announce_undo,
543         announce: announce,
544         block_undo: block_undo,
545         block: block,
546         poster: poster,
547         user: user
548       }
549     end
550
551     test "deletes the original block", %{
552       block_undo: block_undo,
553       block: block
554     } do
555       {:ok, _block_undo, _meta} = SideEffects.handle(block_undo)
556
557       refute Activity.get_by_id(block.id)
558     end
559
560     test "unblocks the blocked user", %{block_undo: block_undo, block: block} do
561       blocker = User.get_by_ap_id(block.data["actor"])
562       blocked = User.get_by_ap_id(block.data["object"])
563
564       {:ok, _block_undo, _} = SideEffects.handle(block_undo)
565       refute User.blocks?(blocker, blocked)
566     end
567
568     test "an announce undo removes the announce from the object", %{
569       announce_undo: announce_undo,
570       post: post
571     } do
572       {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
573
574       object = Object.get_by_ap_id(post.data["object"])
575
576       assert object.data["announcement_count"] == 0
577       assert object.data["announcements"] == []
578     end
579
580     test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do
581       {:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
582       refute Activity.get_by_id(announce.id)
583     end
584
585     test "a reaction undo removes the reaction from the object", %{
586       reaction_undo: reaction_undo,
587       post: post
588     } do
589       {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
590
591       object = Object.get_by_ap_id(post.data["object"])
592
593       assert object.data["reaction_count"] == 0
594       assert object.data["reactions"] == []
595     end
596
597     test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do
598       {:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
599       refute Activity.get_by_id(reaction.id)
600     end
601
602     test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do
603       {:ok, _like_undo, _} = SideEffects.handle(like_undo)
604
605       object = Object.get_by_ap_id(post.data["object"])
606
607       assert object.data["like_count"] == 0
608       assert object.data["likes"] == []
609     end
610
611     test "deletes the original like", %{like_undo: like_undo, like: like} do
612       {:ok, _like_undo, _} = SideEffects.handle(like_undo)
613       refute Activity.get_by_id(like.id)
614     end
615   end
616
617   describe "like objects" do
618     setup do
619       poster = insert(:user)
620       user = insert(:user)
621       {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
622
623       {:ok, like_data, _meta} = Builder.like(user, post.object)
624       {:ok, like, _meta} = ActivityPub.persist(like_data, local: true)
625
626       %{like: like, user: user, poster: poster}
627     end
628
629     test "add the like to the original object", %{like: like, user: user} do
630       {:ok, like, _} = SideEffects.handle(like)
631       object = Object.get_by_ap_id(like.data["object"])
632       assert object.data["like_count"] == 1
633       assert user.ap_id in object.data["likes"]
634     end
635
636     test "creates a notification", %{like: like, poster: poster} do
637       {:ok, like, _} = SideEffects.handle(like)
638       assert Repo.get_by(Notification, user_id: poster.id, activity_id: like.id)
639     end
640   end
641
642   describe "creation of ChatMessages" do
643     test "notifies the recipient" do
644       author = insert(:user, local: false)
645       recipient = insert(:user, local: true)
646
647       {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
648
649       {:ok, create_activity_data, _meta} =
650         Builder.create(author, chat_message_data["id"], [recipient.ap_id])
651
652       {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
653
654       {:ok, _create_activity, _meta} =
655         SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
656
657       assert Repo.get_by(Notification, user_id: recipient.id, activity_id: create_activity.id)
658     end
659
660     test "it streams the created ChatMessage" do
661       author = insert(:user, local: true)
662       recipient = insert(:user, local: true)
663
664       {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
665
666       {:ok, create_activity_data, _meta} =
667         Builder.create(author, chat_message_data["id"], [recipient.ap_id])
668
669       {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
670
671       {:ok, _create_activity, meta} =
672         SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
673
674       assert [_, _] = meta[:streamables]
675     end
676
677     test "it creates a Chat and MessageReferences for the local users and bumps the unread count, except for the author" do
678       author = insert(:user, local: true)
679       recipient = insert(:user, local: true)
680
681       {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
682
683       {:ok, create_activity_data, _meta} =
684         Builder.create(author, chat_message_data["id"], [recipient.ap_id])
685
686       {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
687
688       with_mocks([
689         {
690           Pleroma.Web.Streamer,
691           [],
692           [
693             stream: fn _, _ -> nil end
694           ]
695         },
696         {
697           Pleroma.Web.Push,
698           [],
699           [
700             send: fn _ -> nil end
701           ]
702         }
703       ]) do
704         {:ok, _create_activity, meta} =
705           SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
706
707         # The notification gets created
708         assert [notification] = meta[:notifications]
709         assert notification.activity_id == create_activity.id
710
711         # But it is not sent out
712         refute called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
713         refute called(Pleroma.Web.Push.send(notification))
714
715         # Same for the user chat stream
716         assert [{topics, _}, _] = meta[:streamables]
717         assert topics == ["user", "user:pleroma_chat"]
718         refute called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
719
720         chat = Chat.get(author.id, recipient.ap_id)
721
722         [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all()
723
724         assert cm_ref.object.data["content"] == "hey"
725         assert cm_ref.unread == false
726
727         chat = Chat.get(recipient.id, author.ap_id)
728
729         [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all()
730
731         assert cm_ref.object.data["content"] == "hey"
732         assert cm_ref.unread == true
733       end
734     end
735
736     test "it creates a Chat for the local users and bumps the unread count" do
737       author = insert(:user, local: false)
738       recipient = insert(:user, local: true)
739
740       {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
741
742       {:ok, create_activity_data, _meta} =
743         Builder.create(author, chat_message_data["id"], [recipient.ap_id])
744
745       {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
746
747       {:ok, _create_activity, _meta} =
748         SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
749
750       # An object is created
751       assert Object.get_by_ap_id(chat_message_data["id"])
752
753       # The remote user won't get a chat
754       chat = Chat.get(author.id, recipient.ap_id)
755       refute chat
756
757       # The local user will get a chat
758       chat = Chat.get(recipient.id, author.ap_id)
759       assert chat
760
761       author = insert(:user, local: true)
762       recipient = insert(:user, local: true)
763
764       {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
765
766       {:ok, create_activity_data, _meta} =
767         Builder.create(author, chat_message_data["id"], [recipient.ap_id])
768
769       {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
770
771       {:ok, _create_activity, _meta} =
772         SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
773
774       # Both users are local and get the chat
775       chat = Chat.get(author.id, recipient.ap_id)
776       assert chat
777
778       chat = Chat.get(recipient.id, author.ap_id)
779       assert chat
780     end
781   end
782
783   describe "announce objects" do
784     setup do
785       poster = insert(:user)
786       user = insert(:user)
787       {:ok, post} = CommonAPI.post(poster, %{status: "hey"})
788       {:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
789
790       {:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
791
792       {:ok, private_announce_data, _meta} =
793         Builder.announce(user, private_post.object, public: false)
794
795       {:ok, relay_announce_data, _meta} =
796         Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
797
798       {:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
799       {:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
800       {:ok, relay_announce, _meta} = ActivityPub.persist(relay_announce_data, local: true)
801
802       %{
803         announce: announce,
804         user: user,
805         poster: poster,
806         private_announce: private_announce,
807         relay_announce: relay_announce
808       }
809     end
810
811     test "adds the announce to the original object", %{announce: announce, user: user} do
812       {:ok, announce, _} = SideEffects.handle(announce)
813       object = Object.get_by_ap_id(announce.data["object"])
814       assert object.data["announcement_count"] == 1
815       assert user.ap_id in object.data["announcements"]
816     end
817
818     test "does not add the announce to the original object if the actor is a service actor", %{
819       relay_announce: announce
820     } do
821       {:ok, announce, _} = SideEffects.handle(announce)
822       object = Object.get_by_ap_id(announce.data["object"])
823       assert object.data["announcement_count"] == nil
824     end
825
826     test "creates a notification", %{announce: announce, poster: poster} do
827       {:ok, announce, _} = SideEffects.handle(announce)
828       assert Repo.get_by(Notification, user_id: poster.id, activity_id: announce.id)
829     end
830
831     test "it streams out the announce", %{announce: announce} do
832       with_mocks([
833         {
834           Pleroma.Web.Streamer,
835           [],
836           [
837             stream: fn _, _ -> nil end
838           ]
839         },
840         {
841           Pleroma.Web.Push,
842           [],
843           [
844             send: fn _ -> nil end
845           ]
846         }
847       ]) do
848         {:ok, announce, _} = SideEffects.handle(announce)
849
850         assert called(Pleroma.Web.Streamer.stream(["user", "list"], announce))
851
852         assert called(Pleroma.Web.Push.send(:_))
853       end
854     end
855   end
856
857   describe "removing a follower" do
858     setup do
859       user = insert(:user)
860       followed = insert(:user)
861
862       {:ok, _, _, follow_activity} = CommonAPI.follow(user, followed)
863
864       {:ok, reject_data, []} = Builder.reject(followed, follow_activity)
865       {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: true)
866
867       %{user: user, followed: followed, reject: reject}
868     end
869
870     test "", %{user: user, followed: followed, reject: reject} do
871       assert User.following?(user, followed)
872       assert Pleroma.FollowingRelationship.get(user, followed)
873
874       {:ok, _, _} = SideEffects.handle(reject)
875
876       refute User.following?(user, followed)
877       refute Pleroma.FollowingRelationship.get(user, followed)
878       assert User.get_follow_state(user, followed) == nil
879       assert User.get_follow_state(user, followed, nil) == nil
880     end
881   end
882
883   describe "removing a follower from remote" do
884     setup do
885       user = insert(:user)
886       followed = insert(:user, local: false)
887
888       # Mock a local-to-remote follow
889       {:ok, follow_data, []} = Builder.follow(user, followed)
890
891       follow_data =
892         follow_data
893         |> Map.put("state", "accept")
894
895       {:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true)
896       {:ok, _, _} = SideEffects.handle(follow)
897
898       # Mock a remote-to-local accept
899       {:ok, accept_data, _} = Builder.accept(followed, follow)
900       {:ok, accept, _} = ActivityPub.persist(accept_data, local: false)
901       {:ok, _, _} = SideEffects.handle(accept)
902
903       # Mock a remote-to-local reject
904       {:ok, reject_data, []} = Builder.reject(followed, follow)
905       {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: false)
906
907       %{user: user, followed: followed, reject: reject}
908     end
909
910     test "", %{user: user, followed: followed, reject: reject} do
911       assert User.following?(user, followed)
912       assert Pleroma.FollowingRelationship.get(user, followed)
913
914       {:ok, _, _} = SideEffects.handle(reject)
915
916       refute User.following?(user, followed)
917       refute Pleroma.FollowingRelationship.get(user, followed)
918
919       assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] ==
920                "reject"
921
922       assert User.get_follow_state(user, followed) == nil
923       assert User.get_follow_state(user, followed, nil) == nil
924     end
925   end
926
927   describe "Group actors" do
928     setup do
929       poster =
930         insert(:user,
931           local: false,
932           nickname: "poster@example.com",
933           ap_id: "https://example.com/users/poster"
934         )
935
936       group = insert(:user, actor_type: "Group")
937
938       make_create = fn mentioned_users ->
939         mentions = mentioned_users |> Enum.map(fn u -> "@#{u.nickname}" end) |> Enum.join(" ")
940         {:ok, draft} = ActivityDraft.create(poster, %{status: "#{mentions} hey"})
941
942         create_activity_data =
943           Utils.make_create_data(draft.changes |> Map.put(:published, nil), %{})
944           |> put_in(["object", "id"], "https://example.com/object")
945           |> put_in(["id"], "https://example.com/activity")
946
947         assert Enum.all?(mentioned_users, fn u -> u.ap_id in create_activity_data["to"] end)
948
949         create_activity_data
950       end
951
952       %{poster: poster, group: group, make_create: make_create}
953     end
954
955     test "group should boost it", %{make_create: make_create, group: group} do
956       create_activity_data = make_create.([group])
957       {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
958
959       {:ok, _create_activity, _meta} =
960         SideEffects.handle(create_activity,
961           local: false,
962           object_data: create_activity_data["object"]
963         )
964
965       object = Object.normalize(create_activity, fetch: false)
966       assert [announce] = get_announces_of_object(object)
967       assert announce.actor == group.ap_id
968     end
969
970     test "remote group should not boost it", %{make_create: make_create, group: group} do
971       remote_group =
972         insert(:user, actor_type: "Group", local: false, nickname: "remotegroup@example.com")
973
974       create_activity_data = make_create.([group, remote_group])
975       {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
976
977       {:ok, _create_activity, _meta} =
978         SideEffects.handle(create_activity,
979           local: false,
980           object_data: create_activity_data["object"]
981         )
982
983       object = Object.normalize(create_activity, fetch: false)
984       assert [announce] = get_announces_of_object(object)
985       assert announce.actor == group.ap_id
986     end
987
988     test "group should not boost it if group is blocking poster", %{
989       make_create: make_create,
990       group: group,
991       poster: poster
992     } do
993       {:ok, _} = CommonAPI.block(group, poster)
994       create_activity_data = make_create.([group])
995       {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
996
997       {:ok, _create_activity, _meta} =
998         SideEffects.handle(create_activity,
999           local: false,
1000           object_data: create_activity_data["object"]
1001         )
1002
1003       object = Object.normalize(create_activity, fetch: false)
1004       assert [] = get_announces_of_object(object)
1005     end
1006   end
1007 end