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 object = Object.normalize(like_activity.data["object"])
111 assert Utils.make_unlike_data(user, like_activity, nil) == %{
113 "actor" => user.ap_id,
114 "object" => like_activity.data,
115 "to" => [user.follower_address, object.data["actor"]],
116 "cc" => [Pleroma.Constants.as_public()],
117 "context" => like_activity.data["context"]
120 assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
122 "actor" => user.ap_id,
123 "object" => like_activity.data,
124 "to" => [user.follower_address, object.data["actor"]],
125 "cc" => [Pleroma.Constants.as_public()],
126 "context" => like_activity.data["context"],
127 "id" => "9mJEZK0tky1w2xD2vY"
132 describe "make_like_data" do
135 other_user = insert(:user)
136 third_user = insert(:user)
137 [user: user, other_user: other_user, third_user: third_user]
140 test "addresses actor's follower address if the activity is public", %{
142 other_user: other_user,
143 third_user: third_user
145 expected_to = Enum.sort([user.ap_id, other_user.follower_address])
146 expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
149 CommonAPI.post(user, %{
151 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
154 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
155 assert Enum.sort(to) == expected_to
156 assert Enum.sort(cc) == expected_cc
159 test "does not adress actor's follower address if the activity is not public", %{
161 other_user: other_user,
162 third_user: third_user
164 expected_to = Enum.sort([user.ap_id])
165 expected_cc = [third_user.ap_id]
168 CommonAPI.post(user, %{
169 "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
170 "visibility" => "private"
173 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
174 assert Enum.sort(to) == expected_to
175 assert Enum.sort(cc) == expected_cc
179 describe "fetch_ordered_collection" do
182 test "fetches the first OrderedCollectionPage when an OrderedCollection is encountered" do
184 %{method: :get, url: "http://mastodon.com/outbox"} ->
185 json(%{"type" => "OrderedCollection", "first" => "http://mastodon.com/outbox?page=true"})
187 %{method: :get, url: "http://mastodon.com/outbox?page=true"} ->
188 json(%{"type" => "OrderedCollectionPage", "orderedItems" => ["ok"]})
191 assert Utils.fetch_ordered_collection("http://mastodon.com/outbox", 1) == ["ok"]
194 test "fetches several pages in the right order one after another, but only the specified amount" do
196 %{method: :get, url: "http://example.com/outbox"} ->
198 "type" => "OrderedCollectionPage",
199 "orderedItems" => [0],
200 "next" => "http://example.com/outbox?page=1"
203 %{method: :get, url: "http://example.com/outbox?page=1"} ->
205 "type" => "OrderedCollectionPage",
206 "orderedItems" => [1],
207 "next" => "http://example.com/outbox?page=2"
210 %{method: :get, url: "http://example.com/outbox?page=2"} ->
211 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [2]})
214 assert Utils.fetch_ordered_collection("http://example.com/outbox", 0) == [0]
215 assert Utils.fetch_ordered_collection("http://example.com/outbox", 1) == [0, 1]
218 test "returns an error if the url doesn't have an OrderedCollection/Page" do
220 %{method: :get, url: "http://example.com/not-an-outbox"} ->
221 json(%{"type" => "NotAnOutbox"})
224 assert {:error, _} = Utils.fetch_ordered_collection("http://example.com/not-an-outbox", 1)
227 test "returns the what was collected if there are less pages than specified" do
229 %{method: :get, url: "http://example.com/outbox"} ->
231 "type" => "OrderedCollectionPage",
232 "orderedItems" => [0],
233 "next" => "http://example.com/outbox?page=1"
236 %{method: :get, url: "http://example.com/outbox?page=1"} ->
237 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [1]})
240 assert Utils.fetch_ordered_collection("http://example.com/outbox", 5) == [0, 1]
244 test "make_json_ld_header/0" do
245 assert Utils.make_json_ld_header() == %{
247 "https://www.w3.org/ns/activitystreams",
248 "http://localhost:4001/schemas/litepub-0.1.jsonld",
256 describe "get_existing_votes" do
257 test "fetches existing votes" do
259 other_user = insert(:user)
262 CommonAPI.post(user, %{
263 "status" => "How do I pronounce LaTeX?",
265 "options" => ["laytekh", "lahtekh", "latex"],
271 object = Object.normalize(activity)
272 {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
273 assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
276 test "fetches only Create activities" do
278 other_user = insert(:user)
281 CommonAPI.post(user, %{
282 "status" => "Are we living in a society?",
284 "options" => ["yes", "no"],
289 object = Object.normalize(activity)
290 {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
291 vote_object = Object.normalize(vote)
292 {:ok, _activity, _object} = ActivityPub.like(user, vote_object)
293 [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
294 assert fetched_vote.id == vote.id
298 describe "update_follow_state_for_all/2" do
299 test "updates the state of all Follow activities with the same actor and object" do
300 user = insert(:user, locked: true)
301 follower = insert(:user)
303 {:ok, follow_activity} = ActivityPub.follow(follower, user)
304 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
307 follow_activity_two.data
308 |> Map.put("state", "accept")
310 cng = Ecto.Changeset.change(follow_activity_two, data: data)
312 {:ok, follow_activity_two} = Repo.update(cng)
314 {:ok, follow_activity_two} =
315 Utils.update_follow_state_for_all(follow_activity_two, "accept")
317 assert refresh_record(follow_activity).data["state"] == "accept"
318 assert refresh_record(follow_activity_two).data["state"] == "accept"
322 describe "update_follow_state/2" do
323 test "updates the state of the given follow activity" do
324 user = insert(:user, locked: true)
325 follower = insert(:user)
327 {:ok, follow_activity} = ActivityPub.follow(follower, user)
328 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
331 follow_activity_two.data
332 |> Map.put("state", "accept")
334 cng = Ecto.Changeset.change(follow_activity_two, data: data)
336 {:ok, follow_activity_two} = Repo.update(cng)
338 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
340 assert refresh_record(follow_activity).data["state"] == "pending"
341 assert refresh_record(follow_activity_two).data["state"] == "reject"
345 describe "update_element_in_object/3" do
346 test "updates likes" do
348 activity = insert(:note_activity)
349 object = Object.normalize(activity)
351 assert {:ok, updated_object} =
352 Utils.update_element_in_object(
358 assert updated_object.data["likes"] == [user.ap_id]
359 assert updated_object.data["like_count"] == 1
363 describe "add_like_to_object/2" do
364 test "add actor to likes" do
366 user2 = insert(:user)
367 object = insert(:note)
369 assert {:ok, updated_object} =
370 Utils.add_like_to_object(
371 %Activity{data: %{"actor" => user.ap_id}},
375 assert updated_object.data["likes"] == [user.ap_id]
376 assert updated_object.data["like_count"] == 1
378 assert {:ok, updated_object2} =
379 Utils.add_like_to_object(
380 %Activity{data: %{"actor" => user2.ap_id}},
384 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
385 assert updated_object2.data["like_count"] == 2
389 describe "remove_like_from_object/2" do
390 test "removes ap_id from likes" do
392 user2 = insert(:user)
393 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
395 assert {:ok, updated_object} =
396 Utils.remove_like_from_object(
397 %Activity{data: %{"actor" => user.ap_id}},
401 assert updated_object.data["likes"] == [user2.ap_id]
402 assert updated_object.data["like_count"] == 1
406 describe "get_existing_like/2" do
407 test "fetches existing like" do
408 note_activity = insert(:note_activity)
409 assert object = Object.normalize(note_activity)
412 refute Utils.get_existing_like(user.ap_id, object)
413 {:ok, like_activity, _object} = ActivityPub.like(user, object)
415 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
419 describe "get_get_existing_announce/2" do
420 test "returns nil if announce not found" do
421 actor = insert(:user)
422 refute Utils.get_existing_announce(actor.ap_id, %{data: %{"id" => "test"}})
425 test "fetches existing announce" do
426 note_activity = insert(:note_activity)
427 assert object = Object.normalize(note_activity)
428 actor = insert(:user)
430 {:ok, announce, _object} = ActivityPub.announce(actor, object)
431 assert Utils.get_existing_announce(actor.ap_id, object) == announce
435 describe "fetch_latest_block/2" do
436 test "fetches last block activities" do
437 user1 = insert(:user)
438 user2 = insert(:user)
440 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
441 assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
442 assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2)
444 assert Utils.fetch_latest_block(user1, user2) == activity
448 describe "recipient_in_message/3" do
449 test "returns true when recipient in `to`" do
450 recipient = insert(:user)
451 actor = insert(:user)
452 assert Utils.recipient_in_message(recipient, actor, %{"to" => recipient.ap_id})
454 assert Utils.recipient_in_message(
457 %{"to" => [recipient.ap_id], "cc" => ""}
461 test "returns true when recipient in `cc`" do
462 recipient = insert(:user)
463 actor = insert(:user)
464 assert Utils.recipient_in_message(recipient, actor, %{"cc" => recipient.ap_id})
466 assert Utils.recipient_in_message(
469 %{"cc" => [recipient.ap_id], "to" => ""}
473 test "returns true when recipient in `bto`" do
474 recipient = insert(:user)
475 actor = insert(:user)
476 assert Utils.recipient_in_message(recipient, actor, %{"bto" => recipient.ap_id})
478 assert Utils.recipient_in_message(
481 %{"bcc" => "", "bto" => [recipient.ap_id]}
485 test "returns true when recipient in `bcc`" do
486 recipient = insert(:user)
487 actor = insert(:user)
488 assert Utils.recipient_in_message(recipient, actor, %{"bcc" => recipient.ap_id})
490 assert Utils.recipient_in_message(
493 %{"bto" => "", "bcc" => [recipient.ap_id]}
497 test "returns true when message without addresses fields" do
498 recipient = insert(:user)
499 actor = insert(:user)
500 assert Utils.recipient_in_message(recipient, actor, %{"bccc" => recipient.ap_id})
502 assert Utils.recipient_in_message(
505 %{"btod" => "", "bccc" => [recipient.ap_id]}
509 test "returns false" do
510 recipient = insert(:user)
511 actor = insert(:user)
512 refute Utils.recipient_in_message(recipient, actor, %{"to" => "ap_id"})
516 describe "lazy_put_activity_defaults/2" do
517 test "returns map with id and published data" do
518 note_activity = insert(:note_activity)
519 object = Object.normalize(note_activity)
520 res = Utils.lazy_put_activity_defaults(%{"context" => object.data["id"]})
521 assert res["context"] == object.data["id"]
522 assert res["context_id"] == object.id
524 assert res["published"]
527 test "returns map with fake id and published data" do
529 "context" => "pleroma:fakecontext",
531 "id" => "pleroma:fakeid",
533 } = Utils.lazy_put_activity_defaults(%{}, true)
536 test "returns activity data with object" do
537 note_activity = insert(:note_activity)
538 object = Object.normalize(note_activity)
541 Utils.lazy_put_activity_defaults(%{
542 "context" => object.data["id"],
546 assert res["context"] == object.data["id"]
547 assert res["context_id"] == object.id
549 assert res["published"]
550 assert res["object"]["id"]
551 assert res["object"]["published"]
552 assert res["object"]["context"] == object.data["id"]
553 assert res["object"]["context_id"] == object.id
557 describe "make_flag_data" do
558 test "returns empty map when params is invalid" do
559 assert Utils.make_flag_data(%{}, %{}) == %{}
562 test "returns map with Flag object" do
563 reporter = insert(:user)
564 target_account = insert(:user)
565 {:ok, activity} = CommonAPI.post(target_account, %{"status" => "foobar"})
566 context = Utils.generate_context_id()
569 target_ap_id = target_account.ap_id
570 activity_ap_id = activity.data["id"]
573 Utils.make_flag_data(
577 account: target_account,
578 statuses: [%{"id" => activity.data["id"]}],
586 "content" => ^content,
587 "context" => ^context,
588 "object" => [^target_ap_id, ^activity_ap_id],
594 describe "add_announce_to_object/2" do
595 test "adds actor to announcement" do
597 object = insert(:note)
600 insert(:note_activity,
602 "actor" => user.ap_id,
603 "cc" => [Pleroma.Constants.as_public()]
607 assert {:ok, updated_object} = Utils.add_announce_to_object(activity, object)
608 assert updated_object.data["announcements"] == [user.ap_id]
609 assert updated_object.data["announcement_count"] == 1
613 describe "remove_announce_from_object/2" do
614 test "removes actor from announcements" do
616 user2 = insert(:user)
620 data: %{"announcements" => [user.ap_id, user2.ap_id], "announcement_count" => 2}
623 activity = insert(:note_activity, data: %{"actor" => user.ap_id})
625 assert {:ok, updated_object} = Utils.remove_announce_from_object(activity, object)
626 assert updated_object.data["announcements"] == [user2.ap_id]
627 assert updated_object.data["announcement_count"] == 1