Merge branch 'develop' into feature/database-compaction
[akkoma] / test / web / twitter_api / views / activity_view_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.TwitterAPI.ActivityViewTest do
6 use Pleroma.DataCase
7
8 alias Pleroma.Activity
9 alias Pleroma.Object
10 alias Pleroma.Repo
11 alias Pleroma.User
12 alias Pleroma.Web.ActivityPub.ActivityPub
13 alias Pleroma.Web.CommonAPI
14 alias Pleroma.Web.CommonAPI.Utils
15 alias Pleroma.Web.TwitterAPI.ActivityView
16 alias Pleroma.Web.TwitterAPI.UserView
17
18 import Pleroma.Factory
19 import Tesla.Mock
20
21 setup do
22 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
23 :ok
24 end
25
26 import Mock
27
28 test "returns a temporary ap_id based user for activities missing db users" do
29 user = insert(:user)
30
31 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
32
33 Repo.delete(user)
34 Cachex.clear(:user_cache)
35
36 %{"user" => tw_user} = ActivityView.render("activity.json", activity: activity)
37
38 assert tw_user["screen_name"] == "erroruser@example.com"
39 assert tw_user["name"] == user.ap_id
40 assert tw_user["statusnet_profile_url"] == user.ap_id
41 end
42
43 test "tries to get a user by nickname if fetching by ap_id doesn't work" do
44 user = insert(:user)
45
46 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
47
48 {:ok, user} =
49 user
50 |> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"})
51 |> Repo.update()
52
53 Cachex.clear(:user_cache)
54
55 result = ActivityView.render("activity.json", activity: activity)
56 assert result["user"]["id"] == user.id
57 end
58
59 test "tells if the message is muted for some reason" do
60 user = insert(:user)
61 other_user = insert(:user)
62
63 {:ok, user} = User.mute(user, other_user)
64
65 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "test"})
66 status = ActivityView.render("activity.json", %{activity: activity})
67
68 assert status["muted"] == false
69
70 status = ActivityView.render("activity.json", %{activity: activity, for: user})
71
72 assert status["muted"] == true
73 end
74
75 test "a create activity with a html status" do
76 text = """
77 #Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg
78 """
79
80 {:ok, activity} = CommonAPI.post(insert(:user), %{"status" => text})
81
82 result = ActivityView.render("activity.json", activity: activity)
83
84 assert result["statusnet_html"] ==
85 "<a class=\"hashtag\" data-tag=\"bike\" href=\"http://localhost:4001/tag/bike\" rel=\"tag\">#Bike</a> log - Commute Tuesday<br /><a href=\"https://pla.bike/posts/20181211/\">https://pla.bike/posts/20181211/</a><br /><a class=\"hashtag\" data-tag=\"cycling\" href=\"http://localhost:4001/tag/cycling\" rel=\"tag\">#cycling</a> <a class=\"hashtag\" data-tag=\"chscycling\" href=\"http://localhost:4001/tag/chscycling\" rel=\"tag\">#CHScycling</a> <a class=\"hashtag\" data-tag=\"commute\" href=\"http://localhost:4001/tag/commute\" rel=\"tag\">#commute</a><br />MVIMG_20181211_054020.jpg"
86
87 assert result["text"] ==
88 "#Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg"
89 end
90
91 test "a create activity with a summary containing emoji" do
92 {:ok, activity} =
93 CommonAPI.post(insert(:user), %{
94 "spoiler_text" => ":woollysocks: meow",
95 "status" => "."
96 })
97
98 result = ActivityView.render("activity.json", activity: activity)
99
100 expected = ":woollysocks: meow"
101
102 expected_html =
103 "<img height=\"32px\" width=\"32px\" alt=\"woollysocks\" title=\"woollysocks\" src=\"http://localhost:4001/finmoji/128px/woollysocks-128.png\" /> meow"
104
105 assert result["summary"] == expected
106 assert result["summary_html"] == expected_html
107 end
108
109 test "a create activity with a summary containing invalid HTML" do
110 {:ok, activity} =
111 CommonAPI.post(insert(:user), %{
112 "spoiler_text" => "<span style=\"color: magenta; font-size: 32px;\">meow</span>",
113 "status" => "."
114 })
115
116 result = ActivityView.render("activity.json", activity: activity)
117
118 expected = "meow"
119
120 assert result["summary"] == expected
121 assert result["summary_html"] == expected
122 end
123
124 test "a create activity with a note" do
125 user = insert(:user)
126 other_user = insert(:user, %{nickname: "shp"})
127
128 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
129 object = Object.normalize(activity.data["object"])
130
131 result = ActivityView.render("activity.json", activity: activity)
132
133 convo_id = Utils.context_to_conversation_id(object.data["context"])
134
135 expected = %{
136 "activity_type" => "post",
137 "attachments" => [],
138 "attentions" => [
139 UserView.render("show.json", %{user: other_user})
140 ],
141 "created_at" => object.data["published"] |> Utils.date_to_asctime(),
142 "external_url" => object.data["id"],
143 "fave_num" => 0,
144 "favorited" => false,
145 "id" => activity.id,
146 "in_reply_to_status_id" => nil,
147 "in_reply_to_screen_name" => nil,
148 "in_reply_to_user_id" => nil,
149 "in_reply_to_profileurl" => nil,
150 "in_reply_to_ostatus_uri" => nil,
151 "is_local" => true,
152 "is_post_verb" => true,
153 "possibly_sensitive" => false,
154 "repeat_num" => 0,
155 "repeated" => false,
156 "pinned" => false,
157 "statusnet_conversation_id" => convo_id,
158 "summary" => "",
159 "summary_html" => "",
160 "statusnet_html" =>
161 "Hey <span class=\"h-card\"><a data-user=\"#{other_user.id}\" class=\"u-url mention\" href=\"#{
162 other_user.ap_id
163 }\">@<span>shp</span></a></span>!",
164 "tags" => [],
165 "text" => "Hey @shp!",
166 "uri" => object.data["id"],
167 "user" => UserView.render("show.json", %{user: user}),
168 "visibility" => "direct",
169 "card" => nil,
170 "muted" => false
171 }
172
173 assert result == expected
174 end
175
176 test "a list of activities" do
177 user = insert(:user)
178 other_user = insert(:user, %{nickname: "shp"})
179 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
180 object = Object.normalize(activity.data["object"])
181
182 convo_id = Utils.context_to_conversation_id(object.data["context"])
183
184 mocks = [
185 {
186 Utils,
187 [:passthrough],
188 [context_to_conversation_id: fn _ -> false end]
189 },
190 {
191 User,
192 [:passthrough],
193 [get_cached_by_ap_id: fn _ -> nil end]
194 }
195 ]
196
197 with_mocks mocks do
198 [result] = ActivityView.render("index.json", activities: [activity])
199
200 assert result["statusnet_conversation_id"] == convo_id
201 assert result["user"]
202 refute called(Utils.context_to_conversation_id(:_))
203 refute called(User.get_cached_by_ap_id(user.ap_id))
204 refute called(User.get_cached_by_ap_id(other_user.ap_id))
205 end
206 end
207
208 test "an activity that is a reply" do
209 user = insert(:user)
210 other_user = insert(:user, %{nickname: "shp"})
211
212 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
213
214 {:ok, answer} =
215 CommonAPI.post(other_user, %{"status" => "Hi!", "in_reply_to_status_id" => activity.id})
216
217 result = ActivityView.render("activity.json", %{activity: answer})
218
219 assert result["in_reply_to_status_id"] == activity.id
220 end
221
222 test "a like activity" do
223 user = insert(:user)
224 other_user = insert(:user, %{nickname: "shp"})
225
226 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
227 {:ok, like, _object} = CommonAPI.favorite(activity.id, other_user)
228
229 result = ActivityView.render("activity.json", activity: like)
230 activity = Pleroma.Activity.get_by_ap_id(activity.data["id"])
231
232 expected = %{
233 "activity_type" => "like",
234 "created_at" => like.data["published"] |> Utils.date_to_asctime(),
235 "external_url" => like.data["id"],
236 "id" => like.id,
237 "in_reply_to_status_id" => activity.id,
238 "is_local" => true,
239 "is_post_verb" => false,
240 "favorited_status" => ActivityView.render("activity.json", activity: activity),
241 "statusnet_html" => "shp favorited a status.",
242 "text" => "shp favorited a status.",
243 "uri" => "tag:#{like.data["id"]}:objectType=Favourite",
244 "user" => UserView.render("show.json", user: other_user)
245 }
246
247 assert result == expected
248 end
249
250 test "a like activity for deleted post" do
251 user = insert(:user)
252 other_user = insert(:user, %{nickname: "shp"})
253
254 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
255 {:ok, like, _object} = CommonAPI.favorite(activity.id, other_user)
256 CommonAPI.delete(activity.id, user)
257
258 result = ActivityView.render("activity.json", activity: like)
259
260 expected = %{
261 "activity_type" => "like",
262 "created_at" => like.data["published"] |> Utils.date_to_asctime(),
263 "external_url" => like.data["id"],
264 "id" => like.id,
265 "in_reply_to_status_id" => nil,
266 "is_local" => true,
267 "is_post_verb" => false,
268 "favorited_status" => nil,
269 "statusnet_html" => "shp favorited a status.",
270 "text" => "shp favorited a status.",
271 "uri" => "tag:#{like.data["id"]}:objectType=Favourite",
272 "user" => UserView.render("show.json", user: other_user)
273 }
274
275 assert result == expected
276 end
277
278 test "an announce activity" do
279 user = insert(:user)
280 other_user = insert(:user, %{nickname: "shp"})
281
282 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
283 {:ok, announce, object} = CommonAPI.repeat(activity.id, other_user)
284
285 convo_id = Utils.context_to_conversation_id(object.data["context"])
286
287 activity = Activity.get_by_id(activity.id)
288
289 result = ActivityView.render("activity.json", activity: announce)
290
291 expected = %{
292 "activity_type" => "repeat",
293 "created_at" => announce.data["published"] |> Utils.date_to_asctime(),
294 "external_url" => announce.data["id"],
295 "id" => announce.id,
296 "is_local" => true,
297 "is_post_verb" => false,
298 "statusnet_html" => "shp retweeted a status.",
299 "text" => "shp retweeted a status.",
300 "uri" => "tag:#{announce.data["id"]}:objectType=note",
301 "user" => UserView.render("show.json", user: other_user),
302 "retweeted_status" => ActivityView.render("activity.json", activity: activity),
303 "statusnet_conversation_id" => convo_id
304 }
305
306 assert result == expected
307 end
308
309 test "A follow activity" do
310 user = insert(:user)
311 other_user = insert(:user, %{nickname: "shp"})
312
313 {:ok, follower} = User.follow(user, other_user)
314 {:ok, follow} = ActivityPub.follow(follower, other_user)
315
316 result = ActivityView.render("activity.json", activity: follow)
317
318 expected = %{
319 "activity_type" => "follow",
320 "attentions" => [],
321 "created_at" => follow.data["published"] |> Utils.date_to_asctime(),
322 "external_url" => follow.data["id"],
323 "id" => follow.id,
324 "in_reply_to_status_id" => nil,
325 "is_local" => true,
326 "is_post_verb" => false,
327 "statusnet_html" => "#{user.nickname} started following shp",
328 "text" => "#{user.nickname} started following shp",
329 "user" => UserView.render("show.json", user: user)
330 }
331
332 assert result == expected
333 end
334
335 test "a delete activity" do
336 user = insert(:user)
337
338 {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
339 {:ok, delete} = CommonAPI.delete(activity.id, user)
340
341 result = ActivityView.render("activity.json", activity: delete)
342
343 expected = %{
344 "activity_type" => "delete",
345 "attentions" => [],
346 "created_at" => delete.data["published"] |> Utils.date_to_asctime(),
347 "external_url" => delete.data["id"],
348 "id" => delete.id,
349 "in_reply_to_status_id" => nil,
350 "is_local" => true,
351 "is_post_verb" => false,
352 "statusnet_html" => "deleted notice {{tag",
353 "text" => "deleted notice {{tag",
354 "uri" => delete.data["object"],
355 "user" => UserView.render("show.json", user: user)
356 }
357
358 assert result == expected
359 end
360
361 test "a peertube video" do
362 {:ok, object} =
363 ActivityPub.fetch_object_from_id(
364 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
365 )
366
367 %Activity{} = activity = Activity.get_create_by_object_ap_id(object.data["id"])
368
369 result = ActivityView.render("activity.json", activity: activity)
370
371 assert length(result["attachments"]) == 1
372 assert result["summary"] == "Friday Night"
373 end
374 end