1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.UtilsTest do
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.Utils
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 "fetch the latest Block" do
30 test "fetches the latest Block activity" do
31 blocker = insert(:user)
32 blocked = insert(:user)
33 {:ok, activity} = ActivityPub.block(blocker, blocked)
35 assert activity == Utils.fetch_latest_block(blocker, blocked)
39 describe "determine_explicit_mentions()" do
40 test "works with an object that has mentions" do
45 "href" => "https://example.com/~alyssa",
46 "name" => "Alyssa P. Hacker"
51 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
54 test "works with an object that does not have mentions" do
57 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
61 assert Utils.determine_explicit_mentions(object) == []
64 test "works with an object that has mentions and other tags" do
69 "href" => "https://example.com/~alyssa",
70 "name" => "Alyssa P. Hacker"
72 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
76 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
79 test "works with an object that has no tags" do
82 assert Utils.determine_explicit_mentions(object) == []
85 test "works with an object that has only IR tags" do
86 object = %{"tag" => ["2hu"]}
88 assert Utils.determine_explicit_mentions(object) == []
91 test "works with an object has tags as map" do
95 "href" => "https://example.com/~alyssa",
96 "name" => "Alyssa P. Hacker"
100 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
104 describe "make_unlike_data/3" do
105 test "returns data for unlike activity" do
107 like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"})
109 assert Utils.make_unlike_data(user, like_activity, nil) == %{
111 "actor" => user.ap_id,
112 "object" => like_activity.data,
113 "to" => [user.follower_address, like_activity.data["actor"]],
114 "cc" => [Pleroma.Constants.as_public()],
115 "context" => like_activity.data["context"]
118 assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
120 "actor" => user.ap_id,
121 "object" => like_activity.data,
122 "to" => [user.follower_address, like_activity.data["actor"]],
123 "cc" => [Pleroma.Constants.as_public()],
124 "context" => like_activity.data["context"],
125 "id" => "9mJEZK0tky1w2xD2vY"
130 describe "make_like_data" do
133 other_user = insert(:user)
134 third_user = insert(:user)
135 [user: user, other_user: other_user, third_user: third_user]
138 test "addresses actor's follower address if the activity is public", %{
140 other_user: other_user,
141 third_user: third_user
143 expected_to = Enum.sort([user.ap_id, other_user.follower_address])
144 expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
147 CommonAPI.post(user, %{
149 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
152 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
153 assert Enum.sort(to) == expected_to
154 assert Enum.sort(cc) == expected_cc
157 test "does not adress actor's follower address if the activity is not public", %{
159 other_user: other_user,
160 third_user: third_user
162 expected_to = Enum.sort([user.ap_id])
163 expected_cc = [third_user.ap_id]
166 CommonAPI.post(user, %{
167 "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
168 "visibility" => "private"
171 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
172 assert Enum.sort(to) == expected_to
173 assert Enum.sort(cc) == expected_cc
177 describe "fetch_ordered_collection" do
180 test "fetches the first OrderedCollectionPage when an OrderedCollection is encountered" do
182 %{method: :get, url: "http://mastodon.com/outbox"} ->
183 json(%{"type" => "OrderedCollection", "first" => "http://mastodon.com/outbox?page=true"})
185 %{method: :get, url: "http://mastodon.com/outbox?page=true"} ->
186 json(%{"type" => "OrderedCollectionPage", "orderedItems" => ["ok"]})
189 assert Utils.fetch_ordered_collection("http://mastodon.com/outbox", 1) == ["ok"]
192 test "fetches several pages in the right order one after another, but only the specified amount" do
194 %{method: :get, url: "http://example.com/outbox"} ->
196 "type" => "OrderedCollectionPage",
197 "orderedItems" => [0],
198 "next" => "http://example.com/outbox?page=1"
201 %{method: :get, url: "http://example.com/outbox?page=1"} ->
203 "type" => "OrderedCollectionPage",
204 "orderedItems" => [1],
205 "next" => "http://example.com/outbox?page=2"
208 %{method: :get, url: "http://example.com/outbox?page=2"} ->
209 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [2]})
212 assert Utils.fetch_ordered_collection("http://example.com/outbox", 0) == [0]
213 assert Utils.fetch_ordered_collection("http://example.com/outbox", 1) == [0, 1]
216 test "returns an error if the url doesn't have an OrderedCollection/Page" do
218 %{method: :get, url: "http://example.com/not-an-outbox"} ->
219 json(%{"type" => "NotAnOutbox"})
222 assert {:error, _} = Utils.fetch_ordered_collection("http://example.com/not-an-outbox", 1)
225 test "returns the what was collected if there are less pages than specified" do
227 %{method: :get, url: "http://example.com/outbox"} ->
229 "type" => "OrderedCollectionPage",
230 "orderedItems" => [0],
231 "next" => "http://example.com/outbox?page=1"
234 %{method: :get, url: "http://example.com/outbox?page=1"} ->
235 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [1]})
238 assert Utils.fetch_ordered_collection("http://example.com/outbox", 5) == [0, 1]
242 test "make_json_ld_header/0" do
243 assert Utils.make_json_ld_header() == %{
245 "https://www.w3.org/ns/activitystreams",
246 "http://localhost:4001/schemas/litepub-0.1.jsonld",
254 describe "get_existing_votes" do
255 test "fetches existing votes" do
257 other_user = insert(:user)
260 CommonAPI.post(user, %{
261 "status" => "How do I pronounce LaTeX?",
263 "options" => ["laytekh", "lahtekh", "latex"],
269 object = Object.normalize(activity)
270 {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
271 assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
274 test "fetches only Create activities" do
276 other_user = insert(:user)
279 CommonAPI.post(user, %{
280 "status" => "Are we living in a society?",
282 "options" => ["yes", "no"],
287 object = Object.normalize(activity)
288 {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
289 vote_object = Object.normalize(vote)
290 {:ok, _activity, _object} = ActivityPub.like(user, vote_object)
291 [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
292 assert fetched_vote.id == vote.id
296 describe "update_follow_state_for_all/2" do
297 test "updates the state of all Follow activities with the same actor and object" do
298 user = insert(:user, info: %{locked: true})
299 follower = insert(:user)
301 {:ok, follow_activity} = ActivityPub.follow(follower, user)
302 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
305 follow_activity_two.data
306 |> Map.put("state", "accept")
308 cng = Ecto.Changeset.change(follow_activity_two, data: data)
310 {:ok, follow_activity_two} = Repo.update(cng)
312 {:ok, follow_activity_two} =
313 Utils.update_follow_state_for_all(follow_activity_two, "accept")
315 assert refresh_record(follow_activity).data["state"] == "accept"
316 assert refresh_record(follow_activity_two).data["state"] == "accept"
320 describe "update_follow_state/2" do
321 test "updates the state of the given follow activity" do
322 user = insert(:user, info: %{locked: true})
323 follower = insert(:user)
325 {:ok, follow_activity} = ActivityPub.follow(follower, user)
326 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
329 follow_activity_two.data
330 |> Map.put("state", "accept")
332 cng = Ecto.Changeset.change(follow_activity_two, data: data)
334 {:ok, follow_activity_two} = Repo.update(cng)
336 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
338 assert refresh_record(follow_activity).data["state"] == "pending"
339 assert refresh_record(follow_activity_two).data["state"] == "reject"
343 describe "update_element_in_object/3" do
344 test "updates likes" do
346 activity = insert(:note_activity)
347 object = Object.normalize(activity)
349 assert {:ok, updated_object} =
350 Utils.update_element_in_object(
356 assert updated_object.data["likes"] == [user.ap_id]
357 assert updated_object.data["like_count"] == 1
361 describe "add_like_to_object/2" do
362 test "add actor to likes" do
364 user2 = insert(:user)
365 object = insert(:note)
367 assert {:ok, updated_object} =
368 Utils.add_like_to_object(
369 %Activity{data: %{"actor" => user.ap_id}},
373 assert updated_object.data["likes"] == [user.ap_id]
374 assert updated_object.data["like_count"] == 1
376 assert {:ok, updated_object2} =
377 Utils.add_like_to_object(
378 %Activity{data: %{"actor" => user2.ap_id}},
382 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
383 assert updated_object2.data["like_count"] == 2
387 describe "remove_like_from_object/2" do
388 test "removes ap_id from likes" do
390 user2 = insert(:user)
391 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
393 assert {:ok, updated_object} =
394 Utils.remove_like_from_object(
395 %Activity{data: %{"actor" => user.ap_id}},
399 assert updated_object.data["likes"] == [user2.ap_id]
400 assert updated_object.data["like_count"] == 1
404 describe "get_existing_like/2" do
405 test "fetches existing like" do
406 note_activity = insert(:note_activity)
407 assert object = Object.normalize(note_activity)
410 refute Utils.get_existing_like(user.ap_id, object)
411 {:ok, like_activity, _object} = ActivityPub.like(user, object)
413 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
417 describe "get_get_existing_announce/2" do
418 test "returns nil if announce not found" do
419 actor = insert(:user)
420 refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}})
423 test "fetches existing announce" do
424 note_activity = insert(:note_activity)
425 assert object = Object.normalize(note_activity)
426 actor = insert(:user)
428 {:ok, announce, _object} = ActivityPub.announce(actor, object)
429 assert Utils.get_existing_announce(actor.ap_id, object) == announce
433 describe "fetch_latest_block/2" do
434 test "fetches last block activities" do
435 user1 = insert(:user)
436 user2 = insert(:user)
438 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
439 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
440 assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2)
442 assert Utils.fetch_latest_block(user1, user2) == activity
446 describe "recipient_in_message/3" do
447 test "returns true when recipient in `to`" do
448 recipient = insert(:user)
449 actor = insert(:user)
450 assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id})
452 assert Utils.recipient_in_message(
455 %{"to" => [recipient.ap_id], "cc" => ""}
459 test "returns true when recipient in `cc`" do
460 recipient = insert(:user)
461 actor = insert(:user)
462 assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id})
464 assert Utils.recipient_in_message(
467 %{"cc" => [recipient.ap_id], "to" => ""}
471 test "returns true when recipient in `bto`" do
472 recipient = insert(:user)
473 actor = insert(:user)
474 assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id})
476 assert Utils.recipient_in_message(
479 %{"bcc" => "", "bto" => [recipient.ap_id]}
483 test "returns true when recipient in `bcc`" do
484 recipient = insert(:user)
485 actor = insert(:user)
486 assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id})
488 assert Utils.recipient_in_message(
491 %{"bto" => "", "bcc" => [recipient.ap_id]}
495 test "returns true when message without addresses fields" do
496 recipient = insert(:user)
497 actor = insert(:user)
498 assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id})
500 assert Utils.recipient_in_message(
503 %{"btod" => "", "bccc" => [recipient.ap_id]}
507 test "returns false" do
508 recipient = insert(:user)
509 actor = insert(:user)
510 refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"})
514 describe "lazy_put_activity_defaults/2" do
515 test "returns map with id and published data" do
516 note_activity = insert(:note_activity)
517 object = Object.normalize(note_activity)
518 res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
519 assert res["context"] == object.data["id"]
520 assert res["context_id"] == object.id
522 assert res["published"]
525 test "returns map with fake id and published data" do
527 "context" => "pleroma:fakecontext",
529 "id" => "pleroma:fakeid",
531 } = Utils.lazy_put_activity_defaults(%{}, true)
534 test "returns activity data with object" do
535 note_activity = insert(:note_activity)
536 object = Object.normalize(note_activity)
539 Utils.lazy_put_activity_defaults(%{
540 "context" => object.data["id"],
544 assert res["context"] == object.data["id"]
545 assert res["context_id"] == object.id
547 assert res["published"]
548 assert res["object"]["id"]
549 assert res["object"]["published"]
550 assert res["object"]["context"] == object.data["id"]
551 assert res["object"]["context_id"] == object.id
555 describe "make_flag_data" do
556 test "returns empty map when params is invalid" do
557 assert Utils.make_flag_data(%{}, %{}) == %{}
560 test "returns map with Flag object" do
561 reporter = insert(:user)
562 target_account = insert(:user)
563 {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"})
564 context = Utils.generate_context_id()
567 target_ap_id = target_account.ap_id
568 activity_ap_id = activity.data["id"]
571 Utils.make_flag_data(
575 account: target_account,
576 statuses: [%{"id" => activity.data["id"]}],
584 "content" => ^content,
585 "context" => ^context,
586 "object" => [^target_ap_id, ^activity_ap_id],
592 describe "add_announce_to_object/2" do
593 test "adds actor to announcement" do
595 object = insert(:note)
598 insert(:note_activity,
600 "actor" => user.ap_id,
601 "cc" => [Pleroma.Constants.as_public()]
605 assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object)
606 assert updated_object.data["announcements"] == [user.ap_id]
607 assert updated_object.data["announcement_count"] == 1
611 describe "remove_announce_from_object/2" do
612 test "removes actor from announcements" do
614 user2 = insert(:user)
618 data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2}
621 activity = insert(:note_activity, data: %{"actor" => user.ap_id})
623 assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object)
624 assert updated_object.data["announcements"] == [user2.ap_id]
625 assert updated_object.data["announcement_count"] == 1