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