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 assert Utils.make_unlike_data(user, like_activity, nil) == %{
99 "actor" => user.ap_id,
100 "object" => like_activity.data,
101 "to" => [user.follower_address, like_activity.data["actor"]],
102 "cc" => [Pleroma.Constants.as_public()],
103 "context" => like_activity.data["context"]
106 assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
108 "actor" => user.ap_id,
109 "object" => like_activity.data,
110 "to" => [user.follower_address, like_activity.data["actor"]],
111 "cc" => [Pleroma.Constants.as_public()],
112 "context" => like_activity.data["context"],
113 "id" => "9mJEZK0tky1w2xD2vY"
118 describe "make_like_data" do
121 other_user = insert(:user)
122 third_user = insert(:user)
123 [user: user, other_user: other_user, third_user: third_user]
126 test "addresses actor's follower address if the activity is public", %{
128 other_user: other_user,
129 third_user: third_user
131 expected_to = Enum.sort([user.ap_id, other_user.follower_address])
132 expected_cc = Enum.sort(["https://www.w3.org/ns/activitystreams#Public", third_user.ap_id])
135 CommonAPI.post(user, %{
137 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
140 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
141 assert Enum.sort(to) == expected_to
142 assert Enum.sort(cc) == expected_cc
145 test "does not adress actor's follower address if the activity is not public", %{
147 other_user: other_user,
148 third_user: third_user
150 expected_to = Enum.sort([user.ap_id])
151 expected_cc = [third_user.ap_id]
154 CommonAPI.post(user, %{
155 "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
156 "visibility" => "private"
159 %{"to" => to, "cc" => cc} = Utils.make_like_data(other_user, activity, nil)
160 assert Enum.sort(to) == expected_to
161 assert Enum.sort(cc) == expected_cc
165 describe "fetch_ordered_collection" do
168 test "fetches the first OrderedCollectionPage when an OrderedCollection is encountered" do
170 %{method: :get, url: "http://mastodon.com/outbox"} ->
171 json(%{"type" => "OrderedCollection", "first" => "http://mastodon.com/outbox?page=true"})
173 %{method: :get, url: "http://mastodon.com/outbox?page=true"} ->
174 json(%{"type" => "OrderedCollectionPage", "orderedItems" => ["ok"]})
177 assert Utils.fetch_ordered_collection("http://mastodon.com/outbox", 1) == ["ok"]
180 test "fetches several pages in the right order one after another, but only the specified amount" do
182 %{method: :get, url: "http://example.com/outbox"} ->
184 "type" => "OrderedCollectionPage",
185 "orderedItems" => [0],
186 "next" => "http://example.com/outbox?page=1"
189 %{method: :get, url: "http://example.com/outbox?page=1"} ->
191 "type" => "OrderedCollectionPage",
192 "orderedItems" => [1],
193 "next" => "http://example.com/outbox?page=2"
196 %{method: :get, url: "http://example.com/outbox?page=2"} ->
197 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [2]})
200 assert Utils.fetch_ordered_collection("http://example.com/outbox", 0) == [0]
201 assert Utils.fetch_ordered_collection("http://example.com/outbox", 1) == [0, 1]
204 test "returns an error if the url doesn't have an OrderedCollection/Page" do
206 %{method: :get, url: "http://example.com/not-an-outbox"} ->
207 json(%{"type" => "NotAnOutbox"})
210 assert {:error, _} = Utils.fetch_ordered_collection("http://example.com/not-an-outbox", 1)
213 test "returns the what was collected if there are less pages than specified" do
215 %{method: :get, url: "http://example.com/outbox"} ->
217 "type" => "OrderedCollectionPage",
218 "orderedItems" => [0],
219 "next" => "http://example.com/outbox?page=1"
222 %{method: :get, url: "http://example.com/outbox?page=1"} ->
223 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [1]})
226 assert Utils.fetch_ordered_collection("http://example.com/outbox", 5) == [0, 1]
230 test "make_json_ld_header/0" do
231 assert Utils.make_json_ld_header() == %{
233 "https://www.w3.org/ns/activitystreams",
234 "http://localhost:4001/schemas/litepub-0.1.jsonld",
242 describe "get_existing_votes" do
243 test "fetches existing votes" do
245 other_user = insert(:user)
248 CommonAPI.post(user, %{
249 "status" => "How do I pronounce LaTeX?",
251 "options" => ["laytekh", "lahtekh", "latex"],
257 object = Object.normalize(activity)
258 {:ok, votes, object} = CommonAPI.vote(other_user, object, [0, 1])
259 assert Enum.sort(Utils.get_existing_votes(other_user.ap_id, object)) == Enum.sort(votes)
262 test "fetches only Create activities" do
264 other_user = insert(:user)
267 CommonAPI.post(user, %{
268 "status" => "Are we living in a society?",
270 "options" => ["yes", "no"],
275 object = Object.normalize(activity)
276 {:ok, [vote], object} = CommonAPI.vote(other_user, object, [0])
277 vote_object = Object.normalize(vote)
278 {:ok, _activity, _object} = ActivityPub.like(user, vote_object)
279 [fetched_vote] = Utils.get_existing_votes(other_user.ap_id, object)
280 assert fetched_vote.id == vote.id
284 describe "update_follow_state_for_all/2" do
285 test "updates the state of all Follow activities with the same actor and object" do
286 user = insert(:user, info: %{locked: true})
287 follower = insert(:user)
289 {:ok, follow_activity} = ActivityPub.follow(follower, user)
290 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
293 follow_activity_two.data
294 |> Map.put("state", "accept")
296 cng = Ecto.Changeset.change(follow_activity_two, data: data)
298 {:ok, follow_activity_two} = Repo.update(cng)
300 {:ok, follow_activity_two} =
301 Utils.update_follow_state_for_all(follow_activity_two, "accept")
303 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
304 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
308 describe "update_follow_state/2" do
309 test "updates the state of the given follow activity" do
310 user = insert(:user, info: %{locked: true})
311 follower = insert(:user)
313 {:ok, follow_activity} = ActivityPub.follow(follower, user)
314 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
317 follow_activity_two.data
318 |> Map.put("state", "accept")
320 cng = Ecto.Changeset.change(follow_activity_two, data: data)
322 {:ok, follow_activity_two} = Repo.update(cng)
324 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
326 assert Repo.get(Activity, follow_activity.id).data["state"] == "pending"
327 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
331 describe "update_element_in_object/3" do
332 test "updates likes" do
334 activity = insert(:note_activity)
335 object = Object.normalize(activity)
337 assert {:ok, updated_object} =
338 Utils.update_element_in_object(
344 assert updated_object.data["likes"] == [user.ap_id]
345 assert updated_object.data["like_count"] == 1
349 describe "add_like_to_object/2" do
350 test "add actor to likes" do
352 user2 = insert(:user)
353 object = insert(:note)
355 assert {:ok, updated_object} =
356 Utils.add_like_to_object(
357 %Activity{data: %{"actor" => user.ap_id}},
361 assert updated_object.data["likes"] == [user.ap_id]
362 assert updated_object.data["like_count"] == 1
364 assert {:ok, updated_object2} =
365 Utils.add_like_to_object(
366 %Activity{data: %{"actor" => user2.ap_id}},
370 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
371 assert updated_object2.data["like_count"] == 2
375 describe "remove_like_from_object/2" do
376 test "removes ap_id from likes" do
378 user2 = insert(:user)
379 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
381 assert {:ok, updated_object} =
382 Utils.remove_like_from_object(
383 %Activity{data: %{"actor" => user.ap_id}},
387 assert updated_object.data["likes"] == [user2.ap_id]
388 assert updated_object.data["like_count"] == 1
392 describe "get_existing_like/2" do
393 test "fetches existing like" do
394 note_activity = insert(:note_activity)
395 assert object = Object.normalize(note_activity)
398 refute Utils.get_existing_like(user.ap_id, object)
399 {:ok, like_activity, _object} = ActivityPub.like(user, object)
401 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)