Merge branch 'feature/integration_tesla' into 'develop'
[akkoma] / test / user_test.exs
1 defmodule Pleroma.UserTest do
2 alias Pleroma.Builders.UserBuilder
3 alias Pleroma.{User, Repo, Activity}
4 alias Pleroma.Web.OStatus
5 alias Pleroma.Web.Websub.WebsubClientSubscription
6 alias Pleroma.Web.CommonAPI
7 use Pleroma.DataCase
8
9 import Pleroma.Factory
10 import Ecto.Query
11
12 setup_all do
13 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
14 :ok
15 end
16
17 test "ap_id returns the activity pub id for the user" do
18 user = UserBuilder.build()
19
20 expected_ap_id = "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
21
22 assert expected_ap_id == User.ap_id(user)
23 end
24
25 test "ap_followers returns the followers collection for the user" do
26 user = UserBuilder.build()
27
28 expected_followers_collection = "#{User.ap_id(user)}/followers"
29
30 assert expected_followers_collection == User.ap_followers(user)
31 end
32
33 test "follow takes a user and another user" do
34 user = insert(:user)
35 followed = insert(:user)
36
37 {:ok, user} = User.follow(user, followed)
38
39 user = Repo.get(User, user.id)
40
41 followed = User.get_by_ap_id(followed.ap_id)
42 assert followed.info.follower_count == 1
43
44 assert User.ap_followers(followed) in user.following
45 end
46
47 test "can't follow a deactivated users" do
48 user = insert(:user)
49 followed = insert(:user, info: %{deactivated: true})
50
51 {:error, _} = User.follow(user, followed)
52 end
53
54 test "can't follow a user who blocked us" do
55 blocker = insert(:user)
56 blockee = insert(:user)
57
58 {:ok, blocker} = User.block(blocker, blockee)
59
60 {:error, _} = User.follow(blockee, blocker)
61 end
62
63 test "local users do not automatically follow local locked accounts" do
64 follower = insert(:user, info: %{locked: true})
65 followed = insert(:user, info: %{locked: true})
66
67 {:ok, follower} = User.maybe_direct_follow(follower, followed)
68
69 refute User.following?(follower, followed)
70 end
71
72 # This is a somewhat useless test.
73 # test "following a remote user will ensure a websub subscription is present" do
74 # user = insert(:user)
75 # {:ok, followed} = OStatus.make_user("shp@social.heldscal.la")
76
77 # assert followed.local == false
78
79 # {:ok, user} = User.follow(user, followed)
80 # assert User.ap_followers(followed) in user.following
81
82 # query = from w in WebsubClientSubscription,
83 # where: w.topic == ^followed.info["topic"]
84 # websub = Repo.one(query)
85
86 # assert websub
87 # end
88
89 test "unfollow takes a user and another user" do
90 followed = insert(:user)
91 user = insert(:user, %{following: [User.ap_followers(followed)]})
92
93 {:ok, user, _activity} = User.unfollow(user, followed)
94
95 user = Repo.get(User, user.id)
96
97 assert user.following == []
98 end
99
100 test "unfollow doesn't unfollow yourself" do
101 user = insert(:user)
102
103 {:error, _} = User.unfollow(user, user)
104
105 user = Repo.get(User, user.id)
106 assert user.following == [user.ap_id]
107 end
108
109 test "test if a user is following another user" do
110 followed = insert(:user)
111 user = insert(:user, %{following: [User.ap_followers(followed)]})
112
113 assert User.following?(user, followed)
114 refute User.following?(followed, user)
115 end
116
117 describe "user registration" do
118 @full_user_data %{
119 bio: "A guy",
120 name: "my name",
121 nickname: "nick",
122 password: "test",
123 password_confirmation: "test",
124 email: "email@example.com"
125 }
126
127 test "it requires an email, name, nickname and password, bio is optional" do
128 @full_user_data
129 |> Map.keys()
130 |> Enum.each(fn key ->
131 params = Map.delete(@full_user_data, key)
132 changeset = User.register_changeset(%User{}, params)
133
134 assert if key == :bio, do: changeset.valid?, else: not changeset.valid?
135 end)
136 end
137
138 test "it sets the password_hash, ap_id and following fields" do
139 changeset = User.register_changeset(%User{}, @full_user_data)
140
141 assert changeset.valid?
142
143 assert is_binary(changeset.changes[:password_hash])
144 assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
145
146 assert changeset.changes[:following] == [
147 User.ap_followers(%User{nickname: @full_user_data.nickname})
148 ]
149
150 assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
151 end
152 end
153
154 describe "fetching a user from nickname or trying to build one" do
155 test "gets an existing user" do
156 user = insert(:user)
157 fetched_user = User.get_or_fetch_by_nickname(user.nickname)
158
159 assert user == fetched_user
160 end
161
162 test "gets an existing user, case insensitive" do
163 user = insert(:user, nickname: "nick")
164 fetched_user = User.get_or_fetch_by_nickname("NICK")
165
166 assert user == fetched_user
167 end
168
169 test "fetches an external user via ostatus if no user exists" do
170 fetched_user = User.get_or_fetch_by_nickname("shp@social.heldscal.la")
171 assert fetched_user.nickname == "shp@social.heldscal.la"
172 end
173
174 test "returns nil if no user could be fetched" do
175 fetched_user = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la")
176 assert fetched_user == nil
177 end
178
179 test "returns nil for nonexistant local user" do
180 fetched_user = User.get_or_fetch_by_nickname("nonexistant")
181 assert fetched_user == nil
182 end
183
184 test "updates an existing user, if stale" do
185 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
186
187 orig_user =
188 insert(
189 :user,
190 local: false,
191 nickname: "admin@mastodon.example.org",
192 ap_id: "http://mastodon.example.org/users/admin",
193 last_refreshed_at: a_week_ago,
194 info: %{}
195 )
196
197 assert orig_user.last_refreshed_at == a_week_ago
198
199 user = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
200 assert user.info.source_data["endpoints"]
201
202 refute user.last_refreshed_at == orig_user.last_refreshed_at
203 end
204 end
205
206 test "returns an ap_id for a user" do
207 user = insert(:user)
208
209 assert User.ap_id(user) ==
210 Pleroma.Web.Router.Helpers.o_status_url(
211 Pleroma.Web.Endpoint,
212 :feed_redirect,
213 user.nickname
214 )
215 end
216
217 test "returns an ap_followers link for a user" do
218 user = insert(:user)
219
220 assert User.ap_followers(user) ==
221 Pleroma.Web.Router.Helpers.o_status_url(
222 Pleroma.Web.Endpoint,
223 :feed_redirect,
224 user.nickname
225 ) <> "/followers"
226 end
227
228 describe "remote user creation changeset" do
229 @valid_remote %{
230 bio: "hello",
231 name: "Someone",
232 nickname: "a@b.de",
233 ap_id: "http...",
234 info: %{some: "info"},
235 avatar: %{some: "avatar"}
236 }
237
238 test "it confirms validity" do
239 cs = User.remote_user_creation(@valid_remote)
240 assert cs.valid?
241 end
242
243 test "it sets the follower_adress" do
244 cs = User.remote_user_creation(@valid_remote)
245 # remote users get a fake local follower address
246 assert cs.changes.follower_address ==
247 User.ap_followers(%User{nickname: @valid_remote[:nickname]})
248 end
249
250 test "it enforces the fqn format for nicknames" do
251 cs = User.remote_user_creation(%{@valid_remote | nickname: "bla"})
252 assert cs.changes.local == false
253 assert cs.changes.avatar
254 refute cs.valid?
255 end
256
257 test "it has required fields" do
258 [:name, :ap_id]
259 |> Enum.each(fn field ->
260 cs = User.remote_user_creation(Map.delete(@valid_remote, field))
261 refute cs.valid?
262 end)
263 end
264
265 test "it restricts some sizes" do
266 [bio: 5000, name: 100]
267 |> Enum.each(fn {field, size} ->
268 string = String.pad_leading(".", size)
269 cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
270 assert cs.valid?
271
272 string = String.pad_leading(".", size + 1)
273 cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
274 refute cs.valid?
275 end)
276 end
277 end
278
279 describe "followers and friends" do
280 test "gets all followers for a given user" do
281 user = insert(:user)
282 follower_one = insert(:user)
283 follower_two = insert(:user)
284 not_follower = insert(:user)
285
286 {:ok, follower_one} = User.follow(follower_one, user)
287 {:ok, follower_two} = User.follow(follower_two, user)
288
289 {:ok, res} = User.get_followers(user)
290
291 assert Enum.member?(res, follower_one)
292 assert Enum.member?(res, follower_two)
293 refute Enum.member?(res, not_follower)
294 end
295
296 test "gets all friends (followed users) for a given user" do
297 user = insert(:user)
298 followed_one = insert(:user)
299 followed_two = insert(:user)
300 not_followed = insert(:user)
301
302 {:ok, user} = User.follow(user, followed_one)
303 {:ok, user} = User.follow(user, followed_two)
304
305 {:ok, res} = User.get_friends(user)
306
307 followed_one = User.get_by_ap_id(followed_one.ap_id)
308 followed_two = User.get_by_ap_id(followed_two.ap_id)
309 assert Enum.member?(res, followed_one)
310 assert Enum.member?(res, followed_two)
311 refute Enum.member?(res, not_followed)
312 end
313 end
314
315 describe "updating note and follower count" do
316 test "it sets the info->note_count property" do
317 note = insert(:note)
318
319 user = User.get_by_ap_id(note.data["actor"])
320
321 assert user.info.note_count == 0
322
323 {:ok, user} = User.update_note_count(user)
324
325 assert user.info.note_count == 1
326 end
327
328 test "it increases the info->note_count property" do
329 note = insert(:note)
330 user = User.get_by_ap_id(note.data["actor"])
331
332 assert user.info.note_count == 0
333
334 {:ok, user} = User.increase_note_count(user)
335
336 assert user.info.note_count == 1
337
338 {:ok, user} = User.increase_note_count(user)
339
340 assert user.info.note_count == 2
341 end
342
343 test "it decreases the info->note_count property" do
344 note = insert(:note)
345 user = User.get_by_ap_id(note.data["actor"])
346
347 assert user.info.note_count == 0
348
349 {:ok, user} = User.increase_note_count(user)
350
351 assert user.info.note_count == 1
352
353 {:ok, user} = User.decrease_note_count(user)
354
355 assert user.info.note_count == 0
356
357 {:ok, user} = User.decrease_note_count(user)
358
359 assert user.info.note_count == 0
360 end
361
362 test "it sets the info->follower_count property" do
363 user = insert(:user)
364 follower = insert(:user)
365
366 User.follow(follower, user)
367
368 assert user.info.follower_count == 0
369
370 {:ok, user} = User.update_follower_count(user)
371
372 assert user.info.follower_count == 1
373 end
374 end
375
376 describe "blocks" do
377 test "it blocks people" do
378 user = insert(:user)
379 blocked_user = insert(:user)
380
381 refute User.blocks?(user, blocked_user)
382
383 {:ok, user} = User.block(user, blocked_user)
384
385 assert User.blocks?(user, blocked_user)
386 end
387
388 test "it unblocks users" do
389 user = insert(:user)
390 blocked_user = insert(:user)
391
392 {:ok, user} = User.block(user, blocked_user)
393 {:ok, user} = User.unblock(user, blocked_user)
394
395 refute User.blocks?(user, blocked_user)
396 end
397
398 test "blocks tear down cyclical follow relationships" do
399 blocker = insert(:user)
400 blocked = insert(:user)
401
402 {:ok, blocker} = User.follow(blocker, blocked)
403 {:ok, blocked} = User.follow(blocked, blocker)
404
405 assert User.following?(blocker, blocked)
406 assert User.following?(blocked, blocker)
407
408 {:ok, blocker} = User.block(blocker, blocked)
409 blocked = Repo.get(User, blocked.id)
410
411 assert User.blocks?(blocker, blocked)
412
413 refute User.following?(blocker, blocked)
414 refute User.following?(blocked, blocker)
415 end
416
417 test "blocks tear down blocker->blocked follow relationships" do
418 blocker = insert(:user)
419 blocked = insert(:user)
420
421 {:ok, blocker} = User.follow(blocker, blocked)
422
423 assert User.following?(blocker, blocked)
424 refute User.following?(blocked, blocker)
425
426 {:ok, blocker} = User.block(blocker, blocked)
427 blocked = Repo.get(User, blocked.id)
428
429 assert User.blocks?(blocker, blocked)
430
431 refute User.following?(blocker, blocked)
432 refute User.following?(blocked, blocker)
433 end
434
435 test "blocks tear down blocked->blocker follow relationships" do
436 blocker = insert(:user)
437 blocked = insert(:user)
438
439 {:ok, blocked} = User.follow(blocked, blocker)
440
441 refute User.following?(blocker, blocked)
442 assert User.following?(blocked, blocker)
443
444 {:ok, blocker} = User.block(blocker, blocked)
445 blocked = Repo.get(User, blocked.id)
446
447 assert User.blocks?(blocker, blocked)
448
449 refute User.following?(blocker, blocked)
450 refute User.following?(blocked, blocker)
451 end
452 end
453
454 describe "domain blocking" do
455 test "blocks domains" do
456 user = insert(:user)
457 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
458
459 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
460
461 assert User.blocks?(user, collateral_user)
462 end
463
464 test "unblocks domains" do
465 user = insert(:user)
466 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
467
468 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
469 {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com")
470
471 refute User.blocks?(user, collateral_user)
472 end
473 end
474
475 test "get recipients from activity" do
476 actor = insert(:user)
477 user = insert(:user, local: true)
478 user_two = insert(:user, local: false)
479 addressed = insert(:user, local: true)
480 addressed_remote = insert(:user, local: false)
481
482 {:ok, activity} =
483 CommonAPI.post(actor, %{
484 "status" => "hey @#{addressed.nickname} @#{addressed_remote.nickname}"
485 })
486
487 assert [addressed] == User.get_recipients_from_activity(activity)
488
489 {:ok, user} = User.follow(user, actor)
490 {:ok, _user_two} = User.follow(user_two, actor)
491 recipients = User.get_recipients_from_activity(activity)
492 assert length(recipients) == 2
493 assert user in recipients
494 assert addressed in recipients
495 end
496
497 test ".deactivate can de-activate then re-activate a user" do
498 user = insert(:user)
499 assert false == user.info.deactivated
500 {:ok, user} = User.deactivate(user)
501 assert true == user.info.deactivated
502 {:ok, user} = User.deactivate(user, false)
503 assert false == user.info.deactivated
504 end
505
506 test ".delete deactivates a user, all follow relationships and all create activities" do
507 user = insert(:user)
508 followed = insert(:user)
509 follower = insert(:user)
510
511 {:ok, user} = User.follow(user, followed)
512 {:ok, follower} = User.follow(follower, user)
513
514 {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
515 {:ok, activity_two} = CommonAPI.post(follower, %{"status" => "3hu"})
516
517 {:ok, _, _} = CommonAPI.favorite(activity_two.id, user)
518 {:ok, _, _} = CommonAPI.favorite(activity.id, follower)
519 {:ok, _, _} = CommonAPI.repeat(activity.id, follower)
520
521 {:ok, _} = User.delete(user)
522
523 followed = Repo.get(User, followed.id)
524 follower = Repo.get(User, follower.id)
525 user = Repo.get(User, user.id)
526
527 assert user.info.deactivated
528
529 refute User.following?(user, followed)
530 refute User.following?(followed, follower)
531
532 # TODO: Remove favorites, repeats, delete activities.
533
534 refute Repo.get(Activity, activity.id)
535 end
536
537 test "get_public_key_for_ap_id fetches a user that's not in the db" do
538 assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
539 end
540
541 test "insert or update a user from given data" do
542 user = insert(:user, %{nickname: "nick@name.de"})
543 data = %{ap_id: user.ap_id <> "xxx", name: user.name, nickname: user.nickname}
544
545 assert {:ok, %User{}} = User.insert_or_update_user(data)
546 end
547
548 describe "per-user rich-text filtering" do
549 test "html_filter_policy returns nil when rich-text is enabled" do
550 user = insert(:user)
551
552 assert nil == User.html_filter_policy(user)
553 end
554
555 test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
556 user = insert(:user, %{info: %{no_rich_text: true}})
557
558 assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
559 end
560 end
561
562 describe "caching" do
563 test "invalidate_cache works" do
564 user = insert(:user)
565 user_info = User.get_cached_user_info(user)
566
567 User.invalidate_cache(user)
568
569 {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
570 {:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
571 {:ok, nil} = Cachex.get(:user_cache, "user_info:#{user.id}")
572 end
573
574 test "User.delete() plugs any possible zombie objects" do
575 user = insert(:user)
576
577 {:ok, _} = User.delete(user)
578
579 {:ok, cached_user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
580
581 assert cached_user != user
582
583 {:ok, cached_user} = Cachex.get(:user_cache, "nickname:#{user.ap_id}")
584
585 assert cached_user != user
586 end
587 end
588
589 describe "User.search" do
590 test "finds a user, ranking by similarity" do
591 user = insert(:user, %{name: "lain"})
592 user_two = insert(:user, %{name: "ean"})
593 user_three = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
594 user_four = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
595
596 assert user_four ==
597 User.search("lain@ple") |> List.first() |> Map.put(:search_distance, nil)
598 end
599 end
600 end