Page MenuHomePhorge

No OneTemporary

Size
49 KB
Referenced Files
None
Subscribers
None
diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex
index 7babd47ea..b8c648672 100644
--- a/lib/pleroma/user/search.ex
+++ b/lib/pleroma/user/search.ex
@@ -1,205 +1,210 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Search do
alias Pleroma.Pagination
alias Pleroma.User
import Ecto.Query
@limit 20
def search(query_string, opts \\ []) do
resolve = Keyword.get(opts, :resolve, false)
following = Keyword.get(opts, :following, false)
result_limit = Keyword.get(opts, :limit, @limit)
offset = Keyword.get(opts, :offset, 0)
for_user = Keyword.get(opts, :for_user)
query_string = format_query(query_string)
maybe_resolve(resolve, for_user, query_string)
results =
query_string
|> search_query(for_user, following)
|> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
results
end
defp format_query(query_string) do
# Strip the beginning @ off if there is a query
query_string = String.trim_leading(query_string, "@")
with [name, domain] <- String.split(query_string, "@") do
encoded_domain =
domain
|> String.replace(~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "")
|> String.to_charlist()
|> :idna.encode()
|> to_string()
name <> "@" <> encoded_domain
else
_ -> query_string
end
end
defp search_query(query_string, for_user, following) do
for_user
|> base_query(following)
|> filter_blocked_user(for_user)
|> filter_invisible_users()
+ |> filter_discoverable_users()
|> filter_internal_users()
|> filter_blocked_domains(for_user)
|> fts_search(query_string)
|> trigram_rank(query_string)
|> boost_search_rank(for_user)
|> subquery()
|> order_by(desc: :search_rank)
|> maybe_restrict_local(for_user)
end
defp fts_search(query, query_string) do
query_string = to_tsquery(query_string)
from(
u in query,
where:
fragment(
# The fragment must _exactly_ match `users_fts_index`, otherwise the index won't work
"""
(
setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')
) @@ to_tsquery('simple', ?)
""",
u.nickname,
u.name,
^query_string
)
)
end
defp to_tsquery(query_string) do
String.trim_trailing(query_string, "@" <> local_domain())
|> String.replace(~r/[!-\/|@|[-`|{-~|:-?]+/, " ")
|> String.trim()
|> String.split()
|> Enum.map(&(&1 <> ":*"))
|> Enum.join(" | ")
end
# Considers nickname match, localized nickname match, name match; preferences nickname match
defp trigram_rank(query, query_string) do
from(
u in query,
select_merge: %{
search_rank:
fragment(
"""
similarity(?, ?) +
similarity(?, regexp_replace(?, '@.+', '')) +
similarity(?, trim(coalesce(?, '')))
""",
^query_string,
u.nickname,
^query_string,
u.nickname,
^query_string,
u.name
)
}
)
end
defp base_query(%User{} = user, true), do: User.get_friends_query(user)
defp base_query(_user, _following), do: User
defp filter_invisible_users(query) do
from(q in query, where: q.invisible == false)
end
+ defp filter_discoverable_users(query) do
+ from(q in query, where: q.discoverable == true)
+ end
+
defp filter_internal_users(query) do
from(q in query, where: q.actor_type != "Application")
end
defp filter_blocked_user(query, %User{} = blocker) do
query
|> join(:left, [u], b in Pleroma.UserRelationship,
as: :blocks,
on: b.relationship_type == ^:block and b.source_id == ^blocker.id and u.id == b.target_id
)
|> where([blocks: b], is_nil(b.target_id))
end
defp filter_blocked_user(query, _), do: query
defp filter_blocked_domains(query, %User{domain_blocks: domain_blocks})
when length(domain_blocks) > 0 do
domains = Enum.join(domain_blocks, ",")
from(
q in query,
where: fragment("substring(ap_id from '.*://([^/]*)') NOT IN (?)", ^domains)
)
end
defp filter_blocked_domains(query, _), do: query
defp maybe_resolve(true, user, query) do
case {limit(), user} do
{:all, _} -> :noop
{:unauthenticated, %User{}} -> User.get_or_fetch(query)
{:unauthenticated, _} -> :noop
{false, _} -> User.get_or_fetch(query)
end
end
defp maybe_resolve(_, _, _), do: :noop
defp maybe_restrict_local(q, user) do
case {limit(), user} do
{:all, _} -> restrict_local(q)
{:unauthenticated, %User{}} -> q
{:unauthenticated, _} -> restrict_local(q)
{false, _} -> q
end
end
defp limit, do: Pleroma.Config.get([:instance, :limit_to_local_content], :unauthenticated)
defp restrict_local(q), do: where(q, [u], u.local == true)
defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
defp boost_search_rank(query, %User{} = for_user) do
friends_ids = User.get_friends_ids(for_user)
followers_ids = User.get_followers_ids(for_user)
from(u in subquery(query),
select_merge: %{
search_rank:
fragment(
"""
CASE WHEN (?) THEN (?) * 1.5
WHEN (?) THEN (?) * 1.3
WHEN (?) THEN (?) * 1.1
ELSE (?) END
""",
u.id in ^friends_ids and u.id in ^followers_ids,
u.search_rank,
u.id in ^friends_ids,
u.search_rank,
u.id in ^followers_ids,
u.search_rank,
u.search_rank
)
}
)
end
defp boost_search_rank(query, _for_user), do: query
end
diff --git a/test/fixtures/tesla_mock/admin@mastdon.example.org.json b/test/fixtures/tesla_mock/admin@mastdon.example.org.json
index a911b979a..f961ccb36 100644
--- a/test/fixtures/tesla_mock/admin@mastdon.example.org.json
+++ b/test/fixtures/tesla_mock/admin@mastdon.example.org.json
@@ -1,62 +1,70 @@
{
- "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {
- "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
- "sensitive": "as:sensitive",
- "movedTo": "as:movedTo",
- "Hashtag": "as:Hashtag",
- "ostatus": "http://ostatus.org#",
- "atomUri": "ostatus:atomUri",
- "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
- "conversation": "ostatus:conversation",
- "toot": "http://joinmastodon.org/ns#",
- "Emoji": "toot:Emoji",
- "alsoKnownAs": {
- "@id": "as:alsoKnownAs",
- "@type": "@id"
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "sensitive": "as:sensitive",
+ "movedTo": "as:movedTo",
+ "Hashtag": "as:Hashtag",
+ "ostatus": "http://ostatus.org#",
+ "atomUri": "ostatus:atomUri",
+ "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+ "conversation": "ostatus:conversation",
+ "toot": "http://joinmastodon.org/ns#",
+ "Emoji": "toot:Emoji",
+ "alsoKnownAs": {
+ "@id": "as:alsoKnownAs",
+ "@type": "@id"
+ }
}
- }],
+ ],
"id": "http://mastodon.example.org/users/admin",
"type": "Person",
"following": "http://mastodon.example.org/users/admin/following",
"followers": "http://mastodon.example.org/users/admin/followers",
"inbox": "http://mastodon.example.org/users/admin/inbox",
"outbox": "http://mastodon.example.org/users/admin/outbox",
"preferredUsername": "admin",
"name": null,
+ "discoverable": "true",
"summary": "\u003cp\u003e\u003c/p\u003e",
"url": "http://mastodon.example.org/@admin",
"manuallyApprovesFollowers": false,
"capabilities": {
"acceptsChatMessages": true
},
"publicKey": {
"id": "http://mastodon.example.org/users/admin#main-key",
"owner": "http://mastodon.example.org/users/admin",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtc4Tir+3ADhSNF6VKrtW\nOU32T01w7V0yshmQei38YyiVwVvFu8XOP6ACchkdxbJ+C9mZud8qWaRJKVbFTMUG\nNX4+6Q+FobyuKrwN7CEwhDALZtaN2IPbaPd6uG1B7QhWorrY+yFa8f2TBM3BxnUy\nI4T+bMIZIEYG7KtljCBoQXuTQmGtuffO0UwJksidg2ffCF5Q+K//JfQagJ3UzrR+\nZXbKMJdAw4bCVJYs4Z5EhHYBwQWiXCyMGTd7BGlmMkY6Av7ZqHKC/owp3/0EWDNz\nNqF09Wcpr3y3e8nA10X40MJqp/wR+1xtxp+YGbq/Cj5hZGBG7etFOmIpVBrDOhry\nBwIDAQAB\n-----END PUBLIC KEY-----\n"
},
- "attachment": [{
+ "attachment": [
+ {
"type": "PropertyValue",
"name": "foo",
"value": "bar"
},
{
"type": "PropertyValue",
"name": "foo1",
"value": "bar1"
}
],
"endpoints": {
"sharedInbox": "http://mastodon.example.org/inbox"
},
"icon": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
},
"image": {
"type": "Image",
"mediaType": "image/png",
"url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
},
- "alsoKnownAs": ["http://example.org/users/foo"]
-}
+ "alsoKnownAs": [
+ "http://example.org/users/foo"
+ ]
+}
\ No newline at end of file
diff --git a/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json b/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json
index c42f3a53c..ca76d6e17 100644
--- a/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json
+++ b/test/fixtures/tesla_mock/https___osada.macgirvin.com_channel_mike.json
@@ -1,54 +1,55 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1"
],
"type": "Person",
"id": "https://osada.macgirvin.com/channel/mike",
"preferredUsername": "mike",
"name": "Mike Macgirvin (Osada)",
"updated": "2018-08-29T03:09:11Z",
+ "discoverable": "true",
"icon": {
"type": "Image",
"mediaType": "image/jpeg",
"updated": "2018-08-29T03:10:13Z",
"url": "https://osada.macgirvin.com/photo/profile/l/2",
"height": 300,
"width": 300
},
"url": [
{
"type": "Link",
"mediaType": "text/html",
"href": "https://osada.macgirvin.com/channel/mike"
},
{
"type": "Link",
"mediaType": "text/x-zot+json",
"href": "https://osada.macgirvin.com/channel/mike"
}
],
"inbox": "https://osada.macgirvin.com/inbox/mike",
"outbox": "https://osada.macgirvin.com/outbox/mike",
"followers": "https://osada.macgirvin.com/followers/mike",
"following": "https://osada.macgirvin.com/following/mike",
"endpoints": {
"sharedInbox": "https://osada.macgirvin.com/inbox"
},
"publicKey": {
"id": "https://osada.macgirvin.com/channel/mike/public_key_pem",
"owner": "https://osada.macgirvin.com/channel/mike",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAskSyK2VwBNKbzZl9XNJk\nvxU5AAilmRArMmmKSzphdHaVBHakeafUfixvqNrQ/oX2srJvJKcghNmEMrJ6MJ7r\npeEndVOo7pcP4PwVjtnC06p3J711q5tBehqM25BfCLCrB2YqWF6c8zk3CPN3Na21\n8k5s4cO95N/rGN+Po0XFAX/HjKjlpgNpKRDrpxmXxTU8NZfAqeQGJ5oiMBZI9vVB\n+eU7t1L6F5/XWuUCeP4OMrG8oZX822AREba8rknS6DpkWGES0Rx2eNOyYTf6ue75\nI6Ek6rlO+da5wMWr+3BvYMq4JMIwTHzAO+ZqqJPFpzKSiVuAWb2DOX/MDFecVWJE\ntF/R60lONxe4e/00MPCoDdqkLKdwROsk1yGL7z4Zk6jOWFEhIcWy/d2Ya5CpPvS3\nu4wNN4jkYAjra+8TiloRELhV4gpcEk8nkyNwLXOhYm7zQ5sIc5rfXoIrFzALB86W\nG05Nnqg+77zZIaTZpD9qekYlaEt+0OVtt9TTIeTiudQ983l6mfKwZYymrzymH1dL\nVgxBRYo+Z53QOSLiSKELfTBZxEoP1pBw6RiOHXydmJ/39hGgc2YAY/5ADwW2F2yb\nJ7+gxG6bPJ3ikDLYcD4CB5iJQdnTcDsFt3jyHAT6wOCzFAYPbHUqtzHfUM30dZBn\nnJhQF8udPLcXLaj6GW75JacCAwEAAQ==\n-----END PUBLIC KEY-----\n"
},
"signature": {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1"
],
"type": "RsaSignature2017",
"nonce": "bd60167a764a936788d9538531284dfacc258daae0297bc34a83bce136dedb5d",
"creator": "https://osada.macgirvin.com/channel/mike/public_key_pem",
"created": "2018-10-17T07:16:28Z",
"signatureValue": "WbfFVIPImkd3yNu6brz0CvZaeV242rwAbH0vy8DM4vfnXCxLr5Uv/Wj9gwP+tbooTxGaahAKBeqlGkQp8RLEo37LATrKMRLA/0V6DeeV+C5ORWR9B4WxyWiD3s/9Wf+KesFMtktNLAcMZ5PfnOS/xNYerhnpkp/gWPxtkglmLIWJv+w18A5zZ01JCxsO4QljHbhYaEUPHUfQ97abrkLECeam+FThVwdO6BFCtbjoNXHfzjpSZL/oKyBpi5/fpnqMqOLOQPs5WgBBZJvjEYYkQcoPTyxYI5NGpNbzIjGHPQNuACnOelH16A7L+q4swLWDIaEFeXQ2/5bmqVKZDZZ6usNP4QyTVszwd8jqo27qcDTNibXDUTsTdKpNQvM/3UncBuzuzmUV3FczhtGshIU1/pRVZiQycpVqPlGLvXhP/yZCe+1siyqDd+3uMaS2vkHTObSl5r+VYof+c+TcjrZXHSWnQTg8/X3zkoBWosrQ93VZcwjzMxQoARYv6rphbOoTz7RPmGAXYUt3/PDWkqDlmQDwCpLNNkJo1EidyefZBdD9HXQpCBO0ZU0NHb0JmPvg/+zU0krxlv70bm3RHA/maBETVjroIWzt7EwQEg5pL2hVnvSBG+1wF3BtRVe77etkPOHxLnYYIcAMLlVKCcgDd89DPIziQyruvkx1busHI08="
}
-}
+}
\ No newline at end of file
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 2fdfabbc5..fb82be0c4 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -1,444 +1,445 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Factory do
use ExMachina.Ecto, repo: Pleroma.Repo
alias Pleroma.Object
alias Pleroma.User
def participation_factory do
conversation = insert(:conversation)
user = insert(:user)
%Pleroma.Conversation.Participation{
conversation: conversation,
user: user,
read: false
}
end
def conversation_factory do
%Pleroma.Conversation{
ap_id: sequence(:ap_id, &"https://some_conversation/#{&1}")
}
end
def user_factory do
user = %User{
name: sequence(:name, &"Test テスト User #{&1}"),
email: sequence(:email, &"user#{&1}@example.com"),
nickname: sequence(:nickname, &"nick#{&1}"),
password_hash: Pbkdf2.hash_pwd_salt("test"),
bio: sequence(:bio, &"Tester Number #{&1}"),
+ discoverable: true,
last_digest_emailed_at: NaiveDateTime.utc_now(),
last_refreshed_at: NaiveDateTime.utc_now(),
notification_settings: %Pleroma.User.NotificationSetting{},
multi_factor_authentication_settings: %Pleroma.MFA.Settings{},
ap_enabled: true
}
%{
user
| ap_id: User.ap_id(user),
follower_address: User.ap_followers(user),
following_address: User.ap_following(user),
raw_bio: user.bio
}
end
def user_relationship_factory(attrs \\ %{}) do
source = attrs[:source] || insert(:user)
target = attrs[:target] || insert(:user)
relationship_type = attrs[:relationship_type] || :block
%Pleroma.UserRelationship{
source_id: source.id,
target_id: target.id,
relationship_type: relationship_type
}
end
def note_factory(attrs \\ %{}) do
text = sequence(:text, &"This is :moominmamma: note #{&1}")
user = attrs[:user] || insert(:user)
data = %{
"type" => "Note",
"content" => text,
"source" => text,
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
"actor" => user.ap_id,
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"likes" => [],
"like_count" => 0,
"context" => "2hu",
"summary" => "2hu",
"tag" => ["2hu"],
"emoji" => %{
"2hu" => "corndog.png"
}
}
%Pleroma.Object{
data: merge_attributes(data, Map.get(attrs, :data, %{}))
}
end
def audio_factory(attrs \\ %{}) do
text = sequence(:text, &"lain radio episode #{&1}")
user = attrs[:user] || insert(:user)
data = %{
"type" => "Audio",
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
"artist" => "lain",
"title" => text,
"album" => "lain radio",
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"actor" => user.ap_id,
"length" => 180_000
}
%Pleroma.Object{
data: merge_attributes(data, Map.get(attrs, :data, %{}))
}
end
def listen_factory do
audio = insert(:audio)
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"type" => "Listen",
"actor" => audio.data["actor"],
"to" => audio.data["to"],
"object" => audio.data,
"published" => audio.data["published"]
}
%Pleroma.Activity{
data: data,
actor: data["actor"],
recipients: data["to"]
}
end
def direct_note_factory do
user2 = insert(:user)
%Pleroma.Object{data: data} = note_factory()
%Pleroma.Object{data: Map.merge(data, %{"to" => [user2.ap_id]})}
end
def article_factory do
note_factory()
|> Map.put("type", "Article")
end
def tombstone_factory do
data = %{
"type" => "Tombstone",
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
"formerType" => "Note",
"deleted" => DateTime.utc_now() |> DateTime.to_iso8601()
}
%Pleroma.Object{
data: data
}
end
def direct_note_activity_factory do
dm = insert(:direct_note)
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"type" => "Create",
"actor" => dm.data["actor"],
"to" => dm.data["to"],
"object" => dm.data,
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"context" => dm.data["context"]
}
%Pleroma.Activity{
data: data,
actor: data["actor"],
recipients: data["to"]
}
end
def note_activity_factory(attrs \\ %{}) do
user = attrs[:user] || insert(:user)
note = attrs[:note] || insert(:note, user: user)
data_attrs = attrs[:data_attrs] || %{}
attrs = Map.drop(attrs, [:user, :note, :data_attrs])
data =
%{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"type" => "Create",
"actor" => note.data["actor"],
"to" => note.data["to"],
"object" => note.data["id"],
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"context" => note.data["context"]
}
|> Map.merge(data_attrs)
%Pleroma.Activity{
data: data,
actor: data["actor"],
recipients: data["to"]
}
|> Map.merge(attrs)
end
def article_activity_factory do
article = insert(:article)
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"type" => "Create",
"actor" => article.data["actor"],
"to" => article.data["to"],
"object" => article.data,
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"context" => article.data["context"]
}
%Pleroma.Activity{
data: data,
actor: data["actor"],
recipients: data["to"]
}
end
def announce_activity_factory(attrs \\ %{}) do
note_activity = attrs[:note_activity] || insert(:note_activity)
user = attrs[:user] || insert(:user)
data = %{
"type" => "Announce",
"actor" => note_activity.actor,
"object" => note_activity.data["id"],
"to" => [user.follower_address, note_activity.data["actor"]],
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
"context" => note_activity.data["context"]
}
%Pleroma.Activity{
data: data,
actor: user.ap_id,
recipients: data["to"]
}
end
def like_activity_factory(attrs \\ %{}) do
note_activity = attrs[:note_activity] || insert(:note_activity)
object = Object.normalize(note_activity)
user = insert(:user)
data =
%{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"actor" => user.ap_id,
"type" => "Like",
"object" => object.data["id"],
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
}
|> Map.merge(attrs[:data_attrs] || %{})
%Pleroma.Activity{
data: data
}
end
def follow_activity_factory do
follower = insert(:user)
followed = insert(:user)
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"actor" => follower.ap_id,
"type" => "Follow",
"object" => followed.ap_id,
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
}
%Pleroma.Activity{
data: data,
actor: follower.ap_id
}
end
def report_activity_factory(attrs \\ %{}) do
user = attrs[:user] || insert(:user)
activity = attrs[:activity] || insert(:note_activity)
state = attrs[:state] || "open"
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"actor" => user.ap_id,
"type" => "Flag",
"object" => [activity.actor, activity.data["id"]],
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"to" => [],
"cc" => [activity.actor],
"context" => activity.data["context"],
"state" => state
}
%Pleroma.Activity{
data: data,
actor: data["actor"],
recipients: data["to"] ++ data["cc"]
}
end
def oauth_app_factory do
%Pleroma.Web.OAuth.App{
client_name: sequence(:client_name, &"Some client #{&1}"),
redirect_uris: "https://example.com/callback",
scopes: ["read", "write", "follow", "push", "admin"],
website: "https://example.com",
client_id: Ecto.UUID.generate(),
client_secret: "aaa;/&bbb"
}
end
def instance_factory do
%Pleroma.Instances.Instance{
host: "domain.com",
unreachable_since: nil
}
end
def oauth_token_factory(attrs \\ %{}) do
scopes = Map.get(attrs, :scopes, ["read"])
oauth_app = Map.get_lazy(attrs, :app, fn -> insert(:oauth_app, scopes: scopes) end)
user = Map.get_lazy(attrs, :user, fn -> build(:user) end)
valid_until =
Map.get(attrs, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10))
%Pleroma.Web.OAuth.Token{
token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(),
refresh_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(),
scopes: scopes,
user: user,
app: oauth_app,
valid_until: valid_until
}
end
def oauth_admin_token_factory(attrs \\ %{}) do
user = Map.get_lazy(attrs, :user, fn -> build(:user, is_admin: true) end)
scopes =
attrs
|> Map.get(:scopes, ["admin"])
|> Kernel.++(["admin"])
|> Enum.uniq()
attrs = Map.merge(attrs, %{user: user, scopes: scopes})
oauth_token_factory(attrs)
end
def oauth_authorization_factory do
%Pleroma.Web.OAuth.Authorization{
token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false),
scopes: ["read", "write", "follow", "push"],
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10),
user: build(:user),
app: build(:oauth_app)
}
end
def push_subscription_factory do
%Pleroma.Web.Push.Subscription{
user: build(:user),
token: build(:oauth_token),
endpoint: "https://example.com/example/1234",
key_auth: "8eDyX_uCN0XRhSbY5hs7Hg==",
key_p256dh:
"BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA=",
data: %{}
}
end
def notification_factory do
%Pleroma.Notification{
user: build(:user)
}
end
def scheduled_activity_factory do
%Pleroma.ScheduledActivity{
user: build(:user),
scheduled_at: NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(60), :millisecond),
params: build(:note) |> Map.from_struct() |> Map.get(:data)
}
end
def registration_factory do
user = insert(:user)
%Pleroma.Registration{
user: user,
provider: "twitter",
uid: "171799000",
info: %{
"name" => "John Doe",
"email" => "john@doe.com",
"nickname" => "johndoe",
"description" => "My bio"
}
}
end
def config_factory(attrs \\ %{}) do
%Pleroma.ConfigDB{
key: sequence(:key, &String.to_atom("some_key_#{&1}")),
group: :pleroma,
value:
sequence(
:value,
&%{another_key: "#{&1}somevalue", another: "#{&1}somevalue"}
)
}
|> merge_attributes(attrs)
end
def marker_factory do
%Pleroma.Marker{
user: build(:user),
timeline: "notifications",
lock_version: 0,
last_read_id: "1"
}
end
def mfa_token_factory do
%Pleroma.MFA.Token{
token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false),
authorization: build(:oauth_authorization),
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10),
user: build(:user)
}
end
def filter_factory do
%Pleroma.Filter{
user: build(:user),
filter_id: sequence(:filter_id, & &1),
phrase: "cofe"
}
end
end
diff --git a/test/web/admin_api/search_test.exs b/test/web/admin_api/search_test.exs
index b974cedd5..d88867c52 100644
--- a/test/web/admin_api/search_test.exs
+++ b/test/web/admin_api/search_test.exs
@@ -1,181 +1,190 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.SearchTest do
use Pleroma.Web.ConnCase
alias Pleroma.Web.AdminAPI.Search
import Pleroma.Factory
describe "search for admin" do
test "it ignores case" do
insert(:user, nickname: "papercoach")
insert(:user, nickname: "CanadaPaperCoach")
{:ok, _results, count} =
Search.user(%{
query: "paper",
local: false,
page: 1,
page_size: 50
})
assert count == 2
end
test "it returns local/external users" do
insert(:user, local: true)
insert(:user, local: false)
insert(:user, local: false)
{:ok, _results, local_count} =
Search.user(%{
query: "",
local: true
})
{:ok, _results, external_count} =
Search.user(%{
query: "",
external: true
})
assert local_count == 1
assert external_count == 2
end
test "it returns active/deactivated users" do
insert(:user, deactivated: true)
insert(:user, deactivated: true)
insert(:user, deactivated: false)
{:ok, _results, active_count} =
Search.user(%{
query: "",
active: true
})
{:ok, _results, deactivated_count} =
Search.user(%{
query: "",
deactivated: true
})
assert active_count == 1
assert deactivated_count == 2
end
test "it returns specific user" do
insert(:user)
insert(:user)
user = insert(:user, nickname: "bob", local: true, deactivated: false)
{:ok, _results, total_count} = Search.user(%{query: ""})
{:ok, [^user], count} =
Search.user(%{
query: "Bo",
active: true,
local: true
})
assert total_count == 3
assert count == 1
end
test "it returns user by domain" do
insert(:user)
insert(:user)
user = insert(:user, nickname: "some@domain.com")
{:ok, _results, total} = Search.user()
{:ok, [^user], count} = Search.user(%{query: "domain.com"})
assert total == 3
assert count == 1
end
test "it return user by full nickname" do
insert(:user)
insert(:user)
user = insert(:user, nickname: "some@domain.com")
{:ok, _results, total} = Search.user()
{:ok, [^user], count} = Search.user(%{query: "some@domain.com"})
assert total == 3
assert count == 1
end
test "it returns admin user" do
admin = insert(:user, is_admin: true)
insert(:user)
insert(:user)
{:ok, _results, total} = Search.user()
{:ok, [^admin], count} = Search.user(%{is_admin: true})
assert total == 3
assert count == 1
end
test "it returns moderator user" do
moderator = insert(:user, is_moderator: true)
insert(:user)
insert(:user)
{:ok, _results, total} = Search.user()
{:ok, [^moderator], count} = Search.user(%{is_moderator: true})
assert total == 3
assert count == 1
end
test "it returns users with tags" do
user1 = insert(:user, tags: ["first"])
user2 = insert(:user, tags: ["second"])
insert(:user)
insert(:user)
{:ok, _results, total} = Search.user()
{:ok, users, count} = Search.user(%{tags: ["first", "second"]})
assert total == 4
assert count == 2
assert user1 in users
assert user2 in users
end
test "it returns user by display name" do
user = insert(:user, name: "Display name")
insert(:user)
insert(:user)
{:ok, _results, total} = Search.user()
{:ok, [^user], count} = Search.user(%{name: "display"})
assert total == 3
assert count == 1
end
test "it returns user by email" do
user = insert(:user, email: "some@example.com")
insert(:user)
insert(:user)
{:ok, _results, total} = Search.user()
{:ok, [^user], count} = Search.user(%{email: "some@example.com"})
assert total == 3
assert count == 1
end
test "it returns unapproved user" do
unapproved = insert(:user, approval_pending: true)
insert(:user)
insert(:user)
{:ok, _results, total} = Search.user()
{:ok, [^unapproved], count} = Search.user(%{need_approval: true})
assert total == 3
assert count == 1
end
+
+ test "it returns non-discoverable users" do
+ insert(:user)
+ insert(:user, discoverable: false)
+
+ {:ok, _results, total} = Search.user()
+
+ assert total == 2
+ end
end
end
diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs
index c5f491d6b..a54b765ef 100644
--- a/test/web/mastodon_api/views/account_view_test.exs
+++ b/test/web/mastodon_api/views/account_view_test.exs
@@ -1,569 +1,569 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
use Pleroma.DataCase
alias Pleroma.User
alias Pleroma.UserRelationship
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI.AccountView
import Pleroma.Factory
import Tesla.Mock
setup do
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end
test "Represent a user account" do
background_image = %{
"url" => [%{"href" => "https://example.com/images/asuka_hospital.png"}]
}
user =
insert(:user, %{
follower_count: 3,
note_count: 5,
background: background_image,
nickname: "shp@shitposter.club",
name: ":karjalanpiirakka: shp",
bio:
"<script src=\"invalid-html\"></script><span>valid html</span>. a<br>b<br/>c<br >d<br />f '&<>\"",
inserted_at: ~N[2017-08-15 15:47:06.597036],
emoji: %{"karjalanpiirakka" => "/file.png"},
raw_bio: "valid html. a\nb\nc\nd\nf '&<>\""
})
expected = %{
id: to_string(user.id),
username: "shp",
acct: user.nickname,
display_name: user.name,
locked: false,
created_at: "2017-08-15T15:47:06.000Z",
followers_count: 3,
following_count: 0,
statuses_count: 5,
note: "<span>valid html</span>. a<br/>b<br/>c<br/>d<br/>f &#39;&amp;&lt;&gt;&quot;",
url: user.ap_id,
avatar: "http://localhost:4001/images/avi.png",
avatar_static: "http://localhost:4001/images/avi.png",
header: "http://localhost:4001/images/banner.png",
header_static: "http://localhost:4001/images/banner.png",
emojis: [
%{
static_url: "/file.png",
url: "/file.png",
shortcode: "karjalanpiirakka",
visible_in_picker: false
}
],
fields: [],
bot: false,
source: %{
note: "valid html. a\nb\nc\nd\nf '&<>\"",
sensitive: false,
pleroma: %{
actor_type: "Person",
- discoverable: false
+ discoverable: true
},
fields: []
},
pleroma: %{
ap_id: user.ap_id,
background_image: "https://example.com/images/asuka_hospital.png",
favicon: nil,
confirmation_pending: false,
tags: [],
is_admin: false,
is_moderator: false,
hide_favorites: true,
hide_followers: false,
hide_follows: false,
hide_followers_count: false,
hide_follows_count: false,
relationship: %{},
skip_thread_containment: false,
accepts_chat_messages: nil
}
}
assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true})
end
describe "favicon" do
setup do
[user: insert(:user)]
end
test "is parsed when :instance_favicons is enabled", %{user: user} do
clear_config([:instances_favicons, :enabled], true)
assert %{
pleroma: %{
favicon:
"https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png"
}
} = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
end
test "is nil when :instances_favicons is disabled", %{user: user} do
assert %{pleroma: %{favicon: nil}} =
AccountView.render("show.json", %{user: user, skip_visibility_check: true})
end
end
test "Represent the user account for the account owner" do
user = insert(:user)
notification_settings = %{
block_from_strangers: false,
hide_notification_contents: false
}
privacy = user.default_scope
assert %{
pleroma: %{notification_settings: ^notification_settings, allow_following_move: true},
source: %{privacy: ^privacy}
} = AccountView.render("show.json", %{user: user, for: user})
end
test "Represent a Service(bot) account" do
user =
insert(:user, %{
follower_count: 3,
note_count: 5,
actor_type: "Service",
nickname: "shp@shitposter.club",
inserted_at: ~N[2017-08-15 15:47:06.597036]
})
expected = %{
id: to_string(user.id),
username: "shp",
acct: user.nickname,
display_name: user.name,
locked: false,
created_at: "2017-08-15T15:47:06.000Z",
followers_count: 3,
following_count: 0,
statuses_count: 5,
note: user.bio,
url: user.ap_id,
avatar: "http://localhost:4001/images/avi.png",
avatar_static: "http://localhost:4001/images/avi.png",
header: "http://localhost:4001/images/banner.png",
header_static: "http://localhost:4001/images/banner.png",
emojis: [],
fields: [],
bot: true,
source: %{
note: user.bio,
sensitive: false,
pleroma: %{
actor_type: "Service",
- discoverable: false
+ discoverable: true
},
fields: []
},
pleroma: %{
ap_id: user.ap_id,
background_image: nil,
favicon: nil,
confirmation_pending: false,
tags: [],
is_admin: false,
is_moderator: false,
hide_favorites: true,
hide_followers: false,
hide_follows: false,
hide_followers_count: false,
hide_follows_count: false,
relationship: %{},
skip_thread_containment: false,
accepts_chat_messages: nil
}
}
assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true})
end
test "Represent a Funkwhale channel" do
{:ok, user} =
User.get_or_fetch_by_ap_id(
"https://channels.tests.funkwhale.audio/federation/actors/compositions"
)
assert represented =
AccountView.render("show.json", %{user: user, skip_visibility_check: true})
assert represented.acct == "compositions@channels.tests.funkwhale.audio"
assert represented.url == "https://channels.tests.funkwhale.audio/channels/compositions"
end
test "Represent a deactivated user for an admin" do
admin = insert(:user, is_admin: true)
deactivated_user = insert(:user, deactivated: true)
represented = AccountView.render("show.json", %{user: deactivated_user, for: admin})
assert represented[:pleroma][:deactivated] == true
end
test "Represent a smaller mention" do
user = insert(:user)
expected = %{
id: to_string(user.id),
acct: user.nickname,
username: user.nickname,
url: user.ap_id
}
assert expected == AccountView.render("mention.json", %{user: user})
end
test "demands :for or :skip_visibility_check option for account rendering" do
clear_config([:restrict_unauthenticated, :profiles, :local], false)
user = insert(:user)
user_id = user.id
assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: nil})
assert %{id: ^user_id} = AccountView.render("show.json", %{user: user, for: user})
assert %{id: ^user_id} =
AccountView.render("show.json", %{user: user, skip_visibility_check: true})
assert_raise RuntimeError, ~r/:skip_visibility_check or :for option is required/, fn ->
AccountView.render("show.json", %{user: user})
end
end
describe "relationship" do
defp test_relationship_rendering(user, other_user, expected_result) do
opts = %{user: user, target: other_user, relationships: nil}
assert expected_result == AccountView.render("relationship.json", opts)
relationships_opt = UserRelationship.view_relationships_option(user, [other_user])
opts = Map.put(opts, :relationships, relationships_opt)
assert expected_result == AccountView.render("relationship.json", opts)
assert [expected_result] ==
AccountView.render("relationships.json", %{user: user, targets: [other_user]})
end
@blank_response %{
following: false,
followed_by: false,
blocking: false,
blocked_by: false,
muting: false,
muting_notifications: false,
subscribing: false,
requested: false,
domain_blocking: false,
showing_reblogs: true,
endorsed: false
}
test "represent a relationship for the following and followed user" do
user = insert(:user)
other_user = insert(:user)
{:ok, user} = User.follow(user, other_user)
{:ok, other_user} = User.follow(other_user, user)
{:ok, _subscription} = User.subscribe(user, other_user)
{:ok, _user_relationships} = User.mute(user, other_user, true)
{:ok, _reblog_mute} = CommonAPI.hide_reblogs(user, other_user)
expected =
Map.merge(
@blank_response,
%{
following: true,
followed_by: true,
muting: true,
muting_notifications: true,
subscribing: true,
showing_reblogs: false,
id: to_string(other_user.id)
}
)
test_relationship_rendering(user, other_user, expected)
end
test "represent a relationship for the blocking and blocked user" do
user = insert(:user)
other_user = insert(:user)
{:ok, user} = User.follow(user, other_user)
{:ok, _subscription} = User.subscribe(user, other_user)
{:ok, _user_relationship} = User.block(user, other_user)
{:ok, _user_relationship} = User.block(other_user, user)
expected =
Map.merge(
@blank_response,
%{following: false, blocking: true, blocked_by: true, id: to_string(other_user.id)}
)
test_relationship_rendering(user, other_user, expected)
end
test "represent a relationship for the user blocking a domain" do
user = insert(:user)
other_user = insert(:user, ap_id: "https://bad.site/users/other_user")
{:ok, user} = User.block_domain(user, "bad.site")
expected =
Map.merge(
@blank_response,
%{domain_blocking: true, blocking: false, id: to_string(other_user.id)}
)
test_relationship_rendering(user, other_user, expected)
end
test "represent a relationship for the user with a pending follow request" do
user = insert(:user)
other_user = insert(:user, locked: true)
{:ok, user, other_user, _} = CommonAPI.follow(user, other_user)
user = User.get_cached_by_id(user.id)
other_user = User.get_cached_by_id(other_user.id)
expected =
Map.merge(
@blank_response,
%{requested: true, following: false, id: to_string(other_user.id)}
)
test_relationship_rendering(user, other_user, expected)
end
end
test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
user = insert(:user, pleroma_settings_store: %{fe: "test"})
result =
AccountView.render("show.json", %{user: user, for: user, with_pleroma_settings: true})
assert result.pleroma.settings_store == %{:fe => "test"}
result = AccountView.render("show.json", %{user: user, for: nil, with_pleroma_settings: true})
assert result.pleroma[:settings_store] == nil
result = AccountView.render("show.json", %{user: user, for: user})
assert result.pleroma[:settings_store] == nil
end
test "doesn't sanitize display names" do
user = insert(:user, name: "<marquee> username </marquee>")
result = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
assert result.display_name == "<marquee> username </marquee>"
end
test "never display nil user follow counts" do
user = insert(:user, following_count: 0, follower_count: 0)
result = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
assert result.following_count == 0
assert result.followers_count == 0
end
describe "hiding follows/following" do
test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do
user =
insert(:user, %{
hide_followers: true,
hide_followers_count: true,
hide_follows: true,
hide_follows_count: true
})
other_user = insert(:user)
{:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
assert %{
followers_count: 0,
following_count: 0,
pleroma: %{hide_follows_count: true, hide_followers_count: true}
} = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
end
test "shows when follows/followers are hidden" do
user = insert(:user, hide_followers: true, hide_follows: true)
other_user = insert(:user)
{:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
assert %{
followers_count: 1,
following_count: 1,
pleroma: %{hide_follows: true, hide_followers: true}
} = AccountView.render("show.json", %{user: user, skip_visibility_check: true})
end
test "shows actual follower/following count to the account owner" do
user = insert(:user, hide_followers: true, hide_follows: true)
other_user = insert(:user)
{:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user)
assert User.following?(user, other_user)
assert Pleroma.FollowingRelationship.follower_count(other_user) == 1
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
assert %{
followers_count: 1,
following_count: 1
} = AccountView.render("show.json", %{user: user, for: user})
end
test "shows unread_conversation_count only to the account owner" do
user = insert(:user)
other_user = insert(:user)
{:ok, _activity} =
CommonAPI.post(other_user, %{
status: "Hey @#{user.nickname}.",
visibility: "direct"
})
user = User.get_cached_by_ap_id(user.ap_id)
assert AccountView.render("show.json", %{user: user, for: other_user})[:pleroma][
:unread_conversation_count
] == nil
assert AccountView.render("show.json", %{user: user, for: user})[:pleroma][
:unread_conversation_count
] == 1
end
test "shows unread_count only to the account owner" do
user = insert(:user)
insert_list(7, :notification, user: user, activity: insert(:note_activity))
other_user = insert(:user)
user = User.get_cached_by_ap_id(user.ap_id)
assert AccountView.render(
"show.json",
%{user: user, for: other_user}
)[:pleroma][:unread_notifications_count] == nil
assert AccountView.render(
"show.json",
%{user: user, for: user}
)[:pleroma][:unread_notifications_count] == 7
end
end
describe "follow requests counter" do
test "shows zero when no follow requests are pending" do
user = insert(:user)
assert %{follow_requests_count: 0} =
AccountView.render("show.json", %{user: user, for: user})
other_user = insert(:user)
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
assert %{follow_requests_count: 0} =
AccountView.render("show.json", %{user: user, for: user})
end
test "shows non-zero when follow requests are pending" do
user = insert(:user, locked: true)
assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
other_user = insert(:user)
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
assert %{locked: true, follow_requests_count: 1} =
AccountView.render("show.json", %{user: user, for: user})
end
test "decreases when accepting a follow request" do
user = insert(:user, locked: true)
assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
other_user = insert(:user)
{:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user)
assert %{locked: true, follow_requests_count: 1} =
AccountView.render("show.json", %{user: user, for: user})
{:ok, _other_user} = CommonAPI.accept_follow_request(other_user, user)
assert %{locked: true, follow_requests_count: 0} =
AccountView.render("show.json", %{user: user, for: user})
end
test "decreases when rejecting a follow request" do
user = insert(:user, locked: true)
assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
other_user = insert(:user)
{:ok, other_user, user, _activity} = CommonAPI.follow(other_user, user)
assert %{locked: true, follow_requests_count: 1} =
AccountView.render("show.json", %{user: user, for: user})
{:ok, _other_user} = CommonAPI.reject_follow_request(other_user, user)
assert %{locked: true, follow_requests_count: 0} =
AccountView.render("show.json", %{user: user, for: user})
end
test "shows non-zero when historical unapproved requests are present" do
user = insert(:user, locked: true)
assert %{locked: true} = AccountView.render("show.json", %{user: user, for: user})
other_user = insert(:user)
{:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
{:ok, user} = User.update_and_set_cache(user, %{locked: false})
assert %{locked: false, follow_requests_count: 1} =
AccountView.render("show.json", %{user: user, for: user})
end
end
test "uses mediaproxy urls when it's enabled" do
clear_config([:media_proxy, :enabled], true)
user =
insert(:user,
avatar: %{"url" => [%{"href" => "https://evil.website/avatar.png"}]},
banner: %{"url" => [%{"href" => "https://evil.website/banner.png"}]},
emoji: %{"joker_smile" => "https://evil.website/society.png"}
)
AccountView.render("show.json", %{user: user, skip_visibility_check: true})
|> Enum.all?(fn
{key, url} when key in [:avatar, :avatar_static, :header, :header_static] ->
String.starts_with?(url, Pleroma.Web.base_url())
{:emojis, emojis} ->
Enum.all?(emojis, fn %{url: url, static_url: static_url} ->
String.starts_with?(url, Pleroma.Web.base_url()) &&
String.starts_with?(static_url, Pleroma.Web.base_url())
end)
_ ->
true
end)
|> assert()
end
end

File Metadata

Mime Type
text/x-diff
Expires
Thu, Nov 28, 5:16 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40848
Default Alt Text
(49 KB)

Event Timeline