Browse Source

Merge branch 'develop' into support/update_oban

contrib/munin-healthcheck
Maksim Pechnikov 4 years ago
parent
commit
648cc699e5
79 changed files with 1799 additions and 255 deletions
  1. +2
    -0
      CHANGELOG.md
  2. +2
    -2
      COPYING
  3. +6
    -0
      coveralls.json
  4. +13
    -0
      docs/API/admin_api.md
  5. +11
    -7
      docs/API/differences_in_mastoapi_responses.md
  6. +1
    -1
      docs/API/pleroma_api.md
  7. +2
    -2
      docs/administration/CLI_tasks/emoji.md
  8. +49
    -31
      lib/mix/tasks/pleroma/emoji.ex
  9. +22
    -4
      lib/pleroma/formatter.ex
  10. +3
    -1
      lib/pleroma/gun/conn.ex
  11. +12
    -0
      lib/pleroma/object/containment.ex
  12. +8
    -12
      lib/pleroma/user.ex
  13. +15
    -0
      lib/pleroma/web/activity_pub/activity_pub.ex
  14. +43
    -0
      lib/pleroma/web/activity_pub/builder.ex
  15. +37
    -0
      lib/pleroma/web/activity_pub/object_validator.ex
  16. +32
    -0
      lib/pleroma/web/activity_pub/object_validators/common_validations.ex
  17. +30
    -0
      lib/pleroma/web/activity_pub/object_validators/create_validator.ex
  18. +57
    -0
      lib/pleroma/web/activity_pub/object_validators/like_validator.ex
  19. +63
    -0
      lib/pleroma/web/activity_pub/object_validators/note_validator.ex
  20. +34
    -0
      lib/pleroma/web/activity_pub/object_validators/types/date_time.ex
  21. +29
    -0
      lib/pleroma/web/activity_pub/object_validators/types/object_id.ex
  22. +42
    -0
      lib/pleroma/web/activity_pub/pipeline.ex
  23. +28
    -0
      lib/pleroma/web/activity_pub/side_effects.ex
  24. +56
    -9
      lib/pleroma/web/activity_pub/transmogrifier.ex
  25. +14
    -3
      lib/pleroma/web/admin_api/admin_api_controller.ex
  26. +44
    -0
      lib/pleroma/web/api_spec.ex
  27. +27
    -0
      lib/pleroma/web/api_spec/helpers.ex
  28. +96
    -0
      lib/pleroma/web/api_spec/operations/app_operation.ex
  29. +33
    -0
      lib/pleroma/web/api_spec/schemas/app_create_request.ex
  30. +33
    -0
      lib/pleroma/web/api_spec/schemas/app_create_response.ex
  31. +47
    -11
      lib/pleroma/web/common_api/common_api.ex
  32. +6
    -3
      lib/pleroma/web/mastodon_api/controllers/app_controller.ex
  33. +2
    -1
      lib/pleroma/web/mastodon_api/controllers/notification_controller.ex
  34. +3
    -3
      lib/pleroma/web/mastodon_api/controllers/status_controller.ex
  35. +5
    -3
      lib/pleroma/web/mastodon_api/views/account_view.ex
  36. +1
    -1
      lib/pleroma/web/mastodon_api/views/notification_view.ex
  37. +6
    -4
      lib/pleroma/web/mastodon_api/views/status_view.ex
  38. +2
    -1
      lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
  39. +6
    -1
      lib/pleroma/web/oauth/scopes.ex
  40. +14
    -1
      lib/pleroma/web/router.ex
  41. +12
    -2
      mix.exs
  42. +1
    -0
      mix.lock
  43. BIN
      test/fixtures/emoji/packs/blank.png.zip
  44. +10
    -0
      test/fixtures/emoji/packs/default-manifest.json
  45. +3
    -0
      test/fixtures/emoji/packs/finmoji.json
  46. +10
    -0
      test/fixtures/emoji/packs/manifest.json
  47. +10
    -14
      test/formatter_test.exs
  48. +4
    -4
      test/notification_test.exs
  49. +2
    -1
      test/object_test.exs
  50. +1
    -1
      test/stat_test.exs
  51. +1
    -1
      test/tasks/database_test.exs
  52. +226
    -0
      test/tasks/emoji_test.exs
  53. +3
    -3
      test/user_test.exs
  54. +8
    -8
      test/web/activity_pub/activity_pub_test.exs
  55. +83
    -0
      test/web/activity_pub/object_validator_test.exs
  56. +35
    -0
      test/web/activity_pub/object_validators/note_validator_test.exs
  57. +32
    -0
      test/web/activity_pub/object_validators/types/date_time_test.exs
  58. +37
    -0
      test/web/activity_pub/object_validators/types/object_id_test.exs
  59. +87
    -0
      test/web/activity_pub/pipeline_test.exs
  60. +34
    -0
      test/web/activity_pub/side_effects_test.exs
  61. +3
    -1
      test/web/activity_pub/transmogrifier_test.exs
  62. +1
    -1
      test/web/activity_pub/views/object_view_test.exs
  63. +37
    -2
      test/web/admin_api/admin_api_controller_test.exs
  64. +45
    -0
      test/web/api_spec/app_operation_test.exs
  65. +8
    -5
      test/web/common_api/common_api_test.exs
  66. +3
    -3
      test/web/common_api/common_api_utils_test.exs
  67. +40
    -32
      test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs
  68. +3
    -1
      test/web/mastodon_api/controllers/account_controller_test.exs
  69. +2
    -2
      test/web/mastodon_api/controllers/app_controller_test.exs
  70. +25
    -9
      test/web/mastodon_api/controllers/notification_controller_test.exs
  71. +14
    -10
      test/web/mastodon_api/controllers/status_controller_test.exs
  72. +3
    -0
      test/web/mastodon_api/views/account_view_test.exs
  73. +1
    -1
      test/web/mastodon_api/views/notification_view_test.exs
  74. +56
    -30
      test/web/node_info_test.exs
  75. +1
    -1
      test/web/ostatus/ostatus_controller_test.exs
  76. +8
    -8
      test/web/pleroma_api/controllers/account_controller_test.exs
  77. +1
    -1
      test/web/push/impl_test.exs
  78. +12
    -12
      test/web/streamer/streamer_test.exs
  79. +1
    -1
      test/web/twitter_api/twitter_api_test.exs

+ 2
- 0
CHANGELOG.md View File

@@ -12,11 +12,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Added
- NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list.
- NodeInfo: `pleroma_emoji_reactions` to the `features` list.
- Configuration: `:restrict_unauthenticated` setting, restrict access for unauthenticated users to timelines (public and federate), user profiles and statuses.
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma won’t start. For hackney OTP update is not required.
<details>
<summary>API Changes</summary>
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
</details>

## [2.0.0] - 2019-03-08


+ 2
- 2
COPYING View File

@@ -1,4 +1,4 @@
Unless otherwise stated this repository is copyright © 2017-2019
Unless otherwise stated this repository is copyright © 2017-2020
Pleroma Authors <https://pleroma.social/>, and is distributed under
The GNU Affero General Public License Version 3, you should have received a
copy of the license file as AGPL-3.
@@ -23,7 +23,7 @@ priv/static/images/pleroma-fox-tan-shy.png

---

The following files are copyright © 2017-2019 Pleroma Authors
The following files are copyright © 2017-2020 Pleroma Authors
<https://pleroma.social/>, and are distributed under the Creative Commons
Attribution-ShareAlike 4.0 International license, you should have received
a copy of the license file as CC-BY-SA-4.0.


+ 6
- 0
coveralls.json View File

@@ -0,0 +1,6 @@
{
"skip_files": [
"test/support",
"lib/mix/tasks/pleroma/benchmark.ex"
]
}

+ 13
- 0
docs/API/admin_api.md View File

@@ -392,6 +392,19 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- `email`
- `name`, optional

- Response:
- On success: `204`, empty response
- On failure:
- 400 Bad Request, JSON:

```json
[
{
"error": "Appropriate error message here"
}
]
```

## `GET /api/pleroma/admin/users/:nickname/password_reset`

### Get a password reset token for a given nickname


+ 11
- 7
docs/API/differences_in_mastoapi_responses.md View File

@@ -164,6 +164,7 @@ Additional parameters can be added to the JSON body/Form data:
- `actor_type` - the type of this account.

### Pleroma Settings Store

Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.

The parameter should have a form of `{frontend_name: {...}}`, with `frontend_name` identifying your type of client, e.g. `pleroma_fe`. It will overwrite everything under this property, but will not overwrite other frontend's settings.
@@ -172,17 +173,20 @@ This information is returned in the `verify_credentials` endpoint.

## Authentication

*Pleroma supports refreshing tokens.
*Pleroma supports refreshing tokens.*

`POST /oauth/token`
Post here request with grant_type=refresh_token to obtain new access token. Returns an access token.

Post here request with `grant_type=refresh_token` to obtain new access token. Returns an access token.

## Account Registration

`POST /api/v1/accounts`

Has theses additional parameters (which are the same as in Pleroma-API):
* `fullname`: optional
* `bio`: optional
* `captcha_solution`: optional, contains provider-specific captcha solution,
* `captcha_token`: optional, contains provider-specific captcha token
* `token`: invite token required when the registerations aren't public.

- `fullname`: optional
- `bio`: optional
- `captcha_solution`: optional, contains provider-specific captcha solution,
- `captcha_token`: optional, contains provider-specific captcha token
- `token`: invite token required when the registrations aren't public.

+ 1
- 1
docs/API/pleroma_api.md View File

@@ -431,7 +431,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa

# Emoji Reactions

Emoji reactions work a lot like favourites do. They make it possible to react to a post with a single emoji character.
Emoji reactions work a lot like favourites do. They make it possible to react to a post with a single emoji character. To detect the presence of this feature, you can check `pleroma_emoji_reactions` entry in the features list of nodeinfo.

## `PUT /api/v1/pleroma/statuses/:id/reactions/:emoji`
### React to a post with a unicode emoji


+ 2
- 2
docs/administration/CLI_tasks/emoji.md View File

@@ -39,8 +39,8 @@ mix pleroma.emoji get-packs [option ...] <pack ...>
mix pleroma.emoji gen-pack PACK-URL
```

Currently, only .zip archives are recognized as remote pack files and packs are therefore assumed to be zip archives. This command is intended to run interactively and will first ask you some basic questions about the pack, then download the remote file and generate an SHA256 checksum for it, then generate an emoji file list for you.
Currently, only .zip archives are recognized as remote pack files and packs are therefore assumed to be zip archives. This command is intended to run interactively and will first ask you some basic questions about the pack, then download the remote file and generate an SHA256 checksum for it, then generate an emoji file list for you.

The manifest entry will either be written to a newly created `index.json` file or appended to the existing one, *replacing* the old pack with the same name if it was in the file previously.
The manifest entry will either be written to a newly created `pack_name.json` file (pack name is asked in questions) or appended to the existing one, *replacing* the old pack with the same name if it was in the file previously.

The file list will be written to the file specified previously, *replacing* that file. You _should_ check that the file list doesn't contain anything you don't need in the pack, that is, anything that is not an emoji (the whole pack is downloaded, but only emoji files are extracted).

+ 49
- 31
lib/mix/tasks/pleroma/emoji.ex View File

@@ -14,8 +14,8 @@ defmodule Mix.Tasks.Pleroma.Emoji do

{options, [], []} = parse_global_opts(args)

manifest =
fetch_manifest(if options[:manifest], do: options[:manifest], else: default_manifest())
url_or_path = options[:manifest] || default_manifest()
manifest = fetch_manifest(url_or_path)

Enum.each(manifest, fn {name, info} ->
to_print = [
@@ -40,9 +40,9 @@ defmodule Mix.Tasks.Pleroma.Emoji do

{options, pack_names, []} = parse_global_opts(args)

manifest_url = if options[:manifest], do: options[:manifest], else: default_manifest()
url_or_path = options[:manifest] || default_manifest()

manifest = fetch_manifest(manifest_url)
manifest = fetch_manifest(url_or_path)

for pack_name <- pack_names do
if Map.has_key?(manifest, pack_name) do
@@ -75,7 +75,10 @@ defmodule Mix.Tasks.Pleroma.Emoji do
end

# The url specified in files should be in the same directory
files_url = Path.join(Path.dirname(manifest_url), pack["files"])
files_url =
url_or_path
|> Path.dirname()
|> Path.join(pack["files"])

IO.puts(
IO.ANSI.format([
@@ -133,38 +136,51 @@ defmodule Mix.Tasks.Pleroma.Emoji do
end
end

def run(["gen-pack", src]) do
def run(["gen-pack" | args]) do
start_pleroma()

proposed_name = Path.basename(src) |> Path.rootname()
name = String.trim(IO.gets("Pack name [#{proposed_name}]: "))
# If there's no name, use the default one
name = if String.length(name) > 0, do: name, else: proposed_name
{opts, [src], []} =
OptionParser.parse(
args,
strict: [
name: :string,
license: :string,
homepage: :string,
description: :string,
files: :string,
extensions: :string
]
)

license = String.trim(IO.gets("License: "))
homepage = String.trim(IO.gets("Homepage: "))
description = String.trim(IO.gets("Description: "))
proposed_name = Path.basename(src) |> Path.rootname()
name = get_option(opts, :name, "Pack name:", proposed_name)
license = get_option(opts, :license, "License:")
homepage = get_option(opts, :homepage, "Homepage:")
description = get_option(opts, :description, "Description:")

proposed_files_name = "#{name}.json"
files_name = String.trim(IO.gets("Save file list to [#{proposed_files_name}]: "))
files_name = if String.length(files_name) > 0, do: files_name, else: proposed_files_name
proposed_files_name = "#{name}_files.json"
files_name = get_option(opts, :files, "Save file list to:", proposed_files_name)

default_exts = [".png", ".gif"]
default_exts_str = Enum.join(default_exts, " ")

exts =
String.trim(
IO.gets("Emoji file extensions (separated with spaces) [#{default_exts_str}]: ")
custom_exts =
get_option(
opts,
:extensions,
"Emoji file extensions (separated with spaces):",
Enum.join(default_exts, " ")
)
|> String.split(" ", trim: true)

exts =
if String.length(exts) > 0 do
String.split(exts, " ")
|> Enum.filter(fn e -> e |> String.trim() |> String.length() > 0 end)
else
if MapSet.equal?(MapSet.new(default_exts), MapSet.new(custom_exts)) do
default_exts
else
custom_exts
end

IO.puts("Using #{Enum.join(exts, " ")} extensions")

IO.puts("Downloading the pack and generating SHA256")

binary_archive = Tesla.get!(client(), src).body
@@ -194,14 +210,16 @@ defmodule Mix.Tasks.Pleroma.Emoji do
IO.puts("""

#{files_name} has been created and contains the list of all found emojis in the pack.
Please review the files in the remove those not needed.
Please review the files in the pack and remove those not needed.
""")

if File.exists?("index.json") do
existing_data = File.read!("index.json") |> Jason.decode!()
pack_file = "#{name}.json"

if File.exists?(pack_file) do
existing_data = File.read!(pack_file) |> Jason.decode!()

File.write!(
"index.json",
pack_file,
Jason.encode!(
Map.merge(
existing_data,
@@ -211,11 +229,11 @@ defmodule Mix.Tasks.Pleroma.Emoji do
)
)

IO.puts("index.json file has been update with the #{name} pack")
IO.puts("#{pack_file} has been updated with the #{name} pack")
else
File.write!("index.json", Jason.encode!(pack_json, pretty: true))
File.write!(pack_file, Jason.encode!(pack_json, pretty: true))

IO.puts("index.json has been created with the #{name} pack")
IO.puts("#{pack_file} has been created with the #{name} pack")
end
end



+ 22
- 4
lib/pleroma/formatter.ex View File

@@ -35,9 +35,19 @@ defmodule Pleroma.Formatter do
nickname_text = get_nickname_text(nickname, opts)

link =
~s(<span class="h-card"><a data-user="#{id}" class="u-url mention" href="#{ap_id}" rel="ugc">@<span>#{
nickname_text
}</span></a></span>)
Phoenix.HTML.Tag.content_tag(
:span,
Phoenix.HTML.Tag.content_tag(
:a,
["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
"data-user": id,
class: "u-url mention",
href: ap_id,
rel: "ugc"
),
class: "h-card"
)
|> Phoenix.HTML.safe_to_string()

{link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}

@@ -49,7 +59,15 @@ defmodule Pleroma.Formatter do
def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
tag = String.downcase(tag)
url = "#{Pleroma.Web.base_url()}/tag/#{tag}"
link = ~s(<a class="hashtag" data-tag="#{tag}" href="#{url}" rel="tag ugc">#{tag_text}</a>)

link =
Phoenix.HTML.Tag.content_tag(:a, tag_text,
class: "hashtag",
"data-tag": tag,
href: url,
rel: "tag ugc"
)
|> Phoenix.HTML.safe_to_string()

{link, %{acc | tags: MapSet.put(acc.tags, {tag_text, tag})}}
end


+ 3
- 1
lib/pleroma/gun/conn.ex View File

@@ -49,8 +49,10 @@ defmodule Pleroma.Gun.Conn do

key = "#{uri.scheme}:#{uri.host}:#{uri.port}"

max_connections = pool_opts[:max_connections] || 250

conn_pid =
if Connections.count(name) < opts[:max_connection] do
if Connections.count(name) < max_connections do
do_open(uri, opts)
else
close_least_used_and_do_open(name, uri, opts)


+ 12
- 0
lib/pleroma/object/containment.ex View File

@@ -32,6 +32,18 @@ defmodule Pleroma.Object.Containment do
get_actor(%{"actor" => actor})
end

def get_object(%{"object" => id}) when is_binary(id) do
id
end

def get_object(%{"object" => %{"id" => id}}) when is_binary(id) do
id
end

def get_object(_) do
nil
end

# TODO: We explicitly allow 'tag' URIs through, due to references to legacy OStatus
# objects being present in the test suite environment. Once these objects are
# removed, please also remove this.


+ 8
- 12
lib/pleroma/user.ex View File

@@ -16,6 +16,7 @@ defmodule Pleroma.User do
alias Pleroma.Conversation.Participation
alias Pleroma.Delivery
alias Pleroma.FollowingRelationship
alias Pleroma.Formatter
alias Pleroma.HTML
alias Pleroma.Keys
alias Pleroma.Notification
@@ -452,7 +453,7 @@ defmodule Pleroma.User do

fields =
raw_fields
|> Enum.map(fn f -> Map.update!(f, "value", &AutoLinker.link(&1)) end)
|> Enum.map(fn f -> Map.update!(f, "value", &parse_fields(&1)) end)

changeset
|> put_change(:raw_fields, raw_fields)
@@ -462,6 +463,12 @@ defmodule Pleroma.User do
end
end

defp parse_fields(value) do
value
|> Formatter.linkify(mentions_format: :full)
|> elem(0)
end

defp put_change_if_present(changeset, map_field, value_function) do
if value = get_change(changeset, map_field) do
with {:ok, new_value} <- value_function.(value) do
@@ -1979,17 +1986,6 @@ defmodule Pleroma.User do

def fields(%{fields: fields}), do: fields

def sanitized_fields(%User{} = user) do
user
|> User.fields()
|> Enum.map(fn %{"name" => name, "value" => value} ->
%{
"name" => name,
"value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)
}
end)
end

def validate_fields(changeset, remote? \\ false) do
limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields
limit = Pleroma.Config.get([:instance, limit_name], 0)


+ 15
- 0
lib/pleroma/web/activity_pub/activity_pub.ex View File

@@ -125,6 +125,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do

def increase_poll_votes_if_vote(_create_data), do: :noop

@spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
def persist(object, meta) do
with local <- Keyword.fetch!(meta, :local),
{recipients, _, _} <- get_recipients(object),
{:ok, activity} <-
Repo.insert(%Activity{
data: object,
local: local,
recipients: recipients,
actor: object["actor"]
}) do
{:ok, activity, meta}
end
end

@spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
with nil <- Activity.normalize(map),


+ 43
- 0
lib/pleroma/web/activity_pub/builder.ex View File

@@ -0,0 +1,43 @@
defmodule Pleroma.Web.ActivityPub.Builder do
@moduledoc """
This module builds the objects. Meant to be used for creating local objects.

This module encodes our addressing policies and general shape of our objects.
"""

alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility

@spec like(User.t(), Object.t()) :: {:ok, map(), keyword()}
def like(actor, object) do
object_actor = User.get_cached_by_ap_id(object.data["actor"])

# Address the actor of the object, and our actor's follower collection if the post is public.
to =
if Visibility.is_public?(object) do
[actor.follower_address, object.data["actor"]]
else
[object.data["actor"]]
end

# CC everyone who's been addressed in the object, except ourself and the object actor's
# follower collection
cc =
(object.data["to"] ++ (object.data["cc"] || []))
|> List.delete(actor.ap_id)
|> List.delete(object_actor.follower_address)

{:ok,
%{
"id" => Utils.generate_activity_id(),
"actor" => actor.ap_id,
"type" => "Like",
"object" => object.data["id"],
"to" => to,
"cc" => cc,
"context" => object.data["context"]
}, []}
end
end

+ 37
- 0
lib/pleroma/web/activity_pub/object_validator.ex View File

@@ -0,0 +1,37 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.ObjectValidator do
@moduledoc """
This module is responsible for validating an object (which can be an activity)
and checking if it is both well formed and also compatible with our view of
the system.
"""

alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator

@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
def validate(object, meta)

def validate(%{"type" => "Like"} = object, meta) do
with {:ok, object} <-
object |> LikeValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do
object = stringify_keys(object |> Map.from_struct())
{:ok, object, meta}
end
end

def stringify_keys(object) do
object
|> Map.new(fn {key, val} -> {to_string(key), val} end)
end

def fetch_actor_and_object(object) do
User.get_or_fetch_by_ap_id(object["actor"])
Object.normalize(object["object"])
:ok
end
end

+ 32
- 0
lib/pleroma/web/activity_pub/object_validators/common_validations.ex View File

@@ -0,0 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
import Ecto.Changeset

alias Pleroma.Object
alias Pleroma.User

def validate_actor_presence(cng, field_name \\ :actor) do
cng
|> validate_change(field_name, fn field_name, actor ->
if User.get_cached_by_ap_id(actor) do
[]
else
[{field_name, "can't find user"}]
end
end)
end

def validate_object_presence(cng, field_name \\ :object) do
cng
|> validate_change(field_name, fn field_name, object ->
if Object.get_cached_by_ap_id(object) do
[]
else
[{field_name, "can't find object"}]
end
end)
end
end

+ 30
- 0
lib/pleroma/web/activity_pub/object_validators/create_validator.ex View File

@@ -0,0 +1,30 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do
use Ecto.Schema

alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.Types

import Ecto.Changeset

@primary_key false

embedded_schema do
field(:id, Types.ObjectID, primary_key: true)
field(:actor, Types.ObjectID)
field(:type, :string)
field(:to, {:array, :string})
field(:cc, {:array, :string})
field(:bto, {:array, :string}, default: [])
field(:bcc, {:array, :string}, default: [])

embeds_one(:object, NoteValidator)
end

def cast_data(data) do
cast(%__MODULE__{}, data, __schema__(:fields))
end
end

+ 57
- 0
lib/pleroma/web/activity_pub/object_validators/like_validator.ex View File

@@ -0,0 +1,57 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
use Ecto.Schema

alias Pleroma.Web.ActivityPub.ObjectValidators.Types
alias Pleroma.Web.ActivityPub.Utils

import Ecto.Changeset
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations

@primary_key false

embedded_schema do
field(:id, Types.ObjectID, primary_key: true)
field(:type, :string)
field(:object, Types.ObjectID)
field(:actor, Types.ObjectID)
field(:context, :string)
field(:to, {:array, :string})
field(:cc, {:array, :string})
end

def cast_and_validate(data) do
data
|> cast_data()
|> validate_data()
end

def cast_data(data) do
%__MODULE__{}
|> cast(data, [:id, :type, :object, :actor, :context, :to, :cc])
end

def validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Like"])
|> validate_required([:id, :type, :object, :actor, :context, :to, :cc])
|> validate_actor_presence()
|> validate_object_presence()
|> validate_existing_like()
end

def validate_existing_like(%{changes: %{actor: actor, object: object}} = cng) do
if Utils.get_existing_like(actor, %{data: %{"id" => object}}) do
cng
|> add_error(:actor, "already liked this object")
|> add_error(:object, "already liked by this actor")
else
cng
end
end

def validate_existing_like(cng), do: cng
end

+ 63
- 0
lib/pleroma/web/activity_pub/object_validators/note_validator.ex View File

@@ -0,0 +1,63 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
use Ecto.Schema

alias Pleroma.Web.ActivityPub.ObjectValidators.Types

import Ecto.Changeset

@primary_key false

embedded_schema do
field(:id, Types.ObjectID, primary_key: true)
field(:to, {:array, :string}, default: [])
field(:cc, {:array, :string}, default: [])
field(:bto, {:array, :string}, default: [])
field(:bcc, {:array, :string}, default: [])
# TODO: Write type
field(:tag, {:array, :map}, default: [])
field(:type, :string)
field(:content, :string)
field(:context, :string)
field(:actor, Types.ObjectID)
field(:attributedTo, Types.ObjectID)
field(:summary, :string)
field(:published, Types.DateTime)
# TODO: Write type
field(:emoji, :map, default: %{})
field(:sensitive, :boolean, default: false)
# TODO: Write type
field(:attachment, {:array, :map}, default: [])
field(:replies_count, :integer, default: 0)
field(:like_count, :integer, default: 0)
field(:announcement_count, :integer, default: 0)
field(:inRepyTo, :string)

field(:likes, {:array, :string}, default: [])
field(:announcements, {:array, :string}, default: [])

# see if needed
field(:conversation, :string)
field(:context_id, :string)
end

def cast_and_validate(data) do
data
|> cast_data()
|> validate_data()
end

def cast_data(data) do
%__MODULE__{}
|> cast(data, __schema__(:fields))
end

def validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Note"])
|> validate_required([:id, :actor, :to, :cc, :type, :content, :context])
end
end

+ 34
- 0
lib/pleroma/web/activity_pub/object_validators/types/date_time.ex View File

@@ -0,0 +1,34 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTime do
@moduledoc """
The AP standard defines the date fields in AP as xsd:DateTime. Elixir's
DateTime can't parse this, but it can parse the related iso8601. This
module punches the date until it looks like iso8601 and normalizes to
it.

DateTimes without a timezone offset are treated as UTC.

Reference: https://www.w3.org/TR/activitystreams-vocabulary/#dfn-published
"""
use Ecto.Type

def type, do: :string

def cast(datetime) when is_binary(datetime) do
with {:ok, datetime, _} <- DateTime.from_iso8601(datetime) do
{:ok, DateTime.to_iso8601(datetime)}
else
{:error, :missing_offset} -> cast("#{datetime}Z")
_e -> :error
end
end

def cast(_), do: :error

def dump(data) do
{:ok, data}
end

def load(data) do
{:ok, data}
end
end

+ 29
- 0
lib/pleroma/web/activity_pub/object_validators/types/object_id.ex View File

@@ -0,0 +1,29 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID do
use Ecto.Type

def type, do: :string

def cast(object) when is_binary(object) do
# Host has to be present and scheme has to be an http scheme (for now)
case URI.parse(object) do
%URI{host: nil} -> :error
%URI{host: ""} -> :error
%URI{scheme: scheme} when scheme in ["https", "http"] -> {:ok, object}
_ -> :error
end
end

def cast(%{"id" => object}), do: cast(object)

def cast(_) do
:error
end

def dump(data) do
{:ok, data}
end

def load(data) do
{:ok, data}
end
end

+ 42
- 0
lib/pleroma/web/activity_pub/pipeline.ex View File

@@ -0,0 +1,42 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.Pipeline do
alias Pleroma.Activity
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.SideEffects
alias Pleroma.Web.Federator

@spec common_pipeline(map(), keyword()) :: {:ok, Activity.t(), keyword()} | {:error, any()}
def common_pipeline(object, meta) do
with {_, {:ok, validated_object, meta}} <-
{:validate_object, ObjectValidator.validate(object, meta)},
{_, {:ok, mrfd_object}} <- {:mrf_object, MRF.filter(validated_object)},
{_, {:ok, %Activity{} = activity, meta}} <-
{:persist_object, ActivityPub.persist(mrfd_object, meta)},
{_, {:ok, %Activity{} = activity, meta}} <-
{:execute_side_effects, SideEffects.handle(activity, meta)},
{_, {:ok, _}} <- {:federation, maybe_federate(activity, meta)} do
{:ok, activity, meta}
else
{:mrf_object, {:reject, _}} -> {:ok, nil, meta}
e -> {:error, e}
end
end

defp maybe_federate(activity, meta) do
with {:ok, local} <- Keyword.fetch(meta, :local) do
if local do
Federator.publish(activity)
{:ok, :federated}
else
{:ok, :not_federated}
end
else
_e -> {:error, :badarg}
end
end
end

+ 28
- 0
lib/pleroma/web/activity_pub/side_effects.ex View File

@@ -0,0 +1,28 @@
defmodule Pleroma.Web.ActivityPub.SideEffects do
@moduledoc """
This module looks at an inserted object and executes the side effects that it
implies. For example, a `Like` activity will increase the like count on the
liked object, a `Follow` activity will add the user to the follower
collection, and so on.
"""
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Utils

def handle(object, meta \\ [])

# Tasks this handles:
# - Add like to object
# - Set up notification
def handle(%{data: %{"type" => "Like"}} = object, meta) do
liked_object = Object.get_by_ap_id(object.data["object"])
Utils.add_like_to_object(object, liked_object)
Notification.create_notifications(object)
{:ok, object, meta}
end

# Nothing to do
def handle(object, meta) do
{:ok, object, meta}
end
end

+ 56
- 9
lib/pleroma/web/activity_pub/transmogrifier.ex View File

@@ -13,6 +13,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator
@@ -609,17 +612,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> handle_incoming(options)
end

def handle_incoming(
%{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data,
_options
) do
with actor <- Containment.get_actor(data),
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- get_obj_helper(object_id),
{:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do
def handle_incoming(%{"type" => "Like"} = data, _options) do
with {_, {:ok, cast_data_sym}} <-
{:casting_data,
data |> LikeValidator.cast_data() |> Ecto.Changeset.apply_action(:insert)},
cast_data = ObjectValidator.stringify_keys(Map.from_struct(cast_data_sym)),
:ok <- ObjectValidator.fetch_actor_and_object(cast_data),
{_, {:ok, cast_data}} <- {:ensure_context_presence, ensure_context_presence(cast_data)},
{_, {:ok, cast_data}} <-
{:ensure_recipients_presence, ensure_recipients_presence(cast_data)},
{_, {:ok, activity, _meta}} <-
{:common_pipeline, Pipeline.common_pipeline(cast_data, local: false)} do
{:ok, activity}
else
_e -> :error
e -> {:error, e}
end
end

@@ -1243,4 +1249,45 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def maybe_fix_user_url(data), do: data

def maybe_fix_user_object(data), do: maybe_fix_user_url(data)

defp ensure_context_presence(%{"context" => context} = data) when is_binary(context),
do: {:ok, data}

defp ensure_context_presence(%{"object" => object} = data) when is_binary(object) do
with %{data: %{"context" => context}} when is_binary(context) <- Object.normalize(object) do
{:ok, Map.put(data, "context", context)}
else
_ ->
{:error, :no_context}
end
end

defp ensure_context_presence(_) do
{:error, :no_context}
end

defp ensure_recipients_presence(%{"to" => [_ | _], "cc" => [_ | _]} = data),
do: {:ok, data}

defp ensure_recipients_presence(%{"object" => object} = data) do
case Object.normalize(object) do
%{data: %{"actor" => actor}} ->
data =
data
|> Map.put("to", [actor])
|> Map.put("cc", data["cc"] || [])

{:ok, data}

nil ->
{:error, :no_object}

_ ->
{:error, :no_actor}
end
end

defp ensure_recipients_presence(_) do
{:error, :no_object}
end
end

+ 14
- 3
lib/pleroma/web/admin_api/admin_api_controller.ex View File

@@ -576,9 +576,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do

@doc "Sends registration invite via email"
def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
with true <-
Config.get([:instance, :invites_enabled]) &&
!Config.get([:instance, :registrations_open]),
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
{:ok, invite_token} <- UserInviteToken.create_invite(),
email <-
Pleroma.Emails.UserEmail.user_invitation_email(
@@ -589,6 +588,18 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
),
{:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
json_response(conn, :no_content, "")
else
{:registrations_open, _} ->
errors(
conn,
{:error, "To send invites you need to set the `registrations_open` option to false."}
)

{:invites_enabled, _} ->
errors(
conn,
{:error, "To send invites you need to set the `invites_enabled` option to true."}
)
end
end



+ 44
- 0
lib/pleroma/web/api_spec.ex View File

@@ -0,0 +1,44 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ApiSpec do
alias OpenApiSpex.OpenApi
alias Pleroma.Web.Endpoint
alias Pleroma.Web.Router

@behaviour OpenApi

@impl OpenApi
def spec do
%OpenApi{
servers: [
# Populate the Server info from a phoenix endpoint
OpenApiSpex.Server.from_endpoint(Endpoint)
],
info: %OpenApiSpex.Info{
title: "Pleroma",
description: Application.spec(:pleroma, :description) |> to_string(),
version: Application.spec(:pleroma, :vsn) |> to_string()
},
# populate the paths from a phoenix router
paths: OpenApiSpex.Paths.from_router(Router),
components: %OpenApiSpex.Components{
securitySchemes: %{
"oAuth" => %OpenApiSpex.SecurityScheme{
type: "oauth2",
flows: %OpenApiSpex.OAuthFlows{
password: %OpenApiSpex.OAuthFlow{
authorizationUrl: "/oauth/authorize",
tokenUrl: "/oauth/token",
scopes: %{"read" => "read"}
}
}
}
}
}
}
# discover request/response schemas from path specs
|> OpenApiSpex.resolve_schema_modules()
end
end

+ 27
- 0
lib/pleroma/web/api_spec/helpers.ex View File

@@ -0,0 +1,27 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ApiSpec.Helpers do
def request_body(description, schema_ref, opts \\ []) do
media_types = ["application/json", "multipart/form-data"]

content =
media_types
|> Enum.map(fn type ->
{type,
%OpenApiSpex.MediaType{
schema: schema_ref,
example: opts[:example],
examples: opts[:examples]
}}
end)
|> Enum.into(%{})

%OpenApiSpex.RequestBody{
description: description,
content: content,
required: opts[:required] || false
}
end
end

+ 96
- 0
lib/pleroma/web/api_spec/operations/app_operation.ex View File

@@ -0,0 +1,96 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ApiSpec.AppOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Helpers
alias Pleroma.Web.ApiSpec.Schemas.AppCreateRequest
alias Pleroma.Web.ApiSpec.Schemas.AppCreateResponse

@spec open_api_operation(atom) :: Operation.t()
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end

@spec create_operation() :: Operation.t()
def create_operation do
%Operation{
tags: ["apps"],
summary: "Create an application",
description: "Create a new application to obtain OAuth2 credentials",
operationId: "AppController.create",
requestBody: Helpers.request_body("Parameters", AppCreateRequest, required: true),
responses: %{
200 => Operation.response("App", "application/json", AppCreateResponse),
422 =>
Operation.response(
"Unprocessable Entity",
"application/json",
%Schema{
type: :object,
description:
"If a required parameter is missing or improperly formatted, the request will fail.",
properties: %{
error: %Schema{type: :string}
},
example: %{
"error" => "Validation failed: Redirect URI must be an absolute URI."
}
}
)
}
}
end

def verify_credentials_operation do
%Operation{
tags: ["apps"],
summary: "Verify your app works",
description: "Confirm that the app's OAuth2 credentials work.",
operationId: "AppController.verify_credentials",
security: [
%{
"oAuth" => ["read"]
}
],
responses: %{
200 =>
Operation.response("App", "application/json", %Schema{
type: :object,
description:
"If the Authorization header was provided with a valid token, you should see your app returned as an Application entity.",
properties: %{
name: %Schema{type: :string},
vapid_key: %Schema{type: :string},
website: %Schema{type: :string, nullable: true}
},
example: %{
"name" => "My App",
"vapid_key" =>
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
"website" => "https://myapp.com/"
}
}),
422 =>
Operation.response(
"Unauthorized",
"application/json",
%Schema{
type: :object,
description:
"If the Authorization header contains an invalid token, is malformed, or is not present, an error will be returned indicating an authorization failure.",
properties: %{
error: %Schema{type: :string}
},
example: %{
"error" => "The access token is invalid."
}
}
)
}
}
end
end

+ 33
- 0
lib/pleroma/web/api_spec/schemas/app_create_request.ex View File

@@ -0,0 +1,33 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ApiSpec.Schemas.AppCreateRequest do
alias OpenApiSpex.Schema
require OpenApiSpex

OpenApiSpex.schema(%{
title: "AppCreateRequest",
description: "POST body for creating an app",
type: :object,
properties: %{
client_name: %Schema{type: :string, description: "A name for your application."},
redirect_uris: %Schema{
type: :string,
description:
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
},
scopes: %Schema{
type: :string,
description: "Space separated list of scopes. If none is provided, defaults to `read`."
},
website: %Schema{type: :string, description: "A URL to the homepage of your app"}
},
required: [:client_name, :redirect_uris],
example: %{
"client_name" => "My App",
"redirect_uris" => "https://myapp.com/auth/callback",
"website" => "https://myapp.com/"
}
})
end

+ 33
- 0
lib/pleroma/web/api_spec/schemas/app_create_response.ex View File

@@ -0,0 +1,33 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ApiSpec.Schemas.AppCreateResponse do
alias OpenApiSpex.Schema

require OpenApiSpex

OpenApiSpex.schema(%{
title: "AppCreateResponse",
description: "Response schema for an app",
type: :object,
properties: %{
id: %Schema{type: :string},
name: %Schema{type: :string},
client_id: %Schema{type: :string},
client_secret: %Schema{type: :string},
redirect_uri: %Schema{type: :string},
vapid_key: %Schema{type: :string},
website: %Schema{type: :string, nullable: true}
},
example: %{
"id" => "123",
"name" => "My App",
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
"vapid_key" =>
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
"website" => "https://myapp.com/"
}
})
end

+ 47
- 11
lib/pleroma/web/common_api/common_api.ex View File

@@ -12,6 +12,8 @@ defmodule Pleroma.Web.CommonAPI do
alias Pleroma.User
alias Pleroma.UserRelationship
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility

@@ -19,6 +21,7 @@ defmodule Pleroma.Web.CommonAPI do
import Pleroma.Web.CommonAPI.Utils

require Pleroma.Constants
require Logger

def follow(follower, followed) do
timeout = Pleroma.Config.get([:activitypub, :follow_handshake_timeout])
@@ -109,18 +112,51 @@ defmodule Pleroma.Web.CommonAPI do
end
end

def favorite(id_or_ap_id, user) do
with {_, %Activity{} = activity} <- {:find_activity, get_by_id_or_ap_id(id_or_ap_id)},
object <- Object.normalize(activity),
like_activity <- Utils.get_existing_like(user.ap_id, object) do
if like_activity do
{:ok, like_activity, object}
else
ActivityPub.like(user, object)
end
@spec favorite(User.t(), binary()) :: {:ok, Activity.t() | :already_liked} | {:error, any()}
def favorite(%User{} = user, id) do
case favorite_helper(user, id) do
{:ok, _} = res ->
res

{:error, :not_found} = res ->
res

{:error, e} ->
Logger.error("Could not favorite #{id}. Error: #{inspect(e, pretty: true)}")
{:error, dgettext("errors", "Could not favorite")}
end
end

def favorite_helper(user, id) do
with {_, %Activity{object: object}} <- {:find_object, Activity.get_by_id_with_object(id)},
{_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
{_, {:ok, %Activity{} = activity, _meta}} <-
{:common_pipeline,
Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do
{:ok, activity}
else
{:find_activity, _} -> {:error, :not_found}
_ -> {:error, dgettext("errors", "Could not favorite")}
{:find_object, _} ->
{:error, :not_found}

{:common_pipeline,
{
:error,
{
:validate_object,
{
:error,
changeset
}
}
}} = e ->
if {:object, {"already liked by this actor", []}} in changeset.errors do
{:ok, :already_liked}
else
{:error, e}
end

e ->
{:error, e}
end
end



+ 6
- 3
lib/pleroma/web/mastodon_api/controllers/app_controller.ex View File

@@ -14,17 +14,20 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)

plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials)
plug(OpenApiSpex.Plug.CastAndValidate)

@local_mastodon_name "Mastodon-Local"

defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation

@doc "POST /api/v1/apps"
def create(conn, params) do
def create(%{body_params: params} = conn, _params) do
scopes = Scopes.fetch_scopes(params, ["read"])

app_attrs =
params
|> Map.drop(["scope", "scopes"])
|> Map.put("scopes", scopes)
|> Map.take([:client_name, :redirect_uris, :website])
|> Map.put(:scopes, scopes)

with cs <- App.register_changeset(%App{}, app_attrs),
false <- cs.changes[:client_name] == @local_mastodon_name,


+ 2
- 1
lib/pleroma/web/mastodon_api/controllers/notification_controller.ex View File

@@ -66,7 +66,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
json(conn, %{})
end

# POST /api/v1/notifications/dismiss
# POST /api/v1/notifications/:id/dismiss
# POST /api/v1/notifications/dismiss (deprecated)
def dismiss(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
with {:ok, _notif} <- Notification.dismiss(user, id) do
json(conn, %{})


+ 3
- 3
lib/pleroma/web/mastodon_api/controllers/status_controller.ex View File

@@ -207,9 +207,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end

@doc "POST /api/v1/statuses/:id/favourite"
def favourite(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
def favourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do
with {:ok, _fav} <- CommonAPI.favorite(user, activity_id),
%Activity{} = activity <- Activity.get_by_id(activity_id) do
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
end
end


+ 5
- 3
lib/pleroma/web/mastodon_api/views/account_view.ex View File

@@ -13,16 +13,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
alias Pleroma.Web.MediaProxy

def render("index.json", %{users: users} = opts) do
reading_user = opts[:for]

relationships_opt =
cond do
Map.has_key?(opts, :relationships) ->
opts[:relationships]

is_nil(opts[:for]) ->
is_nil(reading_user) ->
UserRelationship.view_relationships_option(nil, [])

true ->
UserRelationship.view_relationships_option(opts[:for], users)
UserRelationship.view_relationships_option(reading_user, users)
end

opts = Map.put(opts, :relationships, relationships_opt)
@@ -143,7 +145,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
Map.has_key?(opts, :relationships) ->
opts[:relationships]

is_nil(opts[:for]) ->
is_nil(user) ->
UserRelationship.view_relationships_option(nil, [])

true ->


+ 1
- 1
lib/pleroma/web/mastodon_api/views/notification_view.ex View File

@@ -36,7 +36,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
Map.has_key?(opts, :relationships) ->
opts[:relationships]

is_nil(opts[:for]) ->
is_nil(reading_user) ->
UserRelationship.view_relationships_option(nil, [])

true ->


+ 6
- 4
lib/pleroma/web/mastodon_api/views/status_view.ex View File

@@ -72,6 +72,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end

def render("index.json", opts) do
reading_user = opts[:for]

# To do: check AdminAPIControllerTest on the reasons behind nil activities in the list
activities = Enum.filter(opts.activities, & &1)
replied_to_activities = get_replied_to_activities(activities)
@@ -82,8 +84,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|> Enum.map(&Object.normalize(&1).data["id"])
|> Activity.create_by_object_ap_id()
|> Activity.with_preloaded_object(:left)
|> Activity.with_preloaded_bookmark(opts[:for])
|> Activity.with_set_thread_muted_field(opts[:for])
|> Activity.with_preloaded_bookmark(reading_user)
|> Activity.with_set_thread_muted_field(reading_user)
|> Repo.all()

relationships_opt =
@@ -91,13 +93,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
Map.has_key?(opts, :relationships) ->
opts[:relationships]

is_nil(opts[:for]) ->
is_nil(reading_user) ->
UserRelationship.view_relationships_option(nil, [])

true ->
actors = Enum.map(activities ++ parent_activities, &get_user(&1.data["actor"]))

UserRelationship.view_relationships_option(opts[:for], actors)
UserRelationship.view_relationships_option(reading_user, actors)
end

opts =


+ 2
- 1
lib/pleroma/web/nodeinfo/nodeinfo_controller.ex View File

@@ -75,7 +75,8 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
end,
if Config.get([:instance, :safe_dm_mentions]) do
"safe_dm_mentions"
end
end,
"pleroma_emoji_reactions"
]
|> Enum.filter(& &1)



+ 6
- 1
lib/pleroma/web/oauth/scopes.ex View File

@@ -15,7 +15,12 @@ defmodule Pleroma.Web.OAuth.Scopes do
Note: `scopes` is used by Mastodon — supporting it but sticking to
OAuth's standard `scope` wherever we control it
"""
@spec fetch_scopes(map(), list()) :: list()
@spec fetch_scopes(map() | struct(), list()) :: list()

def fetch_scopes(%Pleroma.Web.ApiSpec.Schemas.AppCreateRequest{scopes: scopes}, default) do
parse_scopes(scopes, default)
end

def fetch_scopes(params, default) do
parse_scopes(params["scope"] || params["scopes"], default)
end


+ 14
- 1
lib/pleroma/web/router.ex View File

@@ -29,6 +29,7 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Plugs.SetUserSessionIdPlug)
plug(Pleroma.Plugs.EnsureUserKeyPlug)
plug(Pleroma.Plugs.IdempotencyPlug)
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end

pipeline :authenticated_api do
@@ -44,6 +45,7 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Plugs.SetUserSessionIdPlug)
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
plug(Pleroma.Plugs.IdempotencyPlug)
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end

pipeline :admin_api do
@@ -61,6 +63,7 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
plug(Pleroma.Plugs.UserIsAdminPlug)
plug(Pleroma.Plugs.IdempotencyPlug)
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end

pipeline :mastodon_html do
@@ -94,10 +97,12 @@ defmodule Pleroma.Web.Router do

pipeline :config do
plug(:accepts, ["json", "xml"])
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end

pipeline :pleroma_api do
plug(:accepts, ["html", "json"])
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
end

pipeline :mailbox_preview do
@@ -347,9 +352,11 @@ defmodule Pleroma.Web.Router do

get("/notifications", NotificationController, :index)
get("/notifications/:id", NotificationController, :show)
post("/notifications/:id/dismiss", NotificationController, :dismiss)
post("/notifications/clear", NotificationController, :clear)
post("/notifications/dismiss", NotificationController, :dismiss)
delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple)
# Deprecated: was removed in Mastodon v3, use `/notifications/:id/dismiss` instead
post("/notifications/dismiss", NotificationController, :dismiss)

get("/scheduled_statuses", ScheduledActivityController, :index)
get("/scheduled_statuses/:id", ScheduledActivityController, :show)
@@ -500,6 +507,12 @@ defmodule Pleroma.Web.Router do
)
end

scope "/api" do
pipe_through(:api)

get("/openapi", OpenApiSpex.Plug.RenderSpec, [])
end

scope "/api", Pleroma.Web, as: :authenticated_twitter_api do
pipe_through(:authenticated_api)



+ 12
- 2
mix.exs View File

@@ -37,12 +37,21 @@ defmodule Pleroma.Mixfile do
pleroma: [
include_executables_for: [:unix],
applications: [ex_syslogger: :load, syslog: :load],
steps: [:assemble, &copy_files/1, &copy_nginx_config/1]
steps: [:assemble, &put_otp_version/1, &copy_files/1, &copy_nginx_config/1]
]
]
]
end

def put_otp_version(%{path: target_path} = release) do
File.write!(
Path.join([target_path, "OTP_VERSION"]),
Pleroma.OTPVersion.version()
)

release
end

def copy_files(%{path: target_path} = release) do
File.cp_r!("./rel/files", target_path)
release
@@ -179,7 +188,8 @@ defmodule Pleroma.Mixfile do
git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
{:mox, "~> 0.5", only: :test},
{:restarter, path: "./restarter"}
{:restarter, path: "./restarter"},
{:open_api_spex, "~> 3.6"}
] ++ oauth_deps()
end



+ 1
- 0
mix.lock View File

@@ -74,6 +74,7 @@
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
"oban": {:hex, :oban, "1.2.0", "7cca94d341be43d220571e28f69131c4afc21095b25257397f50973d3fc59b07", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba5f8b3f7d76967b3e23cf8014f6a13e4ccb33431e4808f036709a7f822362ee"},
"open_api_spex": {:hex, :open_api_spex, "3.6.0", "64205aba9f2607f71b08fd43e3351b9c5e9898ec5ef49fc0ae35890da502ade9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "126ba3473966277132079cb1d5bf1e3df9e36fe2acd00166e75fd125cecb59c5"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm", "595d09db74cb093b1903381c9de423276a931a2480a46a1a5dc7f932a2a6375b"},
"phoenix": {:hex, :phoenix, "1.4.13", "67271ad69b51f3719354604f4a3f968f83aa61c19199343656c9caee057ff3b8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ab765a0feddb81fc62e2116c827b5f068df85159c162bee760745276ad7ddc1b"},


BIN
test/fixtures/emoji/packs/blank.png.zip View File


+ 10
- 0
test/fixtures/emoji/packs/default-manifest.json View File

@@ -0,0 +1,10 @@
{
"finmoji": {
"license": "CC BY-NC-ND 4.0",
"homepage": "https://finland.fi/emoji/",
"description": "Finland is the first country in the world to publish its own set of country themed emojis. The Finland emoji collection contains 56 tongue-in-cheek emotions, which were created to explain some hard-to-describe Finnish emotions, Finnish words and customs.",
"src": "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip",
"src_sha256": "384025A1AC6314473863A11AC7AB38A12C01B851A3F82359B89B4D4211D3291D",
"files": "finmoji.json"
}
}

+ 3
- 0
test/fixtures/emoji/packs/finmoji.json View File

@@ -0,0 +1,3 @@
{
"blank": "blank.png"
}

+ 10
- 0
test/fixtures/emoji/packs/manifest.json View File

@@ -0,0 +1,10 @@
{
"blobs.gg": {
"src_sha256": "3a12f3a181678d5b3584a62095411b0d60a335118135910d879920f8ade5a57f",
"src": "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip",
"license": "Apache 2.0",
"homepage": "https://blobs.gg",
"files": "blobs_gg.json",
"description": "Blob Emoji from blobs.gg repacked as apng"
}
}

+ 10
- 14
test/formatter_test.exs View File

@@ -150,13 +150,13 @@ defmodule Pleroma.FormatterTest do
assert length(mentions) == 3

expected_text =
~s(<span class="h-card"><a data-user="#{gsimg.id}" class="u-url mention" href="#{
~s(<span class="h-card"><a class="u-url mention" data-user="#{gsimg.id}" href="#{
gsimg.ap_id
}" rel="ugc">@<span>gsimg</span></a></span> According to <span class="h-card"><a data-user="#{
}" rel="ugc">@<span>gsimg</span></a></span> According to <span class="h-card"><a class="u-url mention" data-user="#{
archaeme.id
}" class="u-url mention" href="#{"https://archeme/@archa_eme_"}" rel="ugc">@<span>archa_eme_</span></a></span>, that is @daggsy. Also hello <span class="h-card"><a data-user="#{
}" href="#{"https://archeme/@archa_eme_"}" rel="ugc">@<span>archa_eme_</span></a></span>, that is @daggsy. Also hello <span class="h-card"><a class="u-url mention" data-user="#{
archaeme_remote.id
}" class="u-url mention" href="#{archaeme_remote.ap_id}" rel="ugc">@<span>archaeme</span></a></span>)
}" href="#{archaeme_remote.ap_id}" rel="ugc">@<span>archaeme</span></a></span>)

assert expected_text == text
end
@@ -171,7 +171,7 @@ defmodule Pleroma.FormatterTest do
assert length(mentions) == 1

expected_text =
~s(<span class="h-card"><a data-user="#{mike.id}" class="u-url mention" href="#{
~s(<span class="h-card"><a class="u-url mention" data-user="#{mike.id}" href="#{
mike.ap_id
}" rel="ugc">@<span>mike</span></a></span> test)

@@ -187,7 +187,7 @@ defmodule Pleroma.FormatterTest do
assert length(mentions) == 1

expected_text =
~s(<span class="h-card"><a data-user="#{o.id}" class="u-url mention" href="#{o.ap_id}" rel="ugc">@<span>o</span></a></span> hi)
~s(<span class="h-card"><a class="u-url mention" data-user="#{o.id}" href="#{o.ap_id}" rel="ugc">@<span>o</span></a></span> hi)

assert expected_text == text
end
@@ -209,17 +209,13 @@ defmodule Pleroma.FormatterTest do
assert mentions == [{"@#{user.nickname}", user}, {"@#{other_user.nickname}", other_user}]

assert expected_text ==
~s(<span class="h-card"><a data-user="#{user.id}" class="u-url mention" href="#{
~s(<span class="h-card"><a class="u-url mention" data-user="#{user.id}" href="#{
user.ap_id
}" rel="ugc">@<span>#{user.nickname}</span></a></span> <span class="h-card"><a data-user="#{
}" rel="ugc">@<span>#{user.nickname}</span></a></span> <span class="h-card"><a class="u-url mention" data-user="#{
other_user.id
}" class="u-url mention" href="#{other_user.ap_id}" rel="ugc">@<span>#{
other_user.nickname
}</span></a></span> hey dudes i hate <span class="h-card"><a data-user="#{
}" href="#{other_user.ap_id}" rel="ugc">@<span>#{other_user.nickname}</span></a></span> hey dudes i hate <span class="h-card"><a class="u-url mention" data-user="#{
third_user.id
}" class="u-url mention" href="#{third_user.ap_id}" rel="ugc">@<span>#{
third_user.nickname
}</span></a></span>)
}" href="#{third_user.ap_id}" rel="ugc">@<span>#{third_user.nickname}</span></a></span>)
end

test "given the 'safe_mention' option, it will still work without any mention" do


+ 4
- 4
test/notification_test.exs View File

@@ -537,7 +537,7 @@ defmodule Pleroma.NotificationTest do
"status" => "hey @#{other_user.nickname}!"
})

{:ok, activity_two, _} = CommonAPI.favorite(activity_one.id, third_user)
{:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)

{enabled_receivers, _disabled_receivers} =
Notification.get_notified_from_activity(activity_two)
@@ -620,7 +620,7 @@ defmodule Pleroma.NotificationTest do

assert Enum.empty?(Notification.for_user(user))

{:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)

assert length(Notification.for_user(user)) == 1

@@ -637,7 +637,7 @@ defmodule Pleroma.NotificationTest do

assert Enum.empty?(Notification.for_user(user))

{:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)

assert length(Notification.for_user(user)) == 1

@@ -692,7 +692,7 @@ defmodule Pleroma.NotificationTest do

assert Enum.empty?(Notification.for_user(user))

{:error, _} = CommonAPI.favorite(activity.id, other_user)
{:error, :not_found} = CommonAPI.favorite(other_user, activity.id)

assert Enum.empty?(Notification.for_user(user))
end


+ 2
- 1
test/object_test.exs View File

@@ -380,7 +380,8 @@ defmodule Pleroma.ObjectTest do

user = insert(:user)
activity = Activity.get_create_by_object_ap_id(object.data["id"])
{:ok, _activity, object} = CommonAPI.favorite(activity.id, user)
{:ok, activity} = CommonAPI.favorite(user, activity.id)
object = Object.get_by_ap_id(activity.data["object"])

assert object.data["like_count"] == 1



+ 1
- 1
test/stat_test.exs View File

@@ -60,7 +60,7 @@ defmodule Pleroma.StateTest do
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
_ = CommonAPI.follow(user, other_user)
CommonAPI.favorite(activity.id, other_user)
CommonAPI.favorite(other_user, activity.id)
CommonAPI.repeat(activity.id, other_user)

assert %{direct: 0, private: 0, public: 1, unlisted: 0} =


+ 1
- 1
test/tasks/database_test.exs View File

@@ -102,7 +102,7 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do
{:ok, %{id: id, object: object}} = CommonAPI.post(user, %{"status" => "test"})
{:ok, %{object: object2}} = CommonAPI.post(user, %{"status" => "test test"})

CommonAPI.favorite(id, user2)
CommonAPI.favorite(user2, id)

likes = %{
"first" =>


+ 226
- 0
test/tasks/emoji_test.exs View File

@@ -0,0 +1,226 @@
defmodule Mix.Tasks.Pleroma.EmojiTest do
use ExUnit.Case, async: true

import ExUnit.CaptureIO
import Tesla.Mock

alias Mix.Tasks.Pleroma.Emoji

describe "ls-packs" do
test "with default manifest as url" do
mock(fn
%{
method: :get,
url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/emoji/packs/default-manifest.json")
}
end)

capture_io(fn -> Emoji.run(["ls-packs"]) end) =~
"https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip"
end

test "with passed manifest as file" do
capture_io(fn ->
Emoji.run(["ls-packs", "-m", "test/fixtures/emoji/packs/manifest.json"])
end) =~ "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip"
end
end

describe "get-packs" do
test "download pack from default manifest" do
mock(fn
%{
method: :get,
url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/emoji/packs/default-manifest.json")
}

%{
method: :get,
url: "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/emoji/packs/blank.png.zip")
}

%{
method: :get,
url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/finmoji.json"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/emoji/packs/finmoji.json")
}
end)

assert capture_io(fn -> Emoji.run(["get-packs", "finmoji"]) end) =~ "Writing pack.json for"

emoji_path =
Path.join(
Pleroma.Config.get!([:instance, :static_dir]),
"emoji"
)

assert File.exists?(Path.join([emoji_path, "finmoji", "pack.json"]))
on_exit(fn -> File.rm_rf!("test/instance_static/emoji/finmoji") end)
end

test "pack not found" do
mock(fn
%{
method: :get,
url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/emoji/packs/default-manifest.json")
}
end)

assert capture_io(fn -> Emoji.run(["get-packs", "not_found"]) end) =~
"No pack named \"not_found\" found"
end

test "raise on bad sha256" do
mock(fn
%{
method: :get,
url: "https://git.pleroma.social/pleroma/emoji-index/raw/master/packs/blobs_gg.zip"
} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/emoji/packs/blank.png.zip")
}
end)

assert_raise RuntimeError, ~r/^Bad SHA256 for blobs.gg/, fn ->
capture_io(fn ->
Emoji.run(["get-packs", "blobs.gg", "-m", "test/fixtures/emoji/packs/manifest.json"])
end)
end
end
end

describe "gen-pack" do
setup do
url = "https://finland.fi/wp-content/uploads/2017/06/finland-emojis.zip"

mock(fn %{
method: :get,
url: ^url
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/emoji/packs/blank.png.zip")}
end)

{:ok, url: url}
end

test "with default extensions", %{url: url} do
name = "pack1"
pack_json = "#{name}.json"
files_json = "#{name}_file.json"
refute File.exists?(pack_json)
refute File.exists?(files_json)

captured =
capture_io(fn ->
Emoji.run([
"gen-pack",
url,
"--name",
name,
"--license",
"license",
"--homepage",
"homepage",
"--description",
"description",
"--files",
files_json,
"--extensions",
".png .gif"
])
end)

assert captured =~ "#{pack_json} has been created with the pack1 pack"
assert captured =~ "Using .png .gif extensions"

assert File.exists?(pack_json)
assert File.exists?(files_json)

on_exit(fn ->
File.rm!(pack_json)
File.rm!(files_json)
end)
end

test "with custom extensions and update existing files", %{url: url} do
name = "pack2"
pack_json = "#{name}.json"
files_json = "#{name}_file.json"
refute File.exists?(pack_json)
refute File.exists?(files_json)

captured =
capture_io(fn ->
Emoji.run([
"gen-pack",
url,
"--name",
name,
"--license",
"license",
"--homepage",
"homepage",
"--description",
"description",
"--files",
files_json,
"--extensions",
" .png .gif .jpeg "
])
end)

assert captured =~ "#{pack_json} has been created with the pack2 pack"
assert captured =~ "Using .png .gif .jpeg extensions"

assert File.exists?(pack_json)
assert File.exists?(files_json)

captured =
capture_io(fn ->
Emoji.run([
"gen-pack",
url,
"--name",
name,
"--license",
"license",
"--homepage",
"homepage",
"--description",
"description",
"--files",
files_json,
"--extensions",
" .png .gif .jpeg "
])
end)

assert captured =~ "#{pack_json} has been updated with the pack2 pack"

on_exit(fn ->
File.rm!(pack_json)
File.rm!(files_json)
end)
end
end
end

+ 3
- 3
test/user_test.exs View File

@@ -1141,8 +1141,8 @@ defmodule Pleroma.UserTest do
object_two = insert(:note, user: follower)
activity_two = insert(:note_activity, user: follower, note: object_two)

{:ok, like, _} = CommonAPI.favorite(activity_two.id, user)
{:ok, like_two, _} = CommonAPI.favorite(activity.id, follower)
{:ok, like} = CommonAPI.favorite(user, activity_two.id)
{:ok, like_two} = CommonAPI.favorite(follower, activity.id)
{:ok, repeat, _} = CommonAPI.repeat(activity_two.id, user)

{:ok, job} = User.delete(user)
@@ -1404,7 +1404,7 @@ defmodule Pleroma.UserTest do
bio = "A.k.a. @nick@domain.com"

expected_text =
~s(A.k.a. <span class="h-card"><a data-user="#{remote_user.id}" class="u-url mention" href="#{
~s(A.k.a. <span class="h-card"><a class="u-url mention" data-user="#{remote_user.id}" href="#{
remote_user.ap_id
}" rel="ugc">@<span>nick@domain.com</span></a></span>)



+ 8
- 8
test/web/activity_pub/activity_pub_test.exs View File

@@ -1900,14 +1900,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, a4} = CommonAPI.post(user2, %{"status" => "Agent Smith "})
{:ok, a5} = CommonAPI.post(user1, %{"status" => "Red or Blue "})

{:ok, _, _} = CommonAPI.favorite(a4.id, user)
{:ok, _, _} = CommonAPI.favorite(a3.id, other_user)
{:ok, _, _} = CommonAPI.favorite(a3.id, user)
{:ok, _, _} = CommonAPI.favorite(a5.id, other_user)
{:ok, _, _} = CommonAPI.favorite(a5.id, user)
{:ok, _, _} = CommonAPI.favorite(a4.id, other_user)
{:ok, _, _} = CommonAPI.favorite(a1.id, user)
{:ok, _, _} = CommonAPI.favorite(a1.id, other_user)
{:ok, _} = CommonAPI.favorite(user, a4.id)
{:ok, _} = CommonAPI.favorite(other_user, a3.id)
{:ok, _} = CommonAPI.favorite(user, a3.id)
{:ok, _} = CommonAPI.favorite(other_user, a5.id)
{:ok, _} = CommonAPI.favorite(user, a5.id)
{:ok, _} = CommonAPI.favorite(other_user, a4.id)
{:ok, _} = CommonAPI.favorite(user, a1.id)
{:ok, _} = CommonAPI.favorite(other_user, a1.id)
result = ActivityPub.fetch_favourites(user)

assert Enum.map(result, & &1.id) == [a1.id, a5.id, a3.id, a4.id]


+ 83
- 0
test/web/activity_pub/object_validator_test.exs View File

@@ -0,0 +1,83 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do
use Pleroma.DataCase

alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI

import Pleroma.Factory

describe "likes" do
setup do
user = insert(:user)
{:ok, post_activity} = CommonAPI.post(user, %{"status" => "uguu"})

valid_like = %{
"to" => [user.ap_id],
"cc" => [],
"type" => "Like",
"id" => Utils.generate_activity_id(),
"object" => post_activity.data["object"],
"actor" => user.ap_id,
"context" => "a context"
}

%{valid_like: valid_like, user: user, post_activity: post_activity}
end

test "returns ok when called in the ObjectValidator", %{valid_like: valid_like} do
{:ok, object, _meta} = ObjectValidator.validate(valid_like, [])

assert "id" in Map.keys(object)
end

test "is valid for a valid object", %{valid_like: valid_like} do
assert LikeValidator.cast_and_validate(valid_like).valid?
end

test "it errors when the actor is missing or not known", %{valid_like: valid_like} do
without_actor = Map.delete(valid_like, "actor")

refute LikeValidator.cast_and_validate(without_actor).valid?

with_invalid_actor = Map.put(valid_like, "actor", "invalidactor")

refute LikeValidator.cast_and_validate(with_invalid_actor).valid?
end

test "it errors when the object is missing or not known", %{valid_like: valid_like} do
without_object = Map.delete(valid_like, "object")

refute LikeValidator.cast_and_validate(without_object).valid?

with_invalid_object = Map.put(valid_like, "object", "invalidobject")

refute LikeValidator.cast_and_validate(with_invalid_object).valid?
end

test "it errors when the actor has already like the object", %{
valid_like: valid_like,
user: user,
post_activity: post_activity
} do
_like = CommonAPI.favorite(user, post_activity.id)

refute LikeValidator.cast_and_validate(valid_like).valid?
end

test "it works when actor or object are wrapped in maps", %{valid_like: valid_like} do
wrapped_like =
valid_like
|> Map.put("actor", %{"id" => valid_like["actor"]})
|> Map.put("object", %{"id" => valid_like["object"]})

validated = LikeValidator.cast_and_validate(wrapped_like)

assert validated.valid?

assert {:actor, valid_like["actor"]} in validated.changes
assert {:object, valid_like["object"]} in validated.changes
end
end
end

+ 35
- 0
test/web/activity_pub/object_validators/note_validator_test.exs View File

@@ -0,0 +1,35 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidatorTest do
use Pleroma.DataCase

alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
alias Pleroma.Web.ActivityPub.Utils

import Pleroma.Factory

describe "Notes" do
setup do
user = insert(:user)

note = %{
"id" => Utils.generate_activity_id(),
"type" => "Note",
"actor" => user.ap_id,
"to" => [user.follower_address],
"cc" => [],
"content" => "Hellow this is content.",
"context" => "xxx",
"summary" => "a post"
}

%{user: user, note: note}
end

test "a basic note validates", %{note: note} do
%{valid?: true} = NoteValidator.cast_and_validate(note)
end
end
end

+ 32
- 0
test/web/activity_pub/object_validators/types/date_time_test.exs View File

@@ -0,0 +1,32 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTimeTest do
alias Pleroma.Web.ActivityPub.ObjectValidators.Types.DateTime
use Pleroma.DataCase

test "it validates an xsd:Datetime" do
valid_strings = [
"2004-04-12T13:20:00",
"2004-04-12T13:20:15.5",
"2004-04-12T13:20:00-05:00",
"2004-04-12T13:20:00Z"
]

invalid_strings = [
"2004-04-12T13:00",
"2004-04-1213:20:00",
"99-04-12T13:00",
"2004-04-12"
]

assert {:ok, "2004-04-01T12:00:00Z"} == DateTime.cast("2004-04-01T12:00:00Z")

Enum.each(valid_strings, fn date_time ->
result = DateTime.cast(date_time)
assert {:ok, _} = result
end)

Enum.each(invalid_strings, fn date_time ->
result = DateTime.cast(date_time)
assert :error == result
end)
end
end

+ 37
- 0
test/web/activity_pub/object_validators/types/object_id_test.exs View File

@@ -0,0 +1,37 @@
defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do
alias Pleroma.Web.ActivityPub.ObjectValidators.Types.ObjectID
use Pleroma.DataCase

@uris [
"http://lain.com/users/lain",
"http://lain.com",
"https://lain.com/object/1"
]

@non_uris [
"https://",
"rin",
1,
:x,
%{"1" => 2}
]

test "it accepts http uris" do
Enum.each(@uris, fn uri ->
assert {:ok, uri} == ObjectID.cast(uri)
end)
end

test "it accepts an object with a nested uri id" do
Enum.each(@uris, fn uri ->
assert {:ok, uri} == ObjectID.cast(%{"id" => uri})
end)
end

test "it rejects non-uri strings" do
Enum.each(@non_uris, fn non_uri ->
assert :error == ObjectID.cast(non_uri)
assert :error == ObjectID.cast(%{"id" => non_uri})
end)
end
end

+ 87
- 0
test/web/activity_pub/pipeline_test.exs View File

@@ -0,0 +1,87 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.PipelineTest do
use Pleroma.DataCase

import Mock
import Pleroma.Factory

describe "common_pipeline/2" do
test "it goes through validation, filtering, persisting, side effects and federation for local activities" do
activity = insert(:note_activity)
meta = [local: true]

with_mocks([
{Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
{
Pleroma.Web.ActivityPub.MRF,
[],
[filter: fn o -> {:ok, o} end]
},
{
Pleroma.Web.ActivityPub.ActivityPub,
[],
[persist: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.ActivityPub.SideEffects,
[],
[handle: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.Federator,
[],
[publish: fn _o -> :ok end]
}
]) do
assert {:ok, ^activity, ^meta} =
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)

assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
assert_called(Pleroma.Web.Federator.publish(activity))
end
end

test "it goes through validation, filtering, persisting, side effects without federation for remote activities" do
activity = insert(:note_activity)
meta = [local: false]

with_mocks([
{Pleroma.Web.ActivityPub.ObjectValidator, [], [validate: fn o, m -> {:ok, o, m} end]},
{
Pleroma.Web.ActivityPub.MRF,
[],
[filter: fn o -> {:ok, o} end]
},
{
Pleroma.Web.ActivityPub.ActivityPub,
[],
[persist: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.ActivityPub.SideEffects,
[],
[handle: fn o, m -> {:ok, o, m} end]
},
{
Pleroma.Web.Federator,
[],
[]
}
]) do
assert {:ok, ^activity, ^meta} =
Pleroma.Web.ActivityPub.Pipeline.common_pipeline(activity, meta)

assert_called(Pleroma.Web.ActivityPub.ObjectValidator.validate(activity, meta))
assert_called(Pleroma.Web.ActivityPub.MRF.filter(activity))
assert_called(Pleroma.Web.ActivityPub.ActivityPub.persist(activity, meta))
assert_called(Pleroma.Web.ActivityPub.SideEffects.handle(activity, meta))
end
end
end
end

+ 34
- 0
test/web/activity_pub/side_effects_test.exs View File

@@ -0,0 +1,34 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
use Pleroma.DataCase

alias Pleroma.Object
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.SideEffects
alias Pleroma.Web.CommonAPI

import Pleroma.Factory

describe "like objects" do
setup do
user = insert(:user)
{:ok, post} = CommonAPI.post(user, %{"status" => "hey"})

{:ok, like_data, _meta} = Builder.like(user, post.object)
{:ok, like, _meta} = ActivityPub.persist(like_data, local: true)

%{like: like, user: user}
end

test "add the like to the original object", %{like: like, user: user} do
{:ok, like, _} = SideEffects.handle(like)
object = Object.get_by_ap_id(like.data["object"])
assert object.data["like_count"] == 1
assert user.ap_id in object.data["likes"]
end
end
end

+ 3
- 1
test/web/activity_pub/transmogrifier_test.exs View File

@@ -334,7 +334,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|> Poison.decode!()
|> Map.put("object", activity.data["object"])

{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
{:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)

refute Enum.empty?(activity.recipients)

assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Like"


+ 1
- 1
test/web/activity_pub/views/object_view_test.exs View File

@@ -59,7 +59,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
object = Object.normalize(note)
user = insert(:user)

{:ok, like_activity, _} = CommonAPI.favorite(note.id, user)
{:ok, like_activity} = CommonAPI.favorite(user, note.id)

result = ObjectView.render("object.json", %{object: like_activity})



+ 37
- 2
test/web/admin_api/admin_api_controller_test.exs View File

@@ -625,6 +625,39 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do

assert json_response(conn, :forbidden)
end

test "email with +", %{conn: conn, admin: admin} do
recipient_email = "foo+bar@baz.com"

conn
|> put_req_header("content-type", "application/json;charset=utf-8")
|> post("/api/pleroma/admin/users/email_invite", %{email: recipient_email})
|> json_response(:no_content)

token_record =
Pleroma.UserInviteToken
|> Repo.all()
|> List.last()

assert token_record
refute token_record.used

notify_email = Config.get([:instance, :notify_email])
instance_name = Config.get([:instance, :name])

email =
Pleroma.Emails.UserEmail.user_invitation_email(
admin,
token_record,
recipient_email
)

Swoosh.TestAssertions.assert_email_sent(
from: {instance_name, notify_email},
to: recipient_email,
html_body: email.html_body
)
end
end

describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
@@ -637,7 +670,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do

conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")

assert json_response(conn, :internal_server_error)
assert json_response(conn, :bad_request) ==
"To send invites you need to set the `invites_enabled` option to true."
end

test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
@@ -646,7 +680,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do

conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")

assert json_response(conn, :internal_server_error)
assert json_response(conn, :bad_request) ==
"To send invites you need to set the `registrations_open` option to false."
end
end



+ 45
- 0
test/web/api_spec/app_operation_test.exs View File

@@ -0,0 +1,45 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.ApiSpec.AppOperationTest do
use Pleroma.Web.ConnCase, async: true

alias Pleroma.Web.ApiSpec
alias Pleroma.Web.ApiSpec.Schemas.AppCreateRequest
alias Pleroma.Web.ApiSpec.Schemas.AppCreateResponse

import OpenApiSpex.TestAssertions
import Pleroma.Factory

test "AppCreateRequest example matches schema" do
api_spec = ApiSpec.spec()
schema = AppCreateRequest.schema()
assert_schema(schema.example, "AppCreateRequest", api_spec)
end

test "AppCreateResponse example matches schema" do
api_spec = ApiSpec.spec()
schema = AppCreateResponse.schema()
assert_schema(schema.example, "AppCreateResponse", api_spec)
end

test "AppController produces a AppCreateResponse", %{conn: conn} do
api_spec = ApiSpec.spec()
app_attrs = build(:oauth_app)

json =
conn
|> put_req_header("content-type", "application/json")
|> post(
"/api/v1/apps",
Jason.encode!(%{
client_name: app_attrs.client_name,
redirect_uris: app_attrs.redirect_uris
})
)
|> json_response(200)

assert_schema(json, "AppCreateResponse", api_spec)
end
end

+ 8
- 5
test/web/common_api/common_api_test.exs View File

@@ -284,9 +284,12 @@ defmodule Pleroma.Web.CommonAPITest do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
{:ok, post_activity} = CommonAPI.post(other_user, %{"status" => "cofe"})

{:ok, %Activity{}, _} = CommonAPI.favorite(activity.id, user)
{:ok, %Activity{data: data}} = CommonAPI.favorite(user, post_activity.id)
assert data["type"] == "Like"
assert data["actor"] == user.ap_id
assert data["object"] == post_activity.data["object"]
end

test "retweeting a status twice returns the status" do
@@ -298,13 +301,13 @@ defmodule Pleroma.Web.CommonAPITest do
{:ok, ^activity, ^object} = CommonAPI.repeat(activity.id, user)
end

test "favoriting a status twice returns the status" do
test "favoriting a status twice returns ok, but without the like activity" do
user = insert(:user)
other_user = insert(:user)

{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
{:ok, %Activity{} = activity, object} = CommonAPI.favorite(activity.id, user)
{:ok, ^activity, ^object} = CommonAPI.favorite(activity.id, user)
{:ok, %Activity{}} = CommonAPI.favorite(user, activity.id)
assert {:ok, :already_liked} = CommonAPI.favorite(user, activity.id)
end
end



+ 3
- 3
test/web/common_api/common_api_utils_test.exs View File

@@ -159,11 +159,11 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
{output, _, _} = Utils.format_input(text, "text/markdown")

assert output ==
~s(<p><strong>hello world</strong></p><p><em>another <span class="h-card"><a data-user="#{
~s(<p><strong>hello world</strong></p><p><em>another <span class="h-card"><a class="u-url mention" data-user="#{
user.id
}" class="u-url mention" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> and <span class="h-card"><a data-user="#{
}" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> and <span class="h-card"><a class="u-url mention" data-user="#{
user.id
}" class="u-url mention" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> <a href="http://google.com" rel="ugc">google.com</a> paragraph</em></p>)
}" href="http://foo.com/user__test" rel="ugc">@<span>user__test</span></a></span> <a href="http://google.com" rel="ugc">google.com</a> paragraph</em></p>)
end
end



+ 40
- 32
test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs View File

@@ -82,9 +82,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
assert user_data = json_response(conn, 200)

assert user_data["note"] ==
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a data-user="#{
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a class="u-url mention" data-user="#{
user2.id
}" class="u-url mention" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..)
}" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..)
end

test "updates the user's locking status", %{conn: conn} do
@@ -273,7 +273,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
test "update fields", %{conn: conn} do
fields = [
%{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "<script>bar</script>"},
%{"name" => "link", "value" => "cofe.io"}
%{"name" => "link.io", "value" => "cofe.io"}
]

account_data =
@@ -283,7 +283,10 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do

assert account_data["fields"] == [
%{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"},
%{"name" => "link", "value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)}
%{
"name" => "link.io",
"value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)
}
]

assert account_data["source"]["fields"] == [
@@ -291,14 +294,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
"name" => "<a href=\"http://google.com\">foo</a>",
"value" => "<script>bar</script>"
},
%{"name" => "link", "value" => "cofe.io"}
%{"name" => "link.io", "value" => "cofe.io"}
]
end

test "update fields via x-www-form-urlencoded", %{conn: conn} do
fields =
[
"fields_attributes[1][name]=link",
"fields_attributes[1][value]=cofe.io",
"fields_attributes[0][name]=<a href=\"http://google.com\">foo</a>",
"fields_attributes[1][value]=http://cofe.io",
"fields_attributes[0][name]=foo",
"fields_attributes[0][value]=bar"
]
|> Enum.join("&")
@@ -310,32 +315,49 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|> json_response(200)

assert account["fields"] == [
%{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"},
%{"name" => "link", "value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)}
%{"name" => "foo", "value" => "bar"},
%{
"name" => "link",
"value" => ~S(<a href="http://cofe.io" rel="ugc">http://cofe.io</a>)
}
]

assert account["source"]["fields"] == [
%{
"name" => "<a href=\"http://google.com\">foo</a>",
"value" => "bar"
},
%{"name" => "link", "value" => "cofe.io"}
%{"name" => "foo", "value" => "bar"},
%{"name" => "link", "value" => "http://cofe.io"}
]
end

test "update fields with empty name", %{conn: conn} do
fields = [
%{"name" => "foo", "value" => ""},
%{"name" => "", "value" => "bar"}
]

account =
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response(200)

assert account["fields"] == [
%{"name" => "foo", "value" => ""}
]
end

test "update fields when invalid request", %{conn: conn} do
name_limit = Pleroma.Config.get([:instance, :account_field_name_length])
value_limit = Pleroma.Config.get([:instance, :account_field_value_length])

long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join()
long_value = Enum.map(0..value_limit, fn _ -> "x" end) |> Enum.join()

fields = [%{"name" => "<b>foo<b>", "value" => long_value}]
fields = [%{"name" => "foo", "value" => long_value}]

assert %{"error" => "Invalid request"} ==
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response(403)

long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join()

fields = [%{"name" => long_name, "value" => "bar"}]

assert %{"error" => "Invalid request"} ==
@@ -346,7 +368,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
Pleroma.Config.put([:instance, :max_account_fields], 1)

fields = [
%{"name" => "<b>foo<b>", "value" => "<i>bar</i>"},
%{"name" => "foo", "value" => "bar"},
%{"name" => "link", "value" => "cofe.io"}
]

@@ -354,20 +376,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response(403)

fields = [
%{"name" => "foo", "value" => ""},
%{"name" => "", "value" => "bar"}
]

account =
conn
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|> json_response(200)

assert account["fields"] == [
%{"name" => "foo", "value" => ""}
]
end
end
end

+ 3
- 1
test/web/mastodon_api/controllers/account_controller_test.exs View File

@@ -794,7 +794,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do

test "Account registration via Application", %{conn: conn} do
conn =
post(conn, "/api/v1/apps", %{
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/apps", %{
client_name: "client_name",
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
scopes: "read, write, follow"


+ 2
- 2
test/web/mastodon_api/controllers/app_controller_test.exs View File

@@ -16,8 +16,7 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do

conn =
conn
|> assign(:user, token.user)
|> assign(:token, token)
|> put_req_header("authorization", "Bearer #{token.token}")
|> get("/api/v1/apps/verify_credentials")

app = Repo.preload(token, :app).app
@@ -37,6 +36,7 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do

conn =
conn
|> put_req_header("content-type", "application/json")
|> assign(:user, user)
|> post("/api/v1/apps", %{
client_name: app_attrs.client_name,


+ 25
- 9
test/web/mastodon_api/controllers/notification_controller_test.exs View File

@@ -26,7 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
|> get("/api/v1/notifications")

expected_response =
"hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
"hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{
user.ap_id
}\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>"

@@ -45,7 +45,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
conn = get(conn, "/api/v1/notifications/#{notification.id}")

expected_response =
"hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
"hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{
user.ap_id
}\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>"

@@ -53,7 +53,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
assert response == expected_response
end

test "dismissing a single notification" do
test "dismissing a single notification (deprecated endpoint)" do
%{user: user, conn: conn} = oauth_access(["write:notifications"])
other_user = insert(:user)

@@ -69,6 +69,22 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
assert %{} = json_response(conn, 200)
end

test "dismissing a single notification" do
%{user: user, conn: conn} = oauth_access(["write:notifications"])
other_user = insert(:user)

{:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})

{:ok, [notification]} = Notification.create_notifications(activity)

conn =
conn
|> assign(:user, user)
|> post("/api/v1/notifications/#{notification.id}/dismiss")

assert %{} = json_response(conn, 200)
end

test "clearing all notifications" do
%{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"])
other_user = insert(:user)
@@ -194,10 +210,10 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
{:ok, private_activity} =
CommonAPI.post(other_user, %{"status" => ".", "visibility" => "private"})

{:ok, _, _} = CommonAPI.favorite(public_activity.id, user)
{:ok, _, _} = CommonAPI.favorite(direct_activity.id, user)
{:ok, _, _} = CommonAPI.favorite(unlisted_activity.id, user)
{:ok, _, _} = CommonAPI.favorite(private_activity.id, user)
{:ok, _} = CommonAPI.favorite(user, public_activity.id)
{:ok, _} = CommonAPI.favorite(user, direct_activity.id)
{:ok, _} = CommonAPI.favorite(user, unlisted_activity.id)
{:ok, _} = CommonAPI.favorite(user, private_activity.id)

activity_ids =
conn
@@ -274,7 +290,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do

{:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
{:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)

@@ -310,7 +326,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do

{:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
{:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
{:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
{:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
{:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
{:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)



+ 14
- 10
test/web/mastodon_api/controllers/status_controller_test.exs View File

@@ -775,7 +775,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
user1 = insert(:user)
user2 = insert(:user)
user3 = insert(:user)
CommonAPI.favorite(activity.id, user2)
{:ok, _} = CommonAPI.favorite(user2, activity.id)
{:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
{:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
{:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
@@ -850,11 +850,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
activity = insert(:note_activity)

post(conn, "/api/v1/statuses/#{activity.id}/favourite")
assert post(conn, "/api/v1/statuses/#{activity.id}/favourite") |> json_response(200)

assert post(conn, "/api/v1/statuses/#{activity.id}/favourite")
|> json_response(200)
end

test "returns 404 error for a wrong id", %{conn: conn} do
conn = post(conn, "/api/v1/statuses/1/favourite")
conn =
conn
|> post("/api/v1/statuses/1/favourite")

assert json_response(conn, 404) == %{"error" => "Record not found"}
end
@@ -866,7 +870,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
test "unfavorites a status and returns it", %{user: user, conn: conn} do
activity = insert(:note_activity)

{:ok, _, _} = CommonAPI.favorite(activity.id, user)
{:ok, _} = CommonAPI.favorite(user, activity.id)

conn = post(conn, "/api/v1/statuses/#{activity.id}/unfavourite")

@@ -1176,7 +1180,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do

test "returns users who have favorited the status", %{conn: conn, activity: activity} do
other_user = insert(:user)
{:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)

response =
conn
@@ -1207,7 +1211,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
other_user = insert(:user)
{:ok, _user_relationship} = User.block(user, other_user)

{:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)

response =
conn
@@ -1219,7 +1223,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do

test "does not fail on an unauthenticated request", %{activity: activity} do
other_user = insert(:user)
{:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)

response =
build_conn()
@@ -1239,7 +1243,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
"visibility" => "direct"
})

{:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
{:ok, _} = CommonAPI.favorite(other_user, activity.id)

favourited_by_url = "/api/v1/statuses/#{activity.id}/favourited_by"

@@ -1399,7 +1403,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
{:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})

{:ok, _, _} = CommonAPI.favorite(activity.id, user)
{:ok, _} = CommonAPI.favorite(user, activity.id)

first_conn = get(conn, "/api/v1/favourites")

@@ -1416,7 +1420,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
"Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
})

{:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
{:ok, _} = CommonAPI.favorite(user, second_activity.id)

last_like = status["id"]



+ 3
- 0
test/web/mastodon_api/views/account_view_test.exs View File

@@ -209,6 +209,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
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 %{


+ 1
- 1
test/web/mastodon_api/views/notification_view_test.exs View File

@@ -54,7 +54,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
user = insert(:user)
another_user = insert(:user)
{:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
{:ok, favorite_activity, _object} = CommonAPI.favorite(create_activity.id, another_user)
{:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id)
{:ok, [notification]} = Notification.create_notifications(favorite_activity)
create_activity = Activity.get_by_id(create_activity.id)



+ 56
- 30
test/web/node_info_test.exs View File

@@ -7,6 +7,8 @@ defmodule Pleroma.Web.NodeInfoTest do

import Pleroma.Factory

alias Pleroma.Config

setup do: clear_config([:mrf_simple])
setup do: clear_config(:instance)

@@ -47,7 +49,7 @@ defmodule Pleroma.Web.NodeInfoTest do

assert result = json_response(conn, 200)

assert Pleroma.Config.get([Pleroma.User, :restricted_nicknames]) ==
assert Config.get([Pleroma.User, :restricted_nicknames]) ==
result["metadata"]["restrictedNicknames"]
end

@@ -65,10 +67,10 @@ defmodule Pleroma.Web.NodeInfoTest do
end

test "returns fieldsLimits field", %{conn: conn} do
Pleroma.Config.put([:instance, :max_account_fields], 10)
Pleroma.Config.put([:instance, :max_remote_account_fields], 15)
Pleroma.Config.put([:instance, :account_field_name_length], 255)
Pleroma.Config.put([:instance, :account_field_value_length], 2048)
Config.put([:instance, :max_account_fields], 10)
Config.put([:instance, :max_remote_account_fields], 15)
Config.put([:instance, :account_field_name_length], 255)
Config.put([:instance, :account_field_value_length], 2048)

response =
conn
@@ -82,8 +84,8 @@ defmodule Pleroma.Web.NodeInfoTest do
end

test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
option = Pleroma.Config.get([:instance, :safe_dm_mentions])
Pleroma.Config.put([:instance, :safe_dm_mentions], true)
option = Config.get([:instance, :safe_dm_mentions])
Config.put([:instance, :safe_dm_mentions], true)

response =
conn
@@ -92,7 +94,7 @@ defmodule Pleroma.Web.NodeInfoTest do

assert "safe_dm_mentions" in response["metadata"]["features"]

Pleroma.Config.put([:instance, :safe_dm_mentions], false)
Config.put([:instance, :safe_dm_mentions], false)

response =
conn
@@ -101,14 +103,14 @@ defmodule Pleroma.Web.NodeInfoTest do

refute "safe_dm_mentions" in response["metadata"]["features"]

Pleroma.Config.put([:instance, :safe_dm_mentions], option)
Config.put([:instance, :safe_dm_mentions], option)
end

describe "`metadata/federation/enabled`" do
setup do: clear_config([:instance, :federating])

test "it shows if federation is enabled/disabled", %{conn: conn} do
Pleroma.Config.put([:instance, :federating], true)
Config.put([:instance, :federating], true)

response =
conn
@@ -117,7 +119,7 @@ defmodule Pleroma.Web.NodeInfoTest do

assert response["metadata"]["federation"]["enabled"] == true

Pleroma.Config.put([:instance, :federating], false)
Config.put([:instance, :federating], false)

response =
conn
@@ -128,15 +130,39 @@ defmodule Pleroma.Web.NodeInfoTest do
end
end

test "it shows default features flags", %{conn: conn} do
response =
conn
|> get("/nodeinfo/2.1.json")
|> json_response(:ok)

default_features = [
"pleroma_api",
"mastodon_api",
"mastodon_api_streaming",
"polls",
"pleroma_explicit_addressing",
"shareable_emoji_packs",
"multifetch",
"pleroma_emoji_reactions",
"pleroma:api/v1/notifications:include_types_filter"
]

assert MapSet.subset?(
MapSet.new(default_features),
MapSet.new(response["metadata"]["features"])
)
end

test "it shows MRF transparency data if enabled", %{conn: conn} do
config = Pleroma.Config.get([:instance, :rewrite_policy])
Pleroma.Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
config = Config.get([:instance, :rewrite_policy])
Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])

option = Pleroma.Config.get([:instance, :mrf_transparency])
Pleroma.Config.put([:instance, :mrf_transparency], true)
option = Config.get([:instance, :mrf_transparency])
Config.put([:instance, :mrf_transparency], true)

simple_config = %{"reject" => ["example.com"]}
Pleroma.Config.put(:mrf_simple, simple_config)
Config.put(:mrf_simple, simple_config)

response =
conn
@@ -145,25 +171,25 @@ defmodule Pleroma.Web.NodeInfoTest do

assert response["metadata"]["federation"]["mrf_simple"] == simple_config

Pleroma.Config.put([:instance, :rewrite_policy], config)
Pleroma.Config.put([:instance, :mrf_transparency], option)
Pleroma.Config.put(:mrf_simple, %{})
Config.put([:instance, :rewrite_policy], config)
Config.put([:instance, :mrf_transparency], option)
Config.put(:mrf_simple, %{})
end

test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do
config = Pleroma.Config.get([:instance, :rewrite_policy])
Pleroma.Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
config = Config.get([:instance, :rewrite_policy])
Config.put([:instance, :rewrite_policy], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])

option = Pleroma.Config.get([:instance, :mrf_transparency])
Pleroma.Config.put([:instance, :mrf_transparency], true)
option = Config.get([:instance, :mrf_transparency])
Config.put([:instance, :mrf_transparency], true)

exclusions = Pleroma.Config.get([:instance, :mrf_transparency_exclusions])
Pleroma.Config.put([:instance, :mrf_transparency_exclusions], ["other.site"])
exclusions = Config.get([:instance, :mrf_transparency_exclusions])
Config.put([:instance, :mrf_transparency_exclusions], ["other.site"])

simple_config = %{"reject" => ["example.com", "other.site"]}
expected_config = %{"reject" => ["example.com"]}

Pleroma.Config.put(:mrf_simple, simple_config)
Config.put(:mrf_simple, simple_config)

response =
conn
@@ -173,9 +199,9 @@ defmodule Pleroma.Web.NodeInfoTest do
assert response["metadata"]["federation"]["mrf_simple"] == expected_config
assert response["metadata"]["federation"]["exclusions"] == true

Pleroma.Config.put([:instance, :rewrite_policy], config)
Pleroma.Config.put([:instance, :mrf_transparency], option)
Pleroma.Config.put([:instance, :mrf_transparency_exclusions], exclusions)
Pleroma.Config.put(:mrf_simple, %{})
Config.put([:instance, :rewrite_policy], config)
Config.put([:instance, :mrf_transparency], option)
Config.put([:instance, :mrf_transparency_exclusions], exclusions)
Config.put(:mrf_simple, %{})
end
end

+ 1
- 1
test/web/ostatus/ostatus_controller_test.exs View File

@@ -136,7 +136,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do

user = insert(:user)

{:ok, like_activity, _} = CommonAPI.favorite(note_activity.id, user)
{:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)

assert like_activity.data["type"] == "Like"



+ 8
- 8
test/web/pleroma_api/controllers/account_controller_test.exs View File

@@ -138,7 +138,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
user: user
} do
[activity | _] = insert_pair(:note_activity)
CommonAPI.favorite(activity.id, user)
CommonAPI.favorite(user, activity.id)

response =
conn
@@ -155,7 +155,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
user: user
} do
activity = insert(:note_activity)
CommonAPI.favorite(activity.id, user)
CommonAPI.favorite(user, activity.id)

build_conn()
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
@@ -172,7 +172,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
"visibility" => "direct"
})

CommonAPI.favorite(direct.id, user)
CommonAPI.favorite(user, direct.id)

for u <- [user, current_user] do
response =
@@ -202,7 +202,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
"visibility" => "direct"
})

CommonAPI.favorite(direct.id, user)
CommonAPI.favorite(user, direct.id)

response =
conn
@@ -219,7 +219,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
activities = insert_list(10, :note_activity)

Enum.each(activities, fn activity ->
CommonAPI.favorite(activity.id, user)
CommonAPI.favorite(user, activity.id)
end)

third_activity = Enum.at(activities, 2)
@@ -245,7 +245,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
7
|> insert_list(:note_activity)
|> Enum.each(fn activity ->
CommonAPI.favorite(activity.id, user)
CommonAPI.favorite(user, activity.id)
end)

response =
@@ -277,7 +277,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
test "returns 403 error when user has hidden own favorites", %{conn: conn} do
user = insert(:user, hide_favorites: true)
activity = insert(:note_activity)
CommonAPI.favorite(activity.id, user)
CommonAPI.favorite(user, activity.id)

conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")

@@ -287,7 +287,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
test "hides favorites for new users by default", %{conn: conn} do
user = insert(:user)
activity = insert(:note_activity)
CommonAPI.favorite(activity.id, user)
CommonAPI.favorite(user, activity.id)

assert user.hide_favorites
conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")


+ 1
- 1
test/web/push/impl_test.exs View File

@@ -170,7 +170,7 @@ defmodule Pleroma.Web.Push.ImplTest do
"<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
})

{:ok, activity, _} = CommonAPI.favorite(activity.id, user)
{:ok, activity} = CommonAPI.favorite(user, activity.id)
object = Object.normalize(activity)

assert Impl.format_body(%{activity: activity}, user, object) == "@Bob has favorited your post"


+ 12
- 12
test/web/streamer/streamer_test.exs View File

@@ -64,9 +64,6 @@ defmodule Pleroma.Web.StreamerTest do
blocked = insert(:user)
{:ok, _user_relationship} = User.block(user, blocked)

{:ok, activity} = CommonAPI.post(user, %{"status" => ":("})
{:ok, notif, _} = CommonAPI.favorite(activity.id, blocked)

task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)

Streamer.add_socket(
@@ -74,6 +71,9 @@ defmodule Pleroma.Web.StreamerTest do
%{transport_pid: task.pid, assigns: %{user: user}}
)

{:ok, activity} = CommonAPI.post(user, %{"status" => ":("})
{:ok, notif} = CommonAPI.favorite(blocked, activity.id)

Streamer.stream("user:notification", notif)
Task.await(task)
end
@@ -83,10 +83,6 @@ defmodule Pleroma.Web.StreamerTest do
} do
user2 = insert(:user)

{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, activity} = CommonAPI.add_mute(user, activity)
{:ok, notif, _} = CommonAPI.favorite(activity.id, user2)

task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)

Streamer.add_socket(
@@ -94,6 +90,10 @@ defmodule Pleroma.Web.StreamerTest do
%{transport_pid: task.pid, assigns: %{user: user}}
)

{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, activity} = CommonAPI.add_mute(user, activity)
{:ok, notif} = CommonAPI.favorite(user2, activity.id)

Streamer.stream("user:notification", notif)
Task.await(task)
end
@@ -103,10 +103,6 @@ defmodule Pleroma.Web.StreamerTest do
} do
user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})

{:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, notif, _} = CommonAPI.favorite(activity.id, user2)

task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)

Streamer.add_socket(
@@ -114,6 +110,10 @@ defmodule Pleroma.Web.StreamerTest do
%{transport_pid: task.pid, assigns: %{user: user}}
)

{:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
{:ok, notif} = CommonAPI.favorite(user2, activity.id)

Streamer.stream("user:notification", notif)
Task.await(task)
end
@@ -476,7 +476,7 @@ defmodule Pleroma.Web.StreamerTest do
CommonAPI.hide_reblogs(user1, user2)

{:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
{:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, user2)
{:ok, favorite_activity} = CommonAPI.favorite(user2, create_activity.id)

task =
Task.async(fn ->


+ 1
- 1
test/web/twitter_api/twitter_api_test.exs View File

@@ -109,7 +109,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
{:ok, user2} = TwitterAPI.register_user(data2)

expected_text =
~s(<span class="h-card"><a data-user="#{user1.id}" class="u-url mention" href="#{
~s(<span class="h-card"><a class="u-url mention" data-user="#{user1.id}" href="#{
user1.ap_id
}" rel="ugc">@<span>john</span></a></span> test)



Loading…
Cancel
Save