1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.UtilsTest do
6 use Pleroma.DataCase, async: true
11 alias Pleroma.Web.ActivityPub.Utils
12 alias Pleroma.Web.AdminAPI.AccountView
13 alias Pleroma.Web.CommonAPI
15 import Pleroma.Factory
17 require Pleroma.Constants
19 describe "fetch the latest Follow" do
20 test "fetches the latest Follow activity" do
21 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
22 follower = User.get_cached_by_ap_id(activity.data["actor"])
23 followed = User.get_cached_by_ap_id(activity.data["object"])
25 assert activity == Utils.fetch_latest_follow(follower, followed)
29 describe "determine_explicit_mentions()" do
30 test "works with an object that has mentions" do
35 "href" => "https://example.com/~alyssa",
36 "name" => "Alyssa P. Hacker"
41 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
44 test "works with an object that does not have mentions" do
47 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
51 assert Utils.determine_explicit_mentions(object) == []
54 test "works with an object that has mentions and other tags" do
59 "href" => "https://example.com/~alyssa",
60 "name" => "Alyssa P. Hacker"
62 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
66 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
69 test "works with an object that has no tags" do
72 assert Utils.determine_explicit_mentions(object) == []
75 test "works with an object that has only IR tags" do
76 object = %{"tag" => ["2hu"]}
78 assert Utils.determine_explicit_mentions(object) == []
81 test "works with an object has tags as map" do
85 "href" => "https://example.com/~alyssa",
86 "name" => "Alyssa P. Hacker"
90 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
94 describe "make_like_data" do
97 other_user = insert(:user)
98 third_user = insert(:user)
99 [user: user, other_user: other_user, third_user: third_user]
102 test "addresses actor's follower address if the activity is public", %{
104 other_user: other_user,
105 third_user: third_user
107 expected_to = Enum.sort([user.ap_id, other_user.follower_address])
108 expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
111 CommonAPI.post(user, %{
113 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
116 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
117 assert Enum.sort(to) == expected_to
118 assert Enum.sort(cc) == expected_cc
121 test "does not adress actor's follower address if the activity is not public", %{
123 other_user: other_user,
124 third_user: third_user
126 expected_to = Enum.sort([user.ap_id])
127 expected_cc = [third_user.ap_id]
130 CommonAPI.post(user, %{
131 status: "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
132 visibility: "private"
135 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
136 assert Enum.sort(to) == expected_to
137 assert Enum.sort(cc) == expected_cc
141 test "make_json_ld_header/0" do
142 assert Utils.make_json_ld_header() == %{
144 "https://www.w3.org/ns/activitystreams",
145 "http://localhost:4001/schemas/litepub-0.1.jsonld",
153 describe "get_existing_votes" do
154 test "fetches existing votes" do
156 other_user = insert(:user)
159 CommonAPI.post(user, %{
160 status: "How do I pronounce LaTeX?",
162 options: ["laytekh", "lahtekh", "latex"],
168 object = Object.normalize(activity, fetch: false)
169 {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
170 assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
173 test "fetches only Create activities" do
175 other_user = insert(:user)
178 CommonAPI.post(user, %{
179 status: "Are we living in a society?",
181 options: ["yes", "no"],
186 object = Object.normalize(activity, fetch: false)
187 {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
188 {:ok, _activity} = CommonAPI.favorite(user, activity.id)
189 [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
190 assert fetched_vote.id == vote.id
194 describe "update_follow_state_for_all/2" do
195 test "updates the state of all Follow activities with the same actor and object" do
196 user = insert(:user, is_locked: true)
197 follower = insert(:user)
199 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
200 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
203 follow_activity_two.data
204 |> Map.put("state", "accept")
206 cng = Ecto.Changeset.change(follow_activity_two, data: data)
208 {:ok, follow_activity_two} = Repo.update(cng)
210 {:ok, follow_activity_two} =
211 Utils.update_follow_state_for_all(follow_activity_two, "accept")
213 assert refresh_record(follow_activity).data["state"] == "accept"
214 assert refresh_record(follow_activity_two).data["state"] == "accept"
217 test "also updates the state of accepted follows" do
219 follower = insert(:user)
221 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
222 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
224 {:ok, follow_activity_two} =
225 Utils.update_follow_state_for_all(follow_activity_two, "reject")
227 assert refresh_record(follow_activity).data["state"] == "reject"
228 assert refresh_record(follow_activity_two).data["state"] == "reject"
232 describe "update_follow_state/2" do
233 test "updates the state of the given follow activity" do
234 user = insert(:user, is_locked: true)
235 follower = insert(:user)
237 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user)
238 {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user)
241 follow_activity_two.data
242 |> Map.put("state", "accept")
244 cng = Ecto.Changeset.change(follow_activity_two, data: data)
246 {:ok, follow_activity_two} = Repo.update(cng)
248 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
250 assert refresh_record(follow_activity).data["state"] == "pending"
251 assert refresh_record(follow_activity_two).data["state"] == "reject"
255 describe "update_element_in_object/3" do
256 test "updates likes" do
258 activity = insert(:note_activity)
259 object = Object.normalize(activity, fetch: false)
261 assert {:ok, updated_object} =
262 Utils.update_element_in_object(
268 assert updated_object.data["likes"] == [user.ap_id]
269 assert updated_object.data["like_count"] == 1
273 describe "add_like_to_object/2" do
274 test "add actor to likes" do
276 user2 = insert(:user)
277 object = insert(:note)
279 assert {:ok, updated_object} =
280 Utils.add_like_to_object(
281 %Activity{data: %{"actor" => user.ap_id}},
285 assert updated_object.data["likes"] == [user.ap_id]
286 assert updated_object.data["like_count"] == 1
288 assert {:ok, updated_object2} =
289 Utils.add_like_to_object(
290 %Activity{data: %{"actor" => user2.ap_id}},
294 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
295 assert updated_object2.data["like_count"] == 2
299 describe "remove_like_from_object/2" do
300 test "removes ap_id from likes" do
302 user2 = insert(:user)
303 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
305 assert {:ok, updated_object} =
306 Utils.remove_like_from_object(
307 %Activity{data: %{"actor" => user.ap_id}},
311 assert updated_object.data["likes"] == [user2.ap_id]
312 assert updated_object.data["like_count"] == 1
316 describe "get_existing_like/2" do
317 test "fetches existing like" do
318 note_activity = insert(:note_activity)
319 assert object = Object.normalize(note_activity, fetch: false)
322 refute Utils.get_existing_like(user.ap_id, object)
323 {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
325 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
329 describe "get_get_existing_announce/2" do
330 test "returns nil if announce not found" do
331 actor = insert(:user)
332 refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}})
335 test "fetches existing announce" do
336 note_activity = insert(:note_activity)
337 assert object = Object.normalize(note_activity, fetch: false)
338 actor = insert(:user)
340 {:ok, announce} = CommonAPI.repeat(note_activity.id, actor)
341 assert Utils.get_existing_announce(actor.ap_id, object) == announce
345 describe "fetch_latest_block/2" do
346 test "fetches last block activities" do
347 user1 = insert(:user)
348 user2 = insert(:user)
350 assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
351 assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
352 assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2)
354 assert Utils.fetch_latest_block(user1, user2) == activity
358 describe "recipient_in_message/3" do
359 test "returns true when recipient in `to`" do
360 recipient = insert(:user)
361 actor = insert(:user)
362 assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id})
364 assert Utils.recipient_in_message(
367 %{"to" => [recipient.ap_id], "cc" => ""}
371 test "returns true when recipient in `cc`" do
372 recipient = insert(:user)
373 actor = insert(:user)
374 assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id})
376 assert Utils.recipient_in_message(
379 %{"cc" => [recipient.ap_id], "to" => ""}
383 test "returns true when recipient in `bto`" do
384 recipient = insert(:user)
385 actor = insert(:user)
386 assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id})
388 assert Utils.recipient_in_message(
391 %{"bcc" => "", "bto" => [recipient.ap_id]}
395 test "returns true when recipient in `bcc`" do
396 recipient = insert(:user)
397 actor = insert(:user)
398 assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id})
400 assert Utils.recipient_in_message(
403 %{"bto" => "", "bcc" => [recipient.ap_id]}
407 test "returns true when message without addresses fields" do
408 recipient = insert(:user)
409 actor = insert(:user)
410 assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id})
412 assert Utils.recipient_in_message(
415 %{"btod" => "", "bccc" => [recipient.ap_id]}
419 test "returns false" do
420 recipient = insert(:user)
421 actor = insert(:user)
422 refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"})
426 describe "lazy_put_activity_defaults/2" do
427 test "returns map with id and published data" do
428 note_activity = insert(:note_activity)
429 object = Object.normalize(note_activity, fetch: false)
430 res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
431 assert res["context"] == object.data["id"]
433 assert res["published"]
436 test "returns map with fake id and published data" do
438 "context" => "pleroma:fakecontext",
439 "id" => "pleroma:fakeid",
441 } = Utils.lazy_put_activity_defaults(%{}, true)
444 test "returns activity data with object" do
445 note_activity = insert(:note_activity)
446 object = Object.normalize(note_activity, fetch: false)
449 Utils.lazy_put_activity_defaults(%{
450 "context" => object.data["id"],
454 assert res["context"] == object.data["id"]
456 assert res["published"]
457 assert res["object"]["id"]
458 assert res["object"]["published"]
459 assert res["object"]["context"] == object.data["id"]
463 describe "make_flag_data" do
464 test "returns empty map when params is invalid" do
465 assert Utils.make_flag_data(%{}, %{}) == %{}
468 test "returns map with Flag object" do
469 reporter = insert(:user)
470 target_account = insert(:user)
471 {:ok, activity} = CommonAPI.post(target_account, %{status: "foobar"})
472 context = Utils.generate_context_id()
475 target_ap_id = target_account.ap_id
476 object_ap_id = activity.object.data["id"]
479 Utils.make_flag_data(
483 account: target_account,
484 statuses: [%{"id" => activity.data["id"]}],
492 "id" => object_ap_id,
493 "content" => content,
494 "published" => activity.object.data["published"],
496 AccountView.render("show.json", %{user: target_account, skip_visibility_check: true})
501 "content" => ^content,
502 "context" => ^context,
503 "object" => [^target_ap_id, ^note_obj],
508 test "returns map with Flag object with a non-Create Activity" do
509 reporter = insert(:user)
510 posting_account = insert(:user)
511 target_account = insert(:user)
513 {:ok, activity} = CommonAPI.post(posting_account, %{status: "foobar"})
514 {:ok, like} = CommonAPI.favorite(target_account, activity.id)
515 context = Utils.generate_context_id()
518 target_ap_id = target_account.ap_id
519 object_ap_id = activity.object.data["id"]
522 Utils.make_flag_data(
526 account: target_account,
527 statuses: [%{"id" => like.data["id"]}],
535 "id" => object_ap_id,
536 "content" => content,
537 "published" => activity.object.data["published"],
539 AccountView.render("show.json", %{user: posting_account, skip_visibility_check: true})
544 "content" => ^content,
545 "context" => ^context,
546 "object" => [^target_ap_id, ^note_obj],
552 describe "add_announce_to_object/2" do
553 test "adds actor to announcement" do
555 object = insert(:note)
558 insert(:note_activity,
560 "actor" => user.ap_id,
561 "cc" => [Pleroma.Constants.as_public()]
565 assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object)
566 assert updated_object.data["announcements"] == [user.ap_id]
567 assert updated_object.data["announcement_count"] == 1
571 describe "remove_announce_from_object/2" do
572 test "removes actor from announcements" do
574 user2 = insert(:user)
578 data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2}
581 activity = insert(:note_activity, data: %{"actor" => user.ap_id})
583 assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object)
584 assert updated_object.data["announcements"] == [user2.ap_id]
585 assert updated_object.data["announcement_count"] == 1
589 describe "get_cached_emoji_reactions/1" do
590 test "returns the data or an emtpy list" do
591 object = insert(:note)
592 assert Utils.get_cached_emoji_reactions(object) == []
594 object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]})
595 assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"]]]
597 object = insert(:note, data: %{"reactions" => %{}})
598 assert Utils.get_cached_emoji_reactions(object) == []