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) == []
92 describe "make_unlike_data/3" do
93 test "returns data for unlike activity" do
95 like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"})
97 object = Object.normalize(like_activity.data["object"])
99 assert Utils.make_unlike_data(user, like_activity, nil) == %{
101 "actor" => user.ap_id,
102 "object" => like_activity.data,
103 "to" => [user.follower_address, object.data["actor"]],
104 "cc" => [Pleroma.Constants.as_public()],
105 "context" => like_activity.data["context"]
108 assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
110 "actor" => user.ap_id,
111 "object" => like_activity.data,
112 "to" => [user.follower_address, object.data["actor"]],
113 "cc" => [Pleroma.Constants.as_public()],
114 "context" => like_activity.data["context"],
115 "id" => "9mJEZK0tky1w2xD2vY"
120 describe "make_like_data" do
123 other_user = insert(:user)
124 third_user = insert(:user)
125 [user: user, other_user: other_user, third_user: third_user]
128 test "addresses actor's follower address if the activity is public", %{
130 other_user: other_user,
131 third_user: third_user
133 expected_to = Enum.sort([user.ap_id, other_user.follower_address])
134 expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
137 CommonAPI.post(user, %{
139 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
142 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
143 assert Enum.sort(to) == expected_to
144 assert Enum.sort(cc) == expected_cc
147 test "does not adress actor's follower address if the activity is not public", %{
149 other_user: other_user,
150 third_user: third_user
152 expected_to = Enum.sort([user.ap_id])
153 expected_cc = [third_user.ap_id]
156 CommonAPI.post(user, %{
157 "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
158 "visibility" => "private"
161 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
162 assert Enum.sort(to) == expected_to
163 assert Enum.sort(cc) == expected_cc
167 describe "fetch_ordered_collection" do
170 test "fetches the first OrderedCollectionPage when an OrderedCollection is encountered" do
172 %{method: :get, url: "http://mastodon.com/outbox"} ->
173 json(%{"type" => "OrderedCollection", "first" => "http://mastodon.com/outbox?page=true"})
175 %{method: :get, url: "http://mastodon.com/outbox?page=true"} ->
176 json(%{"type" => "OrderedCollectionPage", "orderedItems" => ["ok"]})
179 assert Utils.fetch_ordered_collection("http://mastodon.com/outbox", 1) == ["ok"]
182 test "fetches several pages in the right order one after another, but only the specified amount" do
184 %{method: :get, url: "http://example.com/outbox"} ->
186 "type" => "OrderedCollectionPage",
187 "orderedItems" => [0],
188 "next" => "http://example.com/outbox?page=1"
191 %{method: :get, url: "http://example.com/outbox?page=1"} ->
193 "type" => "OrderedCollectionPage",
194 "orderedItems" => [1],
195 "next" => "http://example.com/outbox?page=2"
198 %{method: :get, url: "http://example.com/outbox?page=2"} ->
199 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [2]})
202 assert Utils.fetch_ordered_collection("http://example.com/outbox", 0) == [0]
203 assert Utils.fetch_ordered_collection("http://example.com/outbox", 1) == [0, 1]
206 test "returns an error if the url doesn't have an OrderedCollection/Page" do
208 %{method: :get, url: "http://example.com/not-an-outbox"} ->
209 json(%{"type" => "NotAnOutbox"})
212 assert {:error, _} = Utils.fetch_ordered_collection("http://example.com/not-an-outbox", 1)
215 test "returns the what was collected if there are less pages than specified" do
217 %{method: :get, url: "http://example.com/outbox"} ->
219 "type" => "OrderedCollectionPage",
220 "orderedItems" => [0],
221 "next" => "http://example.com/outbox?page=1"
224 %{method: :get, url: "http://example.com/outbox?page=1"} ->
225 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [1]})
228 assert Utils.fetch_ordered_collection("http://example.com/outbox", 5) == [0, 1]
232 test "make_json_ld_header/0" do
233 assert Utils.make_json_ld_header() == %{
235 "https://www.w3.org/ns/activitystreams",
236 "http://localhost:4001/schemas/litepub-0.1.jsonld",
244 describe "get_existing_votes" do
245 test "fetches existing votes" do
247 other_user = insert(:user)
250 CommonAPI.post(user, %{
251 "status" => "How do I pronounce LaTeX?",
253 "options" => ["laytekh", "lahtekh", "latex"],
259 object = Object.normalize(activity)
260 {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
261 assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
264 test "fetches only Create activities" do
266 other_user = insert(:user)
269 CommonAPI.post(user, %{
270 "status" => "Are we living in a society?",
272 "options" => ["yes", "no"],
277 object = Object.normalize(activity)
278 {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
279 vote_object = Object.normalize(vote)
280 {:ok, _activity, _object} = ActivityPub.like(user, vote_object)
281 [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
282 assert fetched_vote.id == vote.id
286 describe "update_follow_state_for_all/2" do
287 test "updates the state of all Follow activities with the same actor and object" do
288 user = insert(:user, info: %{locked: true})
289 follower = insert(:user)
291 {:ok, follow_activity} = ActivityPub.follow(follower, user)
292 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
295 follow_activity_two.data
296 |> Map.put("state", "accept")
298 cng = Ecto.Changeset.change(follow_activity_two, data: data)
300 {:ok, follow_activity_two} = Repo.update(cng)
302 {:ok, follow_activity_two} =
303 Utils.update_follow_state_for_all(follow_activity_two, "accept")
305 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
306 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
310 describe "update_follow_state/2" do
311 test "updates the state of the given follow activity" do
312 user = insert(:user, info: %{locked: true})
313 follower = insert(:user)
315 {:ok, follow_activity} = ActivityPub.follow(follower, user)
316 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
319 follow_activity_two.data
320 |> Map.put("state", "accept")
322 cng = Ecto.Changeset.change(follow_activity_two, data: data)
324 {:ok, follow_activity_two} = Repo.update(cng)
326 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
328 assert Repo.get(Activity, follow_activity.id).data["state"] == "pending"
329 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
333 describe "update_element_in_object/3" do
334 test "updates likes" do
336 activity = insert(:note_activity)
337 object = Object.normalize(activity)
339 assert {:ok, updated_object} =
340 Utils.update_element_in_object(
346 assert updated_object.data["likes"] == [user.ap_id]
347 assert updated_object.data["like_count"] == 1
351 describe "add_like_to_object/2" do
352 test "add actor to likes" do
354 user2 = insert(:user)
355 object = insert(:note)
357 assert {:ok, updated_object} =
358 Utils.add_like_to_object(
359 %Activity{data: %{"actor" => user.ap_id}},
363 assert updated_object.data["likes"] == [user.ap_id]
364 assert updated_object.data["like_count"] == 1
366 assert {:ok, updated_object2} =
367 Utils.add_like_to_object(
368 %Activity{data: %{"actor" => user2.ap_id}},
372 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
373 assert updated_object2.data["like_count"] == 2
377 describe "remove_like_from_object/2" do
378 test "removes ap_id from likes" do
380 user2 = insert(:user)
381 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
383 assert {:ok, updated_object} =
384 Utils.remove_like_from_object(
385 %Activity{data: %{"actor" => user.ap_id}},
389 assert updated_object.data["likes"] == [user2.ap_id]
390 assert updated_object.data["like_count"] == 1
394 describe "get_existing_like/2" do
395 test "fetches existing like" do
396 note_activity = insert(:note_activity)
397 assert object = Object.normalize(note_activity)
400 refute Utils.get_existing_like(user.ap_id, object)
401 {:ok, like_activity, _object} = ActivityPub.like(user, object)
403 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)