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