ActivityPub.Utils: Fix undo test.
[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 object = Object.normalize(like_activity.data["object"])
98
99 assert Utils.make_unlike_data(user, like_activity, nil) == %{
100 "type" => "Undo",
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"]
106 }
107
108 assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
109 "type" => "Undo",
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"
116 }
117 end
118 end
119
120 describe "make_like_data" do
121 setup do
122 user = insert(:user)
123 other_user = insert(:user)
124 third_user = insert(:user)
125 [user: user, other_user: other_user, third_user: third_user]
126 end
127
128 test "addresses actor's follower address if the activity is public", %{
129 user: user,
130 other_user: other_user,
131 third_user: third_user
132 } do
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])
135
136 {:ok, activity} =
137 CommonAPI.post(user, %{
138 "status" =>
139 "hey @#{other_user.nickname}, @#{third_user.nickname} how about beering together this weekend?"
140 })
141
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
145 end
146
147 test "does not adress actor's follower address if the activity is not public", %{
148 user: user,
149 other_user: other_user,
150 third_user: third_user
151 } do
152 expected_to = Enum.sort([user.ap_id])
153 expected_cc = [third_user.ap_id]
154
155 {:ok, activity} =
156 CommonAPI.post(user, %{
157 "status" => "@#{other_user.nickname} @#{third_user.nickname} bought a new swimsuit!",
158 "visibility" => "private"
159 })
160
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
164 end
165 end
166
167 describe "fetch_ordered_collection" do
168 import Tesla.Mock
169
170 test "fetches the first OrderedCollectionPage when an OrderedCollection is encountered" do
171 mock(fn
172 %{method: :get, url: "http://mastodon.com/outbox"} ->
173 json(%{"type" => "OrderedCollection", "first" => "http://mastodon.com/outbox?page=true"})
174
175 %{method: :get, url: "http://mastodon.com/outbox?page=true"} ->
176 json(%{"type" => "OrderedCollectionPage", "orderedItems" => ["ok"]})
177 end)
178
179 assert Utils.fetch_ordered_collection("http://mastodon.com/outbox", 1) == ["ok"]
180 end
181
182 test "fetches several pages in the right order one after another, but only the specified amount" do
183 mock(fn
184 %{method: :get, url: "http://example.com/outbox"} ->
185 json(%{
186 "type" => "OrderedCollectionPage",
187 "orderedItems" => [0],
188 "next" => "http://example.com/outbox?page=1"
189 })
190
191 %{method: :get, url: "http://example.com/outbox?page=1"} ->
192 json(%{
193 "type" => "OrderedCollectionPage",
194 "orderedItems" => [1],
195 "next" => "http://example.com/outbox?page=2"
196 })
197
198 %{method: :get, url: "http://example.com/outbox?page=2"} ->
199 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [2]})
200 end)
201
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]
204 end
205
206 test "returns an error if the url doesn't have an OrderedCollection/Page" do
207 mock(fn
208 %{method: :get, url: "http://example.com/not-an-outbox"} ->
209 json(%{"type" => "NotAnOutbox"})
210 end)
211
212 assert {:error, _} = Utils.fetch_ordered_collection("http://example.com/not-an-outbox", 1)
213 end
214
215 test "returns the what was collected if there are less pages than specified" do
216 mock(fn
217 %{method: :get, url: "http://example.com/outbox"} ->
218 json(%{
219 "type" => "OrderedCollectionPage",
220 "orderedItems" => [0],
221 "next" => "http://example.com/outbox?page=1"
222 })
223
224 %{method: :get, url: "http://example.com/outbox?page=1"} ->
225 json(%{"type" => "OrderedCollectionPage", "orderedItems" => [1]})
226 end)
227
228 assert Utils.fetch_ordered_collection("http://example.com/outbox", 5) == [0, 1]
229 end
230 end
231
232 test "make_json_ld_header/0" do
233 assert Utils.make_json_ld_header() == %{
234 "@context" => [
235 "https://www.w3.org/ns/activitystreams",
236 "http://localhost:4001/schemas/litepub-0.1.jsonld",
237 %{
238 "@language" => "und"
239 }
240 ]
241 }
242 end
243
244 describe "get_existing_votes" do
245 test "fetches existing votes" do
246 user = insert(:user)
247 other_user = insert(:user)
248
249 {:ok, activity} =
250 CommonAPI.post(user, %{
251 "status" => "How do I pronounce LaTeX?",
252 "poll" => %{
253 "options" => ["laytekh", "lahtekh", "latex"],
254 "expires_in" => 20,
255 "multiple" => true
256 }
257 })
258
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)
262 end
263
264 test "fetches only Create activities" do
265 user = insert(:user)
266 other_user = insert(:user)
267
268 {:ok, activity} =
269 CommonAPI.post(user, %{
270 "status" => "Are we living in a society?",
271 "poll" => %{
272 "options" => ["yes", "no"],
273 "expires_in" => 20
274 }
275 })
276
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
283 end
284 end
285
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)
290
291 {:ok, follow_activity} = ActivityPub.follow(follower, user)
292 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
293
294 data =
295 follow_activity_two.data
296 |> Map.put("state", "accept")
297
298 cng = Ecto.Changeset.change(follow_activity_two, data: data)
299
300 {:ok, follow_activity_two} = Repo.update(cng)
301
302 {:ok, follow_activity_two} =
303 Utils.update_follow_state_for_all(follow_activity_two, "accept")
304
305 assert Repo.get(Activity, follow_activity.id).data["state"] == "accept"
306 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "accept"
307 end
308 end
309
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)
314
315 {:ok, follow_activity} = ActivityPub.follow(follower, user)
316 {:ok, follow_activity_two} = ActivityPub.follow(follower, user)
317
318 data =
319 follow_activity_two.data
320 |> Map.put("state", "accept")
321
322 cng = Ecto.Changeset.change(follow_activity_two, data: data)
323
324 {:ok, follow_activity_two} = Repo.update(cng)
325
326 {:ok, follow_activity_two} = Utils.update_follow_state(follow_activity_two, "reject")
327
328 assert Repo.get(Activity, follow_activity.id).data["state"] == "pending"
329 assert Repo.get(Activity, follow_activity_two.id).data["state"] == "reject"
330 end
331 end
332
333 describe "update_element_in_object/3" do
334 test "updates likes" do
335 user = insert(:user)
336 activity = insert(:note_activity)
337 object = Object.normalize(activity)
338
339 assert {:ok, updated_object} =
340 Utils.update_element_in_object(
341 "like",
342 [user.ap_id],
343 object
344 )
345
346 assert updated_object.data["likes"] == [user.ap_id]
347 assert updated_object.data["like_count"] == 1
348 end
349 end
350
351 describe "add_like_to_object/2" do
352 test "add actor to likes" do
353 user = insert(:user)
354 user2 = insert(:user)
355 object = insert(:note)
356
357 assert {:ok, updated_object} =
358 Utils.add_like_to_object(
359 %Activity{data: %{"actor" => user.ap_id}},
360 object
361 )
362
363 assert updated_object.data["likes"] == [user.ap_id]
364 assert updated_object.data["like_count"] == 1
365
366 assert {:ok, updated_object2} =
367 Utils.add_like_to_object(
368 %Activity{data: %{"actor" => user2.ap_id}},
369 updated_object
370 )
371
372 assert updated_object2.data["likes"] == [user2.ap_id, user.ap_id]
373 assert updated_object2.data["like_count"] == 2
374 end
375 end
376
377 describe "remove_like_from_object/2" do
378 test "removes ap_id from likes" do
379 user = insert(:user)
380 user2 = insert(:user)
381 object = insert(:note, data: %{"likes" => [user.ap_id, user2.ap_id], "like_count" => 2})
382
383 assert {:ok, updated_object} =
384 Utils.remove_like_from_object(
385 %Activity{data: %{"actor" => user.ap_id}},
386 object
387 )
388
389 assert updated_object.data["likes"] == [user2.ap_id]
390 assert updated_object.data["like_count"] == 1
391 end
392 end
393
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)
398
399 user = insert(:user)
400 refute Utils.get_existing_like(user.ap_id, object)
401 {:ok, like_activity, _object} = ActivityPub.like(user, object)
402
403 assert ^like_activity = Utils.get_existing_like(user.ap_id, object)
404 end
405 end
406 end