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