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.AdminAPI.AccountView
14 alias Pleroma.Web.CommonAPI
16 import Pleroma.Factory
18 require Pleroma.Constants
20 describe "fetch the latest Follow" do
21 test "fetches the latest Follow activity" do
22 %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
23 follower = User.get_cached_by_ap_id(activity.data["actor"])
24 followed = User.get_cached_by_ap_id(activity.data["object"])
26 assert activity == Utils.fetch_latest_follow(follower, followed)
30 describe "fetch the latest Block" do
31 test "fetches the latest Block activity" do
32 blocker = insert(:user)
33 blocked = insert(:user)
34 {:ok, activity} = ActivityPub.block(blocker, blocked)
36 assert activity == Utils.fetch_latest_block(blocker, blocked)
40 describe "determine_explicit_mentions()" do
41 test "works with an object that has mentions" do
46 "href" => "https://example.com/~alyssa",
47 "name" => "Alyssa P. Hacker"
52 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
55 test "works with an object that does not have mentions" do
58 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
62 assert Utils.determine_explicit_mentions(object) == []
65 test "works with an object that has mentions and other tags" do
70 "href" => "https://example.com/~alyssa",
71 "name" => "Alyssa P. Hacker"
73 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
77 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
80 test "works with an object that has no tags" do
83 assert Utils.determine_explicit_mentions(object) == []
86 test "works with an object that has only IR tags" do
87 object = %{"tag" => ["2hu"]}
89 assert Utils.determine_explicit_mentions(object) == []
92 test "works with an object has tags as map" do
96 "href" => "https://example.com/~alyssa",
97 "name" => "Alyssa P. Hacker"
101 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
105 describe "make_unlike_data/3" do
106 test "returns data for unlike activity" do
108 like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"})
110 object = Object.normalize(like_activity.data["object"])
112 assert Utils.make_unlike_data(user, like_activity, nil) == %{
114 "actor" => user.ap_id,
115 "object" => like_activity.data,
116 "to" => [user.follower_address, object.data["actor"]],
117 "cc" => [Pleroma.Constants.as_public()],
118 "context" => like_activity.data["context"]
121 assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
123 "actor" => user.ap_id,
124 "object" => like_activity.data,
125 "to" => [user.follower_address, object.data["actor"]],
126 "cc" => [Pleroma.Constants.as_public()],
127 "context" => like_activity.data["context"],
128 "id" => "9mJEZK0tky1w2xD2vY"
133 describe "make_like_data" do
136 other_user = insert(:user)
137 third_user = insert(:user)
138 [user: user, other_user: other_user, third_user: third_user]
141 test "addresses actor's follower address if the activity is public", %{
143 other_user: other_user,
144 third_user: third_user
146 expected_to = Enum.sort([user.ap_id, other_user.follower_address])
147 expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
150 CommonAPI.post(user, %{
152 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
155 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
156 assert Enum.sort(to) == expected_to
157 assert Enum.sort(cc) == expected_cc
160 test "does not adress actor's follower address if the activity is not public", %{
162 other_user: other_user,
163 third_user: third_user
165 expected_to = Enum.sort([user.ap_id])
166 expected_cc = [third_user.ap_id]
169 CommonAPI.post(user, %{
170 "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
171 "visibility" => "private"
174 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
175 assert Enum.sort(to) == expected_to
176 assert Enum.sort(cc) == expected_cc
180 describe "fetch_ordered_collection" do
183 test "fetches the first OrderedCollectionPage when an OrderedCollection is encountered" do
185 %{method: :get, url: "http://mastodon.com/outbox"} ->
186 json(%{"type" => "OrderedCollection", "first" => "http://mastodon.com/outbox?page=true"})
188 %{method: :get, url: "http://mastodon.com/outbox?page=true"} ->
189 json(%{"type" => "OrderedCollectionPage", "orderedItems" => ["ok"]})
192 assert Utils.fetch_ordered_collection("http://mastodon.com/outbox", 1) == ["ok"]
195 test "fetches several pages in the right order one after another, but only the specified amount" do
197 %{method: :get, url: "http://example.com/outbox"} ->
199 "type" => "OrderedCollectionPage",
200 "orderedItems" => [0],
201 "next" => "http://example.com/outbox?page=1"
204 %{method: :get, url: "http://example.com/outbox?page=1"} ->
206 "type" => "OrderedCollectionPage",
207 "orderedItems" => [1],
208 "next" => "http://example.com/outbox?page=2"
211 %{method: :get, url: "http://example.com/outbox?page=2"} ->
212 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [2]})
215 assert Utils.fetch_ordered_collection("http://example.com/outbox", 0) == [0]
216 assert Utils.fetch_ordered_collection("http://example.com/outbox", 1) == [0, 1]
219 test "returns an error if the url doesn't have an OrderedCollection/Page" do
221 %{method: :get, url: "http://example.com/not-an-outbox"} ->
222 json(%{"type" => "NotAnOutbox"})
225 assert {:error, _} = Utils.fetch_ordered_collection("http://example.com/not-an-outbox", 1)
228 test "returns the what was collected if there are less pages than specified" do
230 %{method: :get, url: "http://example.com/outbox"} ->
232 "type" => "OrderedCollectionPage",
233 "orderedItems" => [0],
234 "next" => "http://example.com/outbox?page=1"
237 %{method: :get, url: "http://example.com/outbox?page=1"} ->
238 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [1]})
241 assert Utils.fetch_ordered_collection("http://example.com/outbox", 5) == [0, 1]
245 test "make_json_ld_header/0" do
246 assert Utils.make_json_ld_header() == %{
248 "https://www.w3.org/ns/activitystreams",
249 "http://localhost:4001/schemas/litepub-0.1.jsonld",
257 describe "get_existing_votes" do
258 test "fetches existing votes" do
260 other_user = insert(:user)
263 CommonAPI.post(user, %{
264 "status" => "How do I pronounce LaTeX?",
266 "options" => ["laytekh", "lahtekh", "latex"],
272 object = Object.normalize(activity)
273 {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
274 assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
277 test "fetches only Create activities" do
279 other_user = insert(:user)
282 CommonAPI.post(user, %{
283 "status" => "Are we living in a society?",
285 "options" => ["yes", "no"],
290 object = Object.normalize(activity)
291 {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
292 vote_object = Object.normalize(vote)
293 {:ok, _activity, _object} = ActivityPub.like(user, vote_object)
294 [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
295 assert fetched_vote.id == vote.id
299 describe "update_follow_state_for_all/2" do
300 test "updates the state of all Follow activities with the same actor and object" do
301 user = insert(:user, locked: true)
302 follower = insert(:user)
304 {:ok, follow_activity} = ActivityPub.follow(follower, user)
305 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
308 follow_activity_two.data
309 |> Map.put("state", "accept")
311 cng = Ecto.Changeset.change(follow_activity_two, data: data)
313 {:ok, follow_activity_two} = Repo.update(cng)
315 {:ok, follow_activity_two} =
316 Utils.update_follow_state_for_all(follow_activity_two, "accept")
318 assert refresh_record(follow_activity).data["state"] == "accept"
319 assert refresh_record(follow_activity_two).data["state"] == "accept"
323 describe "update_follow_state/2" do
324 test "updates the state of the given follow activity" do
325 user = insert(:user, locked: true)
326 follower = insert(:user)
328 {:ok, follow_activity} = ActivityPub.follow(follower, user)
329 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
332 follow_activity_two.data
333 |> Map.put("state", "accept")
335 cng = Ecto.Changeset.change(follow_activity_two, data: data)
337 {:ok, follow_activity_two} = Repo.update(cng)
339 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
341 assert refresh_record(follow_activity).data["state"] == "pending"
342 assert refresh_record(follow_activity_two).data["state"] == "reject"
346 describe "update_element_in_object/3" do
347 test "updates likes" do
349 activity = insert(:note_activity)
350 object = Object.normalize(activity)
352 assert {:ok, updated_object} =
353 Utils.update_element_in_object(
359 assert updated_object.data["likes"] == [user.ap_id]
360 assert updated_object.data["like_count"] == 1
364 describe "add_like_to_object/2" do
365 test "add actor to likes" do
367 user2 = insert(:user)
368 object = insert(:note)
370 assert {:ok, updated_object} =
371 Utils.add_like_to_object(
372 %Activity{data: %{"actor" => user.ap_id}},
376 assert updated_object.data["likes"] == [user.ap_id]
377 assert updated_object.data["like_count"] == 1
379 assert {:ok, updated_object2} =
380 Utils.add_like_to_object(
381 %Activity{data: %{"actor" => user2.ap_id}},
385 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
386 assert updated_object2.data["like_count"] == 2
390 describe "remove_like_from_object/2" do
391 test "removes ap_id from likes" do
393 user2 = insert(:user)
394 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
396 assert {:ok, updated_object} =
397 Utils.remove_like_from_object(
398 %Activity{data: %{"actor" => user.ap_id}},
402 assert updated_object.data["likes"] == [user2.ap_id]
403 assert updated_object.data["like_count"] == 1
407 describe "get_existing_like/2" do
408 test "fetches existing like" do
409 note_activity = insert(:note_activity)
410 assert object = Object.normalize(note_activity)
413 refute Utils.get_existing_like(user.ap_id, object)
414 {:ok, like_activity, _object} = ActivityPub.like(user, object)
416 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
420 describe "get_get_existing_announce/2" do
421 test "returns nil if announce not found" do
422 actor = insert(:user)
423 refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}})
426 test "fetches existing announce" do
427 note_activity = insert(:note_activity)
428 assert object = Object.normalize(note_activity)
429 actor = insert(:user)
431 {:ok, announce, _object} = ActivityPub.announce(actor, object)
432 assert Utils.get_existing_announce(actor.ap_id, object) == announce
436 describe "fetch_latest_block/2" do
437 test "fetches last block activities" do
438 user1 = insert(:user)
439 user2 = insert(:user)
441 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
442 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
443 assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2)
445 assert Utils.fetch_latest_block(user1, user2) == activity
449 describe "recipient_in_message/3" do
450 test "returns true when recipient in `to`" do
451 recipient = insert(:user)
452 actor = insert(:user)
453 assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id})
455 assert Utils.recipient_in_message(
458 %{"to" => [recipient.ap_id], "cc" => ""}
462 test "returns true when recipient in `cc`" do
463 recipient = insert(:user)
464 actor = insert(:user)
465 assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id})
467 assert Utils.recipient_in_message(
470 %{"cc" => [recipient.ap_id], "to" => ""}
474 test "returns true when recipient in `bto`" do
475 recipient = insert(:user)
476 actor = insert(:user)
477 assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id})
479 assert Utils.recipient_in_message(
482 %{"bcc" => "", "bto" => [recipient.ap_id]}
486 test "returns true when recipient in `bcc`" do
487 recipient = insert(:user)
488 actor = insert(:user)
489 assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id})
491 assert Utils.recipient_in_message(
494 %{"bto" => "", "bcc" => [recipient.ap_id]}
498 test "returns true when message without addresses fields" do
499 recipient = insert(:user)
500 actor = insert(:user)
501 assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id})
503 assert Utils.recipient_in_message(
506 %{"btod" => "", "bccc" => [recipient.ap_id]}
510 test "returns false" do
511 recipient = insert(:user)
512 actor = insert(:user)
513 refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"})
517 describe "lazy_put_activity_defaults/2" do
518 test "returns map with id and published data" do
519 note_activity = insert(:note_activity)
520 object = Object.normalize(note_activity)
521 res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
522 assert res["context"] == object.data["id"]
523 assert res["context_id"] == object.id
525 assert res["published"]
528 test "returns map with fake id and published data" do
530 "context" => "pleroma:fakecontext",
532 "id" => "pleroma:fakeid",
534 } = Utils.lazy_put_activity_defaults(%{}, true)
537 test "returns activity data with object" do
538 note_activity = insert(:note_activity)
539 object = Object.normalize(note_activity)
542 Utils.lazy_put_activity_defaults(%{
543 "context" => object.data["id"],
547 assert res["context"] == object.data["id"]
548 assert res["context_id"] == object.id
550 assert res["published"]
551 assert res["object"]["id"]
552 assert res["object"]["published"]
553 assert res["object"]["context"] == object.data["id"]
554 assert res["object"]["context_id"] == object.id
558 describe "make_flag_data" do
559 test "returns empty map when params is invalid" do
560 assert Utils.make_flag_data(%{}, %{}) == %{}
563 test "returns map with Flag object" do
564 reporter = insert(:user)
565 target_account = insert(:user)
566 {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"})
567 context = Utils.generate_context_id()
570 target_ap_id = target_account.ap_id
571 activity_ap_id = activity.data["id"]
574 Utils.make_flag_data(
578 account: target_account,
579 statuses: [%{"id" => activity.data["id"]}],
587 "id" => activity_ap_id,
588 "content" => content,
589 "published" => activity.object.data["published"],
590 "actor" => AccountView.render("show.json", %{user: target_account})
595 "content" => ^content,
596 "context" => ^context,
597 "object" => [^target_ap_id, ^note_obj],
603 describe "add_announce_to_object/2" do
604 test "adds actor to announcement" do
606 object = insert(:note)
609 insert(:note_activity,
611 "actor" => user.ap_id,
612 "cc" => [Pleroma.Constants.as_public()]
616 assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object)
617 assert updated_object.data["announcements"] == [user.ap_id]
618 assert updated_object.data["announcement_count"] == 1
622 describe "remove_announce_from_object/2" do
623 test "removes actor from announcements" do
625 user2 = insert(:user)
629 data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2}
632 activity = insert(:note_activity, data: %{"actor" => user.ap_id})
634 assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object)
635 assert updated_object.data["announcements"] == [user2.ap_id]
636 assert updated_object.data["announcement_count"] == 1
640 describe "get_reports_grouped_by_status/1" do
642 [reporter, target_user] = insert_pair(:user)
643 first_status = insert(:note_activity, user: target_user)
644 second_status = insert(:note_activity, user: target_user)
646 CommonAPI.report(reporter, %{
647 "account_id" => target_user.id,
648 "comment" => "I feel offended",
649 "status_ids" => [first_status.id]
652 CommonAPI.report(reporter, %{
653 "account_id" => target_user.id,
654 "comment" => "I feel offended2",
655 "status_ids" => [second_status.id]
658 data = [%{activity: first_status.data["id"]}, %{activity: second_status.data["id"]}]
662 first_status: first_status,
663 second_status: second_status,
668 test "works for deprecated reports format", %{
669 first_status: first_status,
670 second_status: second_status,
673 groups = Utils.get_reports_grouped_by_status(data).groups
675 first_group = Enum.find(groups, &(&1.status.id == first_status.data["id"]))
676 second_group = Enum.find(groups, &(&1.status.id == second_status.data["id"]))
678 assert first_group.status.id == first_status.data["id"]
679 assert second_group.status.id == second_status.data["id"]