Merge branch 'tests/activity_pub' into 'develop'
[akkoma] / test / web / activity_pub / utils_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.UtilsTest do
6 use Pleroma.DataCase
7 alias Pleroma.Activity
8 alias Pleroma.Object
9 alias Pleroma.Repo
10 alias Pleroma.User
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.Utils
13 alias Pleroma.Web.CommonAPI
14
15 import Pleroma.Factory
16
17 require Pleroma.Constants
18
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"])
24
25 assert activity == Utils.fetch_latest_follow(follower, followed)
26 end
27 end
28
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)
34
35 assert activity == Utils.fetch_latest_block(blocker, blocked)
36 end
37 end
38
39 describe "determine_explicit_mentions()" do
40 test "works with an object that has mentions" do
41 object = %{
42 "tag" => [
43 %{
44 "type" => "Mention",
45 "href" => "https://example.com/~alyssa",
46 "name" => "Alyssa P. Hacker"
47 }
48 ]
49 }
50
51 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
52 end
53
54 test "works with an object that does not have mentions" do
55 object = %{
56 "tag" => [
57 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
58 ]
59 }
60
61 assert Utils.determine_explicit_mentions(object) == []
62 end
63
64 test "works with an object that has mentions and other tags" do
65 object = %{
66 "tag" => [
67 %{
68 "type" => "Mention",
69 "href" => "https://example.com/~alyssa",
70 "name" => "Alyssa P. Hacker"
71 },
72 %{"type" => "Hashtag", "href" => "https://example.com/tag/2hu", "name" => "2hu"}
73 ]
74 }
75
76 assert Utils.determine_explicit_mentions(object) == ["https://example.com/~alyssa"]
77 end
78
79 test "works with an object that has no tags" do
80 object = %{}
81
82 assert Utils.determine_explicit_mentions(object) == []
83 end
84
85 test "works with an object that has only IR tags" do
86 object = %{"tag" => ["2hu"]}
87
88 assert Utils.determine_explicit_mentions(object) == []
89 end
90 end
91
92 describe "make_unlike_data/3" do
93 test "returns data for unlike activity" do
94 user = insert(:user)
95 like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"})
96
97 assert Utils.make_unlike_data(user, like_activity, nil) == %{
98 "type" => "Undo",
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"]
104 }
105
106 assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
107 "type" => "Undo",
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"
114 }
115 end
116 end
117
118 describe "make_like_data" do
119 setup do
120 user = insert(:user)
121 other_user = insert(:user)
122 third_user = insert(:user)
123 [user: user, other_user: other_user, third_user: third_user]
124 end
125
126 test "addresses actor's follower address if the activity is public", %{
127 user: user,
128 other_user: other_user,
129 third_user: third_user
130 } do
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])
133
134 {:ok, activity} =
135 CommonAPI.post(user, %{
136 "status" =>
137 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
138 })
139
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
143 end
144
145 test "does not adress actor's follower address if the activity is not public", %{
146 user: user,
147 other_user: other_user,
148 third_user: third_user
149 } do
150 expected_to = Enum.sort([user.ap_id])
151 expected_cc = [third_user.ap_id]
152
153 {:ok, activity} =
154 CommonAPI.post(user, %{
155 "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
156 "visibility" => "private"
157 })
158
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
162 end
163 end
164
165 describe "fetch_ordered_collection" do
166 import Tesla.Mock
167
168 test "fetches the first OrderedCollectionPage when an OrderedCollection is encountered" do
169 mock(fn
170 %{method: :get, url: "http://mastodon.com/outbox"} ->
171 json(%{"type" => "OrderedCollection", "first" => "http://mastodon.com/outbox?page=true"})
172
173 %{method: :get, url: "http://mastodon.com/outbox?page=true"} ->
174 json(%{"type" => "OrderedCollectionPage", "orderedItems" => ["ok"]})
175 end)
176
177 assert Utils.fetch_ordered_collection("http://mastodon.com/outbox", 1) == ["ok"]
178 end
179
180 test "fetches several pages in the right order one after another, but only the specified amount" do
181 mock(fn
182 %{method: :get, url: "http://example.com/outbox"} ->
183 json(%{
184 "type" => "OrderedCollectionPage",
185 "orderedItems" => [0],
186 "next" => "http://example.com/outbox?page=1"
187 })
188
189 %{method: :get, url: "http://example.com/outbox?page=1"} ->
190 json(%{
191 "type" => "OrderedCollectionPage",
192 "orderedItems" => [1],
193 "next" => "http://example.com/outbox?page=2"
194 })
195
196 %{method: :get, url: "http://example.com/outbox?page=2"} ->
197 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [2]})
198 end)
199
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]
202 end
203
204 test "returns an error if the url doesn't have an OrderedCollection/Page" do
205 mock(fn
206 %{method: :get, url: "http://example.com/not-an-outbox"} ->
207 json(%{"type" => "NotAnOutbox"})
208 end)
209
210 assert {:error, _} = Utils.fetch_ordered_collection("http://example.com/not-an-outbox", 1)
211 end
212
213 test "returns the what was collected if there are less pages than specified" do
214 mock(fn
215 %{method: :get, url: "http://example.com/outbox"} ->
216 json(%{
217 "type" => "OrderedCollectionPage",
218 "orderedItems" => [0],
219 "next" => "http://example.com/outbox?page=1"
220 })
221
222 %{method: :get, url: "http://example.com/outbox?page=1"} ->
223 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [1]})
224 end)
225
226 assert Utils.fetch_ordered_collection("http://example.com/outbox", 5) == [0, 1]
227 end
228 end
229
230 test "make_json_ld_header/0" do
231 assert Utils.make_json_ld_header() == %{
232 "@context" => [
233 "https://www.w3.org/ns/activitystreams",
234 "http://localhost:4001/schemas/litepub-0.1.jsonld",
235 %{
236 "@language" => "und"
237 }
238 ]
239 }
240 end
241
242 describe "get_existing_votes" do
243 test "fetches existing votes" do
244 user = insert(:user)
245 other_user = insert(:user)
246
247 {:ok, activity} =
248 CommonAPI.post(user, %{
249 "status" => "How do I pronounce LaTeX?",
250 "poll" => %{
251 "options" => ["laytekh", "lahtekh", "latex"],
252 "expires_in" => 20,
253 "multiple" => true
254 }
255 })
256
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)
260 end
261
262 test "fetches only Create activities" do
263 user = insert(:user)
264 other_user = insert(:user)
265
266 {:ok, activity} =
267 CommonAPI.post(user, %{
268 "status" => "Are we living in a society?",
269 "poll" => %{
270 "options" => ["yes", "no"],
271 "expires_in" => 20
272 }
273 })
274
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
281 end
282 end
283
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)
288
289 {:ok, follow_activity} = ActivityPub.follow(follower, user)
290 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
291
292 data =
293 follow_activity_two.data
294 |> Map.put("state", "accept")
295
296 cng = Ecto.Changeset.change(follow_activity_two, data: data)
297
298 {:ok, follow_activity_two} = Repo.update(cng)
299
300 {:ok, follow_activity_two} =
301 Utils.update_follow_state_for_all(follow_activity_two, "accept")
302
303 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
304 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
305 end
306 end
307
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)
312
313 {:ok, follow_activity} = ActivityPub.follow(follower, user)
314 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
315
316 data =
317 follow_activity_two.data
318 |> Map.put("state", "accept")
319
320 cng = Ecto.Changeset.change(follow_activity_two, data: data)
321
322 {:ok, follow_activity_two} = Repo.update(cng)
323
324 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
325
326 assert Repo.get(Activity, follow_activity.id).data["state"] == "pending"
327 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
328 end
329 end
330
331 describe "update_element_in_object/3" do
332 test "updates likes" do
333 user = insert(:user)
334 activity = insert(:note_activity)
335 object = Object.normalize(activity)
336
337 assert {:ok, updated_object} =
338 Utils.update_element_in_object(
339 "like",
340 [user.ap_id],
341 object
342 )
343
344 assert updated_object.data["likes"] == [user.ap_id]
345 assert updated_object.data["like_count"] == 1
346 end
347 end
348
349 describe "add_like_to_object/2" do
350 test "add actor to likes" do
351 user = insert(:user)
352 user2 = insert(:user)
353 object = insert(:note)
354
355 assert {:ok, updated_object} =
356 Utils.add_like_to_object(
357 %Activity{data: %{"actor" => user.ap_id}},
358 object
359 )
360
361 assert updated_object.data["likes"] == [user.ap_id]
362 assert updated_object.data["like_count"] == 1
363
364 assert {:ok, updated_object2} =
365 Utils.add_like_to_object(
366 %Activity{data: %{"actor" => user2.ap_id}},
367 updated_object
368 )
369
370 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
371 assert updated_object2.data["like_count"] == 2
372 end
373 end
374
375 describe "remove_like_from_object/2" do
376 test "removes ap_id from likes" do
377 user = insert(:user)
378 user2 = insert(:user)
379 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
380
381 assert {:ok, updated_object} =
382 Utils.remove_like_from_object(
383 %Activity{data: %{"actor" => user.ap_id}},
384 object
385 )
386
387 assert updated_object.data["likes"] == [user2.ap_id]
388 assert updated_object.data["like_count"] == 1
389 end
390 end
391
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)
396
397 user = insert(:user)
398 refute Utils.get_existing_like(user.ap_id, object)
399 {:ok, like_activity, _object} = ActivityPub.like(user, object)
400
401 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
402 end
403 end
404 end