Merge branch 'tests' into 'master'

Make tests great again !

See merge request tcit/eventos!14
This commit is contained in:
Thomas Citharel 2018-06-14 18:18:36 +02:00
commit 6bcaaa0e79
51 changed files with 761 additions and 696 deletions

160
.credo.exs Normal file
View file

@ -0,0 +1,160 @@
# This file contains the configuration for Credo and you are probably reading
# this after creating it with `mix credo.gen.config`.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
#
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
#
# Run any exec using `mix credo -C <name>`. If no exec name is given
# "default" is used.
#
name: "default",
#
# These are the files included in the analysis:
files: %{
#
# You can give explicit globs or simply directories.
# In the latter case `**/*.{ex,exs}` will be used.
#
included: ["lib/", "src/", "test/", "web/", "apps/"],
excluded: [~r"/_build/", ~r"/deps/"]
},
#
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
#
requires: [],
#
# If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below:
#
strict: false,
#
# If you want to use uncolored output by default, you can change `color`
# to `false` below:
#
color: true,
#
# You can customize the parameters of any check by adding a second element
# to the tuple.
#
# To disable a check put `false` as second element:
#
# {Credo.Check.Design.DuplicatedCode, false}
#
checks: [
#
## Consistency Checks
#
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.ParameterPatternMatching},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},
#
## Design Checks
#
# You can customize the priority of any check
# Priority values are: `low, normal, high, higher`
#
{Credo.Check.Design.AliasUsage, priority: :low},
# For some checks, you can also set other parameters
#
# If you don't want the `setup` and `test` macro calls in ExUnit tests
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
#
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
#
{Credo.Check.Design.TagTODO, exit_status: 0},
{Credo.Check.Design.TagFIXME},
#
## Readability Checks
#
{Credo.Check.Readability.AliasOrder},
{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 80},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{Credo.Check.Readability.ModuleNames},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
{Credo.Check.Readability.ParenthesesInCondition},
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.PreferImplicitTry},
{Credo.Check.Readability.RedundantBlankLines},
{Credo.Check.Readability.StringSigils},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
{Credo.Check.Readability.VariableNames},
{Credo.Check.Readability.Semicolons},
{Credo.Check.Readability.SpaceAfterCommas},
#
## Refactoring Opportunities
#
{Credo.Check.Refactor.DoubleBooleanNegation},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.CyclomaticComplexity},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.LongQuoteBlocks},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.NegatedConditionsInUnless},
{Credo.Check.Refactor.NegatedConditionsWithElse},
{Credo.Check.Refactor.Nesting, max_nesting: 3},
{Credo.Check.Refactor.PipeChainStart,
excluded_argument_types: [:atom, :binary, :fn, :keyword], excluded_functions: []},
{Credo.Check.Refactor.UnlessWithElse},
#
## Warnings
#
{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.ExpensiveEmptyEnumCheck},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.LazyLogging},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.OperationWithConstantResult},
{Credo.Check.Warning.UnusedEnumOperation},
{Credo.Check.Warning.UnusedFileOperation},
{Credo.Check.Warning.UnusedKeywordOperation},
{Credo.Check.Warning.UnusedListOperation},
{Credo.Check.Warning.UnusedPathOperation},
{Credo.Check.Warning.UnusedRegexOperation},
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation},
{Credo.Check.Warning.RaiseInsideRescue},
#
# Controversial and experimental checks (opt-in, just remove `, false`)
#
{Credo.Check.Refactor.ABCSize, false},
{Credo.Check.Refactor.AppendSingleItem, false},
{Credo.Check.Refactor.VariableRebinding, false},
{Credo.Check.Warning.MapGetUnsafePass, false},
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
#
# Deprecated checks (these will be deleted after a grace period)
#
{Credo.Check.Readability.Specs, false}
#
# Custom checks can be created using `mix credo.gen.check`.
#
]
}
]
}

View file

@ -46,7 +46,7 @@ defmodule Eventos.Actors.Actor do
import Logger import Logger
# @type t :: %Actor{description: String.t, id: integer(), inserted_at: DateTime.t, updated_at: DateTime.t, display_name: String.t, domain: String.t, private_key: String.t, public_key: String.t, suspended: boolean(), url: String.t, username: String.t, organized_events: list(), groups: list(), group_request: list(), user: User.t, field: ActorTypeEnum.t} # @type t :: %Actor{description: String.t, id: integer(), inserted_at: DateTime.t, updated_at: DateTime.t, display_name: String.t, domain: String.t, keys: String.t, suspended: boolean(), url: String.t, username: String.t, organized_events: list(), groups: list(), group_request: list(), user: User.t, field: ActorTypeEnum.t}
schema "actors" do schema "actors" do
field :url, :string field :url, :string
@ -55,13 +55,12 @@ defmodule Eventos.Actors.Actor do
field :following_url, :string field :following_url, :string
field :followers_url, :string field :followers_url, :string
field :shared_inbox_url, :string field :shared_inbox_url, :string
field :type, Eventos.Actors.ActorTypeEnum field :type, Eventos.Actors.ActorTypeEnum, default: :Person
field :name, :string field :name, :string
field :domain, :string field :domain, :string
field :summary, :string field :summary, :string
field :preferred_username, :string field :preferred_username, :string
field :public_key, :string field :keys, :string
field :private_key, :string
field :manually_approves_followers, :boolean, default: false field :manually_approves_followers, :boolean, default: false
field :suspended, :boolean, default: false field :suspended, :boolean, default: false
field :avatar_url, :string field :avatar_url, :string
@ -77,24 +76,25 @@ defmodule Eventos.Actors.Actor do
@doc false @doc false
def changeset(%Actor{} = actor, attrs) do def changeset(%Actor{} = actor, attrs) do
actor actor
|> Ecto.Changeset.cast(attrs, [:url, :outbox_url, :inbox_url, :shared_inbox_url, :following_url, :followers_url, :type, :name, :domain, :summary, :preferred_username, :public_key, :private_key, :manually_approves_followers, :suspended, :avatar_url, :banner_url]) |> Ecto.Changeset.cast(attrs, [:url, :outbox_url, :inbox_url, :shared_inbox_url, :following_url, :followers_url, :type, :name, :domain, :summary, :preferred_username, :keys, :manually_approves_followers, :suspended, :avatar_url, :banner_url])
|> validate_required([:preferred_username, :public_key, :suspended, :url]) |> validate_required([:preferred_username, :keys, :suspended, :url])
|> unique_constraint(:prefered_username, name: :actors_preferred_username_domain_index) |> unique_constraint(:prefered_username, name: :actors_preferred_username_domain_index)
end end
def registration_changeset(%Actor{} = actor, attrs) do def registration_changeset(%Actor{} = actor, attrs) do
actor actor
|> Ecto.Changeset.cast(attrs, [:preferred_username, :domain, :name, :summary, :private_key, :public_key, :suspended, :url, :type]) |> Ecto.Changeset.cast(attrs, [:preferred_username, :domain, :name, :summary, :keys, :keys, :suspended, :url, :type, :avatar_url])
|> validate_required([:preferred_username, :public_key, :suspended, :url, :type]) |> unique_constraint(:preferred_username, name: :actors_preferred_username_domain_index)
|> unique_constraint(:prefered_username, name: :actors_preferred_username_domain_index) |> put_change(:url, "#{EventosWeb.Endpoint.url()}/@#{attrs["prefered_username"]}")
|> validate_required([:preferred_username, :keys, :suspended, :url, :type])
end end
@email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
def remote_actor_creation(params) do def remote_actor_creation(params) do
changes = changes =
%Actor{} %Actor{}
|> Ecto.Changeset.cast(params, [:url, :outbox_url, :inbox_url, :shared_inbox_url, :following_url, :followers_url, :type, :name, :domain, :summary, :preferred_username, :public_key, :manually_approves_followers, :avatar_url, :banner_url]) |> Ecto.Changeset.cast(params, [:url, :outbox_url, :inbox_url, :shared_inbox_url, :following_url, :followers_url, :type, :name, :domain, :summary, :preferred_username, :keys, :manually_approves_followers, :avatar_url, :banner_url])
|> validate_required([:url, :outbox_url, :inbox_url, :type, :name, :domain, :preferred_username, :public_key]) |> validate_required([:url, :outbox_url, :inbox_url, :type, :name, :domain, :preferred_username, :keys])
|> unique_constraint(:preferred_username, name: :actors_preferred_username_domain_index) |> unique_constraint(:preferred_username, name: :actors_preferred_username_domain_index)
|> validate_length(:summary, max: 5000) |> validate_length(:summary, max: 5000)
|> validate_length(:preferred_username, max: 100) |> validate_length(:preferred_username, max: 100)
@ -135,20 +135,31 @@ defmodule Eventos.Actors.Actor do
#@spec get_public_key_for_url(Actor.t) :: {:ok, String.t} #@spec get_public_key_for_url(Actor.t) :: {:ok, String.t}
def get_public_key_for_url(url) do def get_public_key_for_url(url) do
with %Actor{} = actor <- get_or_fetch_by_url(url) do with %Actor{} = actor <- get_or_fetch_by_url(url) do
get_public_key_for_actor(actor) actor
|> get_keys_for_actor
|> Eventos.Service.ActivityPub.Utils.pem_to_public_key
else else
_ -> :error _ -> :error
end end
end end
@deprecated "Use get_keys_for_actor/1 instead"
#@spec get_public_key_for_actor(Actor.t) :: {:ok, String.t} #@spec get_public_key_for_actor(Actor.t) :: {:ok, String.t}
def get_public_key_for_actor(%Actor{} = actor) do def get_public_key_for_actor(%Actor{} = actor) do
{:ok, actor.public_key} {:ok, actor.keys}
end end
@doc """
Returns a pem encoded keypair (if local) or public key
"""
def get_keys_for_actor(%Actor{} = actor) do
actor.keys
end
@deprecated "Use get_keys_for_actor/1 instead"
#@spec get_private_key_for_actor(Actor.t) :: {:ok, String.t} #@spec get_private_key_for_actor(Actor.t) :: {:ok, String.t}
def get_private_key_for_actor(%Actor{} = actor) do def get_private_key_for_actor(%Actor{} = actor) do
actor.private_key actor.keys
end end
def get_followers(%Actor{id: actor_id} = actor) do def get_followers(%Actor{id: actor_id} = actor) do

View file

@ -185,7 +185,7 @@ defmodule Eventos.Actors do
def insert_or_update_actor(data) do def insert_or_update_actor(data) do
cs = Actor.remote_actor_creation(data) cs = Actor.remote_actor_creation(data)
Repo.insert(cs, on_conflict: [set: [public_key: data.public_key, avatar_url: data.avatar_url, banner_url: data.banner_url, name: data.name]], conflict_target: [:preferred_username, :domain]) Repo.insert(cs, on_conflict: [set: [keys: data.keys, avatar_url: data.avatar_url, banner_url: data.banner_url, name: data.name]], conflict_target: [:preferred_username, :domain])
end end
# def increase_event_count(%Actor{} = actor) do # def increase_event_count(%Actor{} = actor) do
@ -335,16 +335,25 @@ defmodule Eventos.Actors do
Register user Register user
""" """
def register(%{email: email, password: password, username: username}) do def register(%{email: email, password: password, username: username}) do
#{:ok, {privkey, pubkey}} = RsaEx.generate_keypair("4096") key = :public_key.generate_key({:rsa, 2048, 65_537})
{:ok, rsa_priv_key} = ExPublicKey.generate_key() entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
{:ok, rsa_pub_key} = ExPublicKey.public_key_from_private_key(rsa_priv_key) pem = [entry] |> :public_key.pem_encode() |> String.trim_trailing()
import Exgravatar
avatar_url = gravatar_url(email, default: "404")
avatar = case HTTPoison.get(avatar_url) do
{:ok, %HTTPoison.Response{status_code: 200}} ->
avatar_url
_ ->
nil
end
actor = Eventos.Actors.Actor.registration_changeset(%Eventos.Actors.Actor{}, %{ actor = Eventos.Actors.Actor.registration_changeset(%Eventos.Actors.Actor{}, %{
preferred_username: username, preferred_username: username,
domain: nil, domain: nil,
private_key: rsa_priv_key |> ExPublicKey.pem_encode(), keys: pem,
public_key: rsa_pub_key |> ExPublicKey.pem_encode(), avatar_url: avatar,
url: EventosWeb.Endpoint.url() <> "/@" <> username,
}) })
user = Eventos.Actors.User.registration_changeset(%Eventos.Actors.User{}, %{ user = Eventos.Actors.User.registration_changeset(%Eventos.Actors.User{}, %{
@ -361,24 +370,19 @@ defmodule Eventos.Actors do
{:ok, user} {:ok, user}
rescue rescue
e in Ecto.InvalidChangesetError -> e in Ecto.InvalidChangesetError ->
{:error, e.changeset.changes.user.errors} {:error, e.changeset}
end end
end end
def register_bot_account(%{name: name, summary: summary}) do def register_bot_account(%{name: name, summary: summary}) do
key = :public_key.generate_key({:rsa, 2048, 65537}) key = :public_key.generate_key({:rsa, 2048, 65_537})
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key) entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
pem = :public_key.pem_encode([entry]) |> String.trim_trailing() pem = [entry] |> :public_key.pem_encode() |> String.trim_trailing()
{:ok, rsa_priv_key} = ExPublicKey.generate_key()
{:ok, rsa_pub_key} = ExPublicKey.public_key_from_private_key(rsa_priv_key)
actor = Eventos.Actors.Actor.registration_changeset(%Eventos.Actors.Actor{}, %{ actor = Eventos.Actors.Actor.registration_changeset(%Eventos.Actors.Actor{}, %{
preferred_username: name, preferred_username: name,
domain: nil, domain: nil,
private_key: pem, keys: pem,
public_key: "toto",
url: EventosWeb.Endpoint.url() <> "/@" <> name,
summary: summary, summary: summary,
type: :Service type: :Service
}) })
@ -387,7 +391,7 @@ defmodule Eventos.Actors do
Eventos.Repo.insert!(actor) Eventos.Repo.insert!(actor)
rescue rescue
e in Ecto.InvalidChangesetError -> e in Ecto.InvalidChangesetError ->
{:error, e} {:error, e.changeset}
end end
end end

View file

@ -26,5 +26,6 @@ defmodule Eventos.Addresses.Address do
def changeset(%Address{} = address, attrs) do def changeset(%Address{} = address, attrs) do
address address
|> cast(attrs, [:description, :floor, :geom, :addressCountry, :addressLocality, :addressRegion, :postalCode, :streetAddress]) |> cast(attrs, [:description, :floor, :geom, :addressCountry, :addressLocality, :addressRegion, :postalCode, :streetAddress])
|> validate_required([:streetAddress])
end end
end end

View file

@ -1,4 +1,8 @@
defmodule Eventos.Events.Comment do defmodule Eventos.Events.Comment do
@moduledoc """
An actor comment (for instance on an event or on a group)
"""
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
@ -10,7 +14,9 @@ defmodule Eventos.Events.Comment do
field :text, :string field :text, :string
field :url, :string field :url, :string
field :local, :boolean, default: true field :local, :boolean, default: true
field :uuid, Ecto.UUID
belongs_to :actor, Actor, [foreign_key: :actor_id] belongs_to :actor, Actor, [foreign_key: :actor_id]
belongs_to :attributed_to, Actor, [foreign_key: :attributed_to_id]
belongs_to :event, Event, [foreign_key: :event_id] belongs_to :event, Event, [foreign_key: :event_id]
belongs_to :in_reply_to_comment, Comment, [foreign_key: :in_reply_to_comment_id] belongs_to :in_reply_to_comment, Comment, [foreign_key: :in_reply_to_comment_id]
belongs_to :origin_comment, Comment, [foreign_key: :origin_comment_id] belongs_to :origin_comment, Comment, [foreign_key: :origin_comment_id]
@ -20,8 +26,11 @@ defmodule Eventos.Events.Comment do
@doc false @doc false
def changeset(comment, attrs) do def changeset(comment, attrs) do
uuid = Ecto.UUID.generate()
comment comment
|> cast(attrs, [:url, :text, :actor_id, :event_id, :in_reply_to_comment_id]) |> cast(attrs, [:url, :text, :actor_id, :event_id, :in_reply_to_comment_id, :attributed_to_id])
|> validate_required([:url, :text, :actor_id]) |> validate_required([:text, :actor_id])
|> put_change(:uuid, uuid)
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/comments/#{uuid}")
end end
end end

View file

@ -53,6 +53,7 @@ defmodule Eventos.Events.Event do
field :publish_at, Timex.Ecto.DateTimeWithTimezone field :publish_at, Timex.Ecto.DateTimeWithTimezone
field :uuid, Ecto.UUID, default: Ecto.UUID.generate() field :uuid, Ecto.UUID, default: Ecto.UUID.generate()
belongs_to :organizer_actor, Actor, [foreign_key: :organizer_actor_id] belongs_to :organizer_actor, Actor, [foreign_key: :organizer_actor_id]
belongs_to :attributed_to, Actor, [foreign_key: :attributed_to_id]
many_to_many :tags, Tag, join_through: "events_tags" many_to_many :tags, Tag, join_through: "events_tags"
belongs_to :category, Category belongs_to :category, Category
many_to_many :participants, Actor, join_through: Participant many_to_many :participants, Actor, join_through: Participant
@ -65,17 +66,22 @@ defmodule Eventos.Events.Event do
@doc false @doc false
def changeset(%Event{} = event, attrs) do def changeset(%Event{} = event, attrs) do
changeset = event uuid = Ecto.UUID.generate()
# TODO : check what's the use here. Tests ?
actor_url = if Map.has_key?(attrs, :organizer_actor) do
attrs.organizer_actor.preferred_username
else
""
end
event
|> cast(attrs, [:title, :description, :url, :begins_on, :ends_on, :organizer_actor_id, :category_id, :state, :status, :public, :thumbnail, :large_image, :publish_at]) |> cast(attrs, [:title, :description, :url, :begins_on, :ends_on, :organizer_actor_id, :category_id, :state, :status, :public, :thumbnail, :large_image, :publish_at])
|> cast_assoc(:tags) |> cast_assoc(:tags)
|> cast_assoc(:address) |> cast_assoc(:address)
|> validate_required([:title, :description, :begins_on, :ends_on, :organizer_actor_id, :category_id])
|> TitleSlug.maybe_generate_slug() |> TitleSlug.maybe_generate_slug()
|> TitleSlug.unique_constraint() |> TitleSlug.unique_constraint()
|> put_change(:uuid, Ecto.UUID.generate()) |> put_change(:uuid, uuid)
|> put_change(:url, "#{EventosWeb.Endpoint.url()}/@#{actor_url}/#{uuid}")
import Logger |> validate_required([:title, :description, :begins_on, :ends_on, :organizer_actor_id, :category_id, :url, :uuid])
Logger.debug(inspect changeset)
changeset
end end
end end

View file

@ -152,10 +152,11 @@ defmodule Eventos.Events do
""" """
def create_event(attrs \\ %{}) do def create_event(attrs \\ %{}) do
%Event{} case %Event{} |> Event.changeset(attrs) |> Repo.insert() do
|> Event.changeset(attrs) {:ok, %Event{} = event} -> {:ok, Repo.preload(event, [:organizer_actor])}
|> Repo.insert!() err -> err
|> Repo.preload([:organizer_actor]) end
end end
@doc """ @doc """
@ -522,8 +523,13 @@ defmodule Eventos.Events do
@doc """ @doc """
Returns the list of sessions for an event Returns the list of sessions for an event
""" """
def list_sessions_for_event(event_id) do def list_sessions_for_event(event_uuid) do
Repo.all(from s in Session, where: s.event_id == ^event_id) Repo.all(
from s in Session,
join: e in Event,
on: s.event_id == e.id,
where: e.uuid == ^event_uuid
)
end end
@doc """ @doc """
@ -741,6 +747,8 @@ defmodule Eventos.Events do
""" """
def get_comment!(id), do: Repo.get!(Comment, id) def get_comment!(id), do: Repo.get!(Comment, id)
def get_comment_with_uuid!(uuid), do: Repo.get_by!(Comment, uuid: uuid)
@doc """ @doc """
Creates a comment. Creates a comment.

View file

@ -9,8 +9,7 @@ defmodule Eventos.Events.Participant do
@primary_key false @primary_key false
schema "participants" do schema "participants" do
field :role, :integer, default: 0 # 0 : participant, 1 : moderator, 2 : administrator, 3 : creator field :role, :integer, default: 0 # 0 : not_approved, 1 : participant, 2 : moderator, 3 : administrator, 4 : creator
field :approved, :boolean
belongs_to :event, Event, primary_key: true belongs_to :event, Event, primary_key: true
belongs_to :actor, Actor, primary_key: true belongs_to :actor, Actor, primary_key: true

View file

@ -17,8 +17,8 @@ defmodule EventosWeb.ActivityPubController do
end end
end end
def event(conn, %{"name" => name, "slug" => slug}) do def event(conn, %{"uuid" => uuid}) do
with %Event{} = event <- Events.get_event_full_by_name_and_slug!(name, slug) do with %Event{} = event <- Events.get_event_full_by_uuid(uuid) do
conn conn
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(ObjectView.render("event.json", %{event: event})) |> json(ObjectView.render("event.json", %{event: event}))
@ -83,13 +83,13 @@ defmodule EventosWeb.ActivityPubController do
def inbox(conn, params) do def inbox(conn, params) do
headers = Enum.into(conn.req_headers, %{}) headers = Enum.into(conn.req_headers, %{})
if !String.contains?(headers["signature"] || "", params["actor"]) do if String.contains?(headers["signature"] || "", params["actor"]) do
Logger.info("Signature not from author, relayed message, fetching from source")
ActivityPub.fetch_event_from_url(params["object"]["id"])
else
Logger.info("Signature error") Logger.info("Signature error")
Logger.info("Could not validate #{params["actor"]}") Logger.info("Could not validate #{params["actor"]}")
Logger.info(inspect(conn.req_headers)) Logger.info(inspect(conn.req_headers))
else
Logger.info("Signature not from author, relayed message, fetching from source")
ActivityPub.fetch_event_from_url(params["object"]["id"])
end end
json(conn, "ok") json(conn, "ok")

View file

@ -2,7 +2,7 @@ defmodule EventosWeb.BotController do
use EventosWeb, :controller use EventosWeb, :controller
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Actors.Bot alias Eventos.Actors.{Bot, Actor}
action_fallback EventosWeb.FallbackController action_fallback EventosWeb.FallbackController
@ -12,9 +12,9 @@ defmodule EventosWeb.BotController do
end end
def create(conn, %{"bot" => bot_params}) do def create(conn, %{"bot" => bot_params}) do
with user <- Guardian.Plug.current_resource, with user <- Guardian.Plug.current_resource(conn),
bot_params <- Map.put(bot_params, "user_id", user.id), bot_params <- Map.put(bot_params, "user_id", user.id),
{:ok, actor} <- Actors.register_bot_account(%{name: bot_params["name"], summary: bot_params["summary"]}), %Actor{} = actor <- Actors.register_bot_account(%{name: bot_params["name"], summary: bot_params["summary"]}),
bot_params <- Map.put(bot_params, "actor_id", actor.id), bot_params <- Map.put(bot_params, "actor_id", actor.id),
{:ok, %Bot{} = bot} <- Actors.create_bot(bot_params) do {:ok, %Bot{} = bot} <- Actors.create_bot(bot_params) do
conn conn

View file

@ -20,21 +20,21 @@ defmodule EventosWeb.CommentController do
end end
end end
def show(conn, %{"id" => id}) do def show(conn, %{"uuid" => uuid}) do
comment = Events.get_comment!(id) comment = Events.get_comment_with_uuid!(uuid)
render(conn, "show.json", comment: comment) render(conn, "show.json", comment: comment)
end end
def update(conn, %{"id" => id, "comment" => comment_params}) do def update(conn, %{"uuid" => uuid, "comment" => comment_params}) do
comment = Events.get_comment!(id) comment = Events.get_comment_with_uuid!(uuid)
with {:ok, %Comment{} = comment} <- Events.update_comment(comment, comment_params) do with {:ok, %Comment{} = comment} <- Events.update_comment(comment, comment_params) do
render(conn, "show.json", comment: comment) render(conn, "show.json", comment: comment)
end end
end end
def delete(conn, %{"id" => id}) do def delete(conn, %{"uuid" => uuid}) do
comment = Events.get_comment!(id) comment = Events.get_comment_with_uuid!(uuid)
with {:ok, %Comment{}} <- Events.delete_comment(comment) do with {:ok, %Comment{}} <- Events.delete_comment(comment) do
send_resp(conn, :no_content, "") send_resp(conn, :no_content, "")
end end

View file

@ -9,6 +9,8 @@ defmodule EventosWeb.EventController do
alias Eventos.Export.ICalendar alias Eventos.Export.ICalendar
alias Eventos.Addresses alias Eventos.Addresses
import Logger
action_fallback EventosWeb.FallbackController action_fallback EventosWeb.FallbackController
def index(conn, _params) do def index(conn, _params) do
@ -32,11 +34,7 @@ defmodule EventosWeb.EventController do
end end
defp process_address(address) do defp process_address(address) do
import Logger
Logger.debug("process address")
Logger.debug(inspect address)
geom = EventosWeb.AddressController.process_geom(address["geom"]) geom = EventosWeb.AddressController.process_geom(address["geom"])
Logger.debug(inspect geom)
case geom do case geom do
nil -> nil ->
address address
@ -53,12 +51,16 @@ defmodule EventosWeb.EventController do
end end
def show(conn, %{"uuid" => uuid}) do def show(conn, %{"uuid" => uuid}) do
event = Events.get_event_full_by_uuid(uuid) case Events.get_event_full_by_uuid(uuid) do
render(conn, "show.json", event: event) nil ->
send_resp(conn, 404, "")
event ->
render(conn, "show.json", event: event)
end
end end
def export_to_ics(conn, %{"uuid" => uuid}) do def export_to_ics(conn, %{"uuid" => uuid}) do
event = Events.get_event_full_by_uuid(uuid) |> ICalendar.export_event() event = uuid |> Events.get_event_full_by_uuid() |> ICalendar.export_event()
send_resp(conn, 200, event) send_resp(conn, 200, event)
end end
@ -71,8 +73,8 @@ defmodule EventosWeb.EventController do
end end
def delete(conn, %{"uuid" => uuid}) do def delete(conn, %{"uuid" => uuid}) do
event = Events.get_event_by_uuid(uuid) with event <- Events.get_event_by_uuid(uuid),
with {:ok, %Event{}} <- Events.delete_event(event) do {:ok, %Event{}} <- Events.delete_event(event) do
send_resp(conn, :no_content, "") send_resp(conn, :no_content, "")
end end
end end

View file

@ -28,8 +28,8 @@ defmodule EventosWeb.SessionController do
render(conn, "show.json", session: session) render(conn, "show.json", session: session)
end end
def show_sessions_for_event(conn, %{"id" => event_id}) do def show_sessions_for_event(conn, %{"uuid" => event_uuid}) do
sessions = Events.list_sessions_for_event(event_id) sessions = Events.list_sessions_for_event(event_uuid)
render(conn, "index.json", sessions: sessions) render(conn, "index.json", sessions: sessions)
end end

View file

@ -16,16 +16,11 @@ defmodule EventosWeb.UserController do
end end
def register(conn, %{"username" => username, "email" => email, "password" => password}) do def register(conn, %{"username" => username, "email" => email, "password" => password}) do
case Actors.register(%{email: email, password: password, username: username}) do with {:ok, %User{} = user} <- Actors.register(%{email: email, password: password, username: username}),
{:ok, %User{} = user} -> {:ok, token, _claims} <- EventosWeb.Guardian.encode_and_sign(user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn conn
|> put_status(:created) |> put_status(:created)
|> render("show_with_token.json", %{token: token, user: user}) |> render("show_with_token.json", %{token: token, user: user})
{:error, error} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(400, Poison.encode!(%{"msg" => handle_changeset_errors(error)}))
end end
end end

View file

@ -1,4 +1,10 @@
defmodule EventosWeb.HTTPSignaturePlug do defmodule EventosWeb.HTTPSignaturePlug do
@moduledoc """
# HTTPSignaturePlug
Plug to check HTTP Signatures on every incoming request
"""
alias Eventos.Service.HTTPSignatures alias Eventos.Service.HTTPSignatures
import Plug.Conn import Plug.Conn
require Logger require Logger
@ -13,7 +19,9 @@ defmodule EventosWeb.HTTPSignaturePlug do
def call(conn, _opts) do def call(conn, _opts) do
user = conn.params["actor"] user = conn.params["actor"]
Logger.debug("Checking sig for #{user}") Logger.debug fn ->
"Checking sig for #{user}"
end
with [signature | _] <- get_req_header(conn, "signature") do with [signature | _] <- get_req_header(conn, "signature") do
cond do cond do
signature && String.contains?(signature, user) -> signature && String.contains?(signature, user) ->

View file

@ -44,8 +44,9 @@ defmodule EventosWeb.Router do
get "/events/:uuid/tracks", TrackController, :show_tracks_for_event get "/events/:uuid/tracks", TrackController, :show_tracks_for_event
get "/events/:uuid/sessions", SessionController, :show_sessions_for_event get "/events/:uuid/sessions", SessionController, :show_sessions_for_event
get "/events/:uuid", EventController, :show get "/events/:uuid", EventController, :show
resources "/comments", CommentController, only: [:show] get "/comments/:uuid", CommentController, :show
get "/bots/:id", BotController, :view get "/bots/:id", BotController, :show
get "/bots", BotController, :index
get "/actors", ActorController, :index get "/actors", ActorController, :index
get "/actors/search/:name", ActorController, :search get "/actors/search/:name", ActorController, :search
@ -74,10 +75,13 @@ defmodule EventosWeb.Router do
patch "/events/:uuid", EventController, :update patch "/events/:uuid", EventController, :update
put "/events/:uuid", EventController, :update put "/events/:uuid", EventController, :update
delete "/events/:uuid", EventController, :delete delete "/events/:uuid", EventController, :delete
resources "/comments", CommentController, except: [:new, :edit, :show] post "/comments", CommentController, :create
patch "/comments/:uuid", CommentController, :update
put "/comments/:uuid", CommentController, :update
delete "/comments/:uuid", CommentController, :delete
#post "/events/:id/request", EventRequestController, :create_for_event #post "/events/:id/request", EventRequestController, :create_for_event
resources "/participant", ParticipantController resources "/participant", ParticipantController
resources "/bots", BotController, except: [:new, :edit, :show] resources "/bots", BotController, except: [:new, :edit, :show, :index]
#resources "/requests", EventRequestController #resources "/requests", EventRequestController
post "/groups", GroupController, :create post "/groups", GroupController, :create
post "/groups/:name/join", GroupController, :join post "/groups/:name/join", GroupController, :join
@ -110,7 +114,7 @@ defmodule EventosWeb.Router do
get "/@:name/outbox", ActivityPubController, :outbox get "/@:name/outbox", ActivityPubController, :outbox
get "/@:name/following", ActivityPubController, :following get "/@:name/following", ActivityPubController, :following
get "/@:name/followers", ActivityPubController, :followers get "/@:name/followers", ActivityPubController, :followers
get "/@:name/:slug", ActivityPubController, :event get "/events/:uuid", ActivityPubController, :event
post "/@:name/inbox", ActivityPubController, :inbox post "/@:name/inbox", ActivityPubController, :inbox
post "/inbox", ActivityPubController, :inbox post "/inbox", ActivityPubController, :inbox
end end

View file

@ -13,7 +13,8 @@ defmodule EventosWeb.ActivityPub.ActorView do
import Ecto.Query import Ecto.Query
def render("actor.json", %{actor: actor}) do def render("actor.json", %{actor: actor}) do
{:ok, public_key} = Actor.get_public_key_for_actor(actor) pem = Actor.get_keys_for_actor(actor)
public_key = Eventos.Service.ActivityPub.Utils.pem_to_public_key_pem(pem)
%{ %{
"id" => actor.url, "id" => actor.url,
@ -48,9 +49,9 @@ defmodule EventosWeb.ActivityPub.ActorView do
end end
def render("following.json", %{actor: actor, page: page}) do def render("following.json", %{actor: actor, page: page}) do
following = Actor.get_followings(actor) actor
|> Actor.get_followings()
collection(following, actor.following_url, page) |> collection(actor.following_url, page)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
@ -67,9 +68,9 @@ defmodule EventosWeb.ActivityPub.ActorView do
end end
def render("followers.json", %{actor: actor, page: page}) do def render("followers.json", %{actor: actor, page: page}) do
followers = Actor.get_followers(actor) actor
|> Actor.get_followers()
collection(followers, actor.followers_url, page) |> collection(actor.followers_url, page)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end

View file

@ -12,7 +12,9 @@ defmodule EventosWeb.CommentView do
def render("comment.json", %{comment: comment}) do def render("comment.json", %{comment: comment}) do
%{id: comment.id, %{id: comment.id,
uuid: comment.uuid,
url: comment.url, url: comment.url,
text: comment.text} text: comment.text
}
end end
end end

View file

@ -1,4 +1,8 @@
defmodule Mix.Tasks.CreateBot do defmodule Mix.Tasks.CreateBot do
@moduledoc """
Creates a bot from a source
"""
use Mix.Task use Mix.Task
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Actors.Bot alias Eventos.Actors.Bot

View file

@ -1,4 +1,10 @@
defmodule Eventos.Service.ActivityPub do defmodule Eventos.Service.ActivityPub do
@moduledoc """
# ActivityPub
Every ActivityPub method
"""
alias Eventos.Events alias Eventos.Events
alias Eventos.Events.{Event, Category} alias Eventos.Events.{Event, Category}
alias Eventos.Service.ActivityPub.Transmogrifier alias Eventos.Service.ActivityPub.Transmogrifier
@ -49,8 +55,8 @@ defmodule Eventos.Service.ActivityPub do
url, url,
[Accept: "application/activity+json"], [Accept: "application/activity+json"],
follow_redirect: true, follow_redirect: true,
timeout: 10000, timeout: 10_000,
recv_timeout: 20000 recv_timeout: 20_000
), ),
{:ok, data} <- Jason.decode(body), {:ok, data} <- Jason.decode(body),
nil <- Events.get_event_by_url!(data["id"]), nil <- Events.get_event_by_url!(data["id"]),
@ -253,7 +259,7 @@ defmodule Eventos.Service.ActivityPub do
preferred_username: data["preferredUsername"], preferred_username: data["preferredUsername"],
follower_address: data["followers"], follower_address: data["followers"],
summary: data["summary"], summary: data["summary"],
public_key: data["publicKey"]["publicKeyPem"], keys: data["publicKey"]["publicKeyPem"],
inbox_url: data["inbox"], inbox_url: data["inbox"],
outbox_url: data["outbox"], outbox_url: data["outbox"],
following_url: data["following"], following_url: data["following"],
@ -285,9 +291,7 @@ defmodule Eventos.Service.ActivityPub do
case bot.type do case bot.type do
"ics" -> "ics" ->
{:ok, %HTTPoison.Response{body: body} = _resp} = HTTPoison.get(bot.source) {:ok, %HTTPoison.Response{body: body} = _resp} = HTTPoison.get(bot.source)
ical_events = body ical_events = body |> ExIcal.parse() |> ExIcal.by_range(DateTime.utc_now(), DateTime.utc_now() |> Timex.shift(years: 1))
|> ExIcal.parse()
|> ExIcal.by_range(DateTime.utc_now(), DateTime.utc_now() |> Timex.shift(years: 1))
activities = ical_events activities = ical_events
|> Enum.chunk_every(limit) |> Enum.chunk_every(limit)
|> Enum.at(page - 1) |> Enum.at(page - 1)

View file

@ -201,10 +201,10 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
if object = Object.get_by_ap_id(id), do: {:ok, object}, else: nil if object = Object.get_by_ap_id(id), do: {:ok, object}, else: nil
end end
def set_reply_to_uri(%{"inReplyTo" => inReplyTo} = object) do def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) do
with false <- String.starts_with?(inReplyTo, "http"), with false <- String.starts_with?(in_reply_to, "http"),
{:ok, %{data: replied_to_object}} <- get_obj_helper(inReplyTo) do {:ok, %{data: replied_to_object}} <- get_obj_helper(in_reply_to) do
Map.put(object, "inReplyTo", replied_to_object["external_url"] || inReplyTo) Map.put(object, "inReplyTo", replied_to_object["external_url"] || in_reply_to)
else else
_e -> object _e -> object
end end
@ -332,10 +332,9 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
# end # end
# #
def add_attributed_to(object) do def add_attributed_to(object) do
attributedTo = object["attributedTo"] || object["actor"] attributed_to = object["attributedTo"] || object["actor"]
object object |> Map.put("attributedTo", attributed_to)
|> Map.put("attributedTo", attributedTo)
end end
# #
# def prepare_attachments(object) do # def prepare_attachments(object) do

View file

@ -1,4 +1,10 @@
defmodule Eventos.Service.ActivityPub.Utils do defmodule Eventos.Service.ActivityPub.Utils do
@moduledoc """
# Utils
Various utils
"""
alias Eventos.Repo alias Eventos.Repo
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Actors.Actor alias Eventos.Actors.Actor
@ -304,4 +310,31 @@ defmodule Eventos.Service.ActivityPub.Utils do
} }
|> Map.merge(additional) |> Map.merge(additional)
end end
@doc """
Converts PEM encoded keys to a public key representation
"""
def pem_to_public_key(pem) do
[private_key_code] = :public_key.pem_decode(pem)
private_key = :public_key.pem_entry_decode(private_key_code)
{:RSAPrivateKey, _, modulus, exponent, _, _, _, _, _, _, _} = private_key
{:RSAPublicKey, modulus, exponent}
end
@doc """
Converts PEM encoded keys to a private key representation
"""
def pem_to_private_key(pem) do
[private_key_code] = :public_key.pem_decode(pem)
:public_key.pem_entry_decode(private_key_code)
end
@doc """
Converts PEM encoded keys to a PEM public key representation
"""
def pem_to_public_key_pem(pem) do
public_key = pem_to_public_key(pem)
public_key = :public_key.pem_entry_encode(:RSAPublicKey, public_key)
:public_key.pem_encode([public_key])
end
end end

View file

@ -1,4 +1,8 @@
defmodule Eventos.Service.Federator do defmodule Eventos.Service.Federator do
@moduledoc """
Handle federated activities
"""
use GenServer use GenServer
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Activity alias Eventos.Activity
@ -16,7 +20,7 @@ defmodule Eventos.Service.Federator do
spawn(fn -> spawn(fn ->
# 1 minute # 1 minute
Process.sleep(1000 * 60 * 1) Process.sleep(1000 * 60)
end) end)
GenServer.start_link( GenServer.start_link(
@ -101,7 +105,9 @@ defmodule Eventos.Service.Federator do
end end
def handle_cast(m, state) do def handle_cast(m, state) do
IO.inspect("Unknown: #{inspect(m)}, #{inspect(state)}") Logger.error fn ->
"Unknown: #{inspect(m)}, #{inspect(state)}"
end
{:noreply, state} {:noreply, state}
end end

View file

@ -1,8 +1,14 @@
# https://tools.ietf.org/html/draft-cavage-http-signatures-08 # https://tools.ietf.org/html/draft-cavage-http-signatures-08
defmodule Eventos.Service.HTTPSignatures do defmodule Eventos.Service.HTTPSignatures do
@moduledoc """
# HTTP Signatures
Generates and checks HTTP Signatures
"""
alias Eventos.Actors.Actor alias Eventos.Actors.Actor
alias Eventos.Service.ActivityPub alias Eventos.Service.ActivityPub
require Logger import Logger
def split_signature(sig) do def split_signature(sig) do
default = %{"headers" => "date"} default = %{"headers" => "date"}
@ -22,8 +28,12 @@ defmodule Eventos.Service.HTTPSignatures do
def validate(headers, signature, public_key) do def validate(headers, signature, public_key) do
sigstring = build_signing_string(headers, signature["headers"]) sigstring = build_signing_string(headers, signature["headers"])
Logger.debug("Signature: #{signature["signature"]}") Logger.debug fn ->
Logger.debug("Sigstring: #{sigstring}") "Signature: #{signature["signature"]}"
end
Logger.debug fn ->
"Sigstring: #{sigstring}"
end
{:ok, sig} = Base.decode64(signature["signature"]) {:ok, sig} = Base.decode64(signature["signature"])
:public_key.verify(sigstring, :sha256, sig, public_key) :public_key.verify(sigstring, :sha256, sig, public_key)
end end
@ -32,7 +42,7 @@ defmodule Eventos.Service.HTTPSignatures do
# TODO: How to get the right key and see if it is actually valid for that request. # TODO: How to get the right key and see if it is actually valid for that request.
# For now, fetch the key for the actor. # For now, fetch the key for the actor.
with actor_id <- conn.params["actor"], with actor_id <- conn.params["actor"],
{:ok, public_key_code} <- Actor.get_public_key_for_url(actor_id), public_key_code <- Actor.get_public_key_for_url(actor_id),
[public_key] = :public_key.pem_decode(public_key_code), [public_key] = :public_key.pem_decode(public_key_code),
public_key = :public_key.pem_entry_decode(public_key) do public_key = :public_key.pem_entry_decode(public_key) do
if validate_conn(conn, public_key) do if validate_conn(conn, public_key) do
@ -42,7 +52,7 @@ defmodule Eventos.Service.HTTPSignatures do
# Fetch user anew and try one more time # Fetch user anew and try one more time
with actor_id <- conn.params["actor"], with actor_id <- conn.params["actor"],
{:ok, _actor} <- ActivityPub.make_actor_from_url(actor_id), {:ok, _actor} <- ActivityPub.make_actor_from_url(actor_id),
{:ok, public_key_code} <- Actor.get_public_key_for_url(actor_id), public_key_code <- Actor.get_public_key_for_url(actor_id),
[public_key] = :public_key.pem_decode(public_key_code), [public_key] = :public_key.pem_decode(public_key_code),
public_key = :public_key.pem_entry_decode(public_key) do public_key = :public_key.pem_entry_decode(public_key) do
validate_conn(conn, public_key) validate_conn(conn, public_key)
@ -70,20 +80,16 @@ defmodule Eventos.Service.HTTPSignatures do
|> Enum.join("\n") |> Enum.join("\n")
end end
def sign(actor, headers) do def sign(%Actor{} = actor, headers) do
with {:ok, private_key_code} = Actor.get_private_key_for_actor(actor), with private_key = Actor.get_keys_for_actor(actor) do
[private_key] = :public_key.pem_decode(private_key_code),
private_key = :public_key.pem_entry_decode(private_key) do
sigstring = build_signing_string(headers, Map.keys(headers)) sigstring = build_signing_string(headers, Map.keys(headers))
signature = signature = sigstring |> :public_key.sign(:sha256, private_key) |> Base.encode64()
:public_key.sign(sigstring, :sha256, private_key)
|> Base.encode64()
[ [
keyId: actor.url <> "#main-key", keyId: actor.url <> "#main-key",
algorithm: "rsa-sha256", algorithm: "rsa-sha256",
headers: Map.keys(headers) |> Enum.join(" "), headers: headers |> Map.keys() |> Enum.join(" "),
signature: signature signature: signature
] ]
|> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end) |> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end)

View file

@ -1,4 +1,10 @@
defmodule Eventos.Service.Streamer do defmodule Eventos.Service.Streamer do
@moduledoc """
# Streamer
Handles streaming activities
"""
use GenServer use GenServer
require Logger require Logger
alias Eventos.Accounts.Actor alias Eventos.Accounts.Actor
@ -30,7 +36,8 @@ defmodule Eventos.Service.Streamer do
end end
def handle_cast(%{action: :ping}, topics) do def handle_cast(%{action: :ping}, topics) do
Map.values(topics) topics
|> Map.values()
|> List.flatten() |> List.flatten()
|> Enum.each(fn socket -> |> Enum.each(fn socket ->
Logger.debug("Sending keepalive ping") Logger.debug("Sending keepalive ping")
@ -51,7 +58,9 @@ defmodule Eventos.Service.Streamer do
sockets_for_topic = sockets[topic] || [] sockets_for_topic = sockets[topic] || []
sockets_for_topic = Enum.uniq([socket | sockets_for_topic]) sockets_for_topic = Enum.uniq([socket | sockets_for_topic])
sockets = Map.put(sockets, topic, sockets_for_topic) sockets = Map.put(sockets, topic, sockets_for_topic)
Logger.debug("Got new conn for #{topic}") Logger.debug fn ->
"Got new conn for #{topic}"
end
{:noreply, sockets} {:noreply, sockets}
end end
@ -60,7 +69,9 @@ defmodule Eventos.Service.Streamer do
sockets_for_topic = sockets[topic] || [] sockets_for_topic = sockets[topic] || []
sockets_for_topic = List.delete(sockets_for_topic, socket) sockets_for_topic = List.delete(sockets_for_topic, socket)
sockets = Map.put(sockets, topic, sockets_for_topic) sockets = Map.put(sockets, topic, sockets_for_topic)
Logger.debug("Removed conn for #{topic}") Logger.debug fn ->
"Removed conn for #{topic}"
end
{:noreply, sockets} {:noreply, sockets}
end end

View file

@ -1,4 +1,9 @@
defmodule Eventos.Service.WebFinger do defmodule Eventos.Service.WebFinger do
@moduledoc """
# WebFinger
Performs the WebFinger requests and responses (json only)
"""
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Service.XmlBuilder alias Eventos.Service.XmlBuilder
@ -59,7 +64,9 @@ defmodule Eventos.Service.WebFinger do
{"application/activity+json", "self"} -> {"application/activity+json", "self"} ->
Map.put(data, "url", link["href"]) Map.put(data, "url", link["href"])
_ -> _ ->
Logger.debug("Unhandled type: #{inspect(link["type"])}") Logger.debug fn ->
"Unhandled type: #{inspect(link["type"])}"
end
data data
end end
end) end)
@ -81,7 +88,7 @@ defmodule Eventos.Service.WebFinger do
address = "http://#{domain}/.well-known/webfinger?resource=acct:#{actor}" address = "http://#{domain}/.well-known/webfinger?resource=acct:#{actor}"
Logger.debug(inspect address) Logger.debug(inspect address)
with {:ok, %HTTPoison.Response{} = response} <- HTTPoison.get(address, [Accept: "application/json, application/activity+json, application/jrd+json"],follow_redirect: true), with {:ok, %HTTPoison.Response{} = response} <- HTTPoison.get(address, [Accept: "application/json, application/activity+json, application/jrd+json"], follow_redirect: true),
%{status_code: status_code, body: body} when status_code in 200..299 <- response do %{status_code: status_code, body: body} when status_code in 200..299 <- response do
{:ok, doc} = Jason.decode(body) {:ok, doc} = Jason.decode(body)
webfinger_from_json(doc) webfinger_from_json(doc)

View file

@ -1,4 +1,10 @@
defmodule Eventos.Service.XmlBuilder do defmodule Eventos.Service.XmlBuilder do
@moduledoc """
XML Builder.
Do we still need this ? Only for xrd ?
"""
def to_xml({tag, attributes, content}) do def to_xml({tag, attributes, content}) do
open_tag = make_open_tag(tag, attributes) open_tag = make_open_tag(tag, attributes)

View file

@ -0,0 +1,15 @@
defmodule Eventos.Repo.Migrations.AddUUIDToComments do
use Ecto.Migration
def up do
alter table(:comments) do
add :uuid, :uuid
end
end
def down do
alter table(:comments) do
remove :uuid
end
end
end

View file

@ -0,0 +1,15 @@
defmodule Eventos.Repo.Migrations.MakeSharedInboxUrlNullable do
use Ecto.Migration
def up do
alter table(:actors) do
modify :shared_inbox_url, :string, null: true, default: nil
end
end
def down do
alter table(:actors) do
add :shared_inbox_url, :string, null: false, default: ""
end
end
end

View file

@ -0,0 +1,17 @@
defmodule Eventos.Repo.Migrations.FusionPublicPrivateKeyIntoKeysColumn do
use Ecto.Migration
def up do
rename table(:actors), :private_key, to: :keys
alter table(:actors) do
remove :public_key
end
end
def down do
alter table(:actors) do
rename :keys, to: :private_key
add :public_key, :text, null: true
end
end
end

View file

@ -0,0 +1,23 @@
defmodule Eventos.Repo.Migrations.AddAttributedToFieldToEventsAndComments do
use Ecto.Migration
def up do
alter table(:events) do
add :attributed_to_id, references(:actors, on_delete: :nothing)
end
alter table(:comments) do
add :attributed_to_id, references(:actors, on_delete: :nothing)
end
end
def down do
alter table(:events) do
remove :attributed_to_id
end
alter table(:comments) do
remove :attributed_to_id
end
end
end

View file

@ -1,14 +1,15 @@
defmodule Eventos.ActorsTest do defmodule Eventos.ActorsTest do
use Eventos.DataCase use Eventos.DataCase
import Eventos.Factory
alias Eventos.Actors alias Eventos.Actors
describe "actors" do describe "actors" do
alias Eventos.Actors.Actor alias Eventos.Actors.Actor
@valid_attrs %{description: "some description", display_name: "some display_name", domain: "some domain", private_key: "some private_key", public_key: "some public_key", suspended: true, uri: "some uri", url: "some url", username: "some username"} @valid_attrs %{summary: "some description", name: "some name", domain: "some domain", keys: "some keypair", suspended: true, uri: "some uri", url: "some url", preferred_username: "some username"}
@update_attrs %{description: "some updated description", display_name: "some updated display_name", domain: "some updated domain", private_key: "some updated private_key", public_key: "some updated public_key", suspended: false, uri: "some updated uri", url: "some updated url", username: "some updated username"} @update_attrs %{summary: "some updated description", name: "some updated name", domain: "some updated domain", keys: "some updated keys", suspended: false, uri: "some updated uri", url: "some updated url", preferred_username: "some updated username"}
@invalid_attrs %{description: nil, display_name: nil, domain: nil, private_key: nil, public_key: nil, suspended: nil, uri: nil, url: nil, username: nil} @invalid_attrs %{summary: nil, name: nil, domain: nil, keys: nil, suspended: nil, uri: nil, url: nil, preferred_username: nil}
def actor_fixture(attrs \\ %{}) do def actor_fixture(attrs \\ %{}) do
{:ok, actor} = {:ok, actor} =
@ -31,15 +32,13 @@ defmodule Eventos.ActorsTest do
test "create_actor/1 with valid data creates a actor" do test "create_actor/1 with valid data creates a actor" do
assert {:ok, %Actor{} = actor} = Actors.create_actor(@valid_attrs) assert {:ok, %Actor{} = actor} = Actors.create_actor(@valid_attrs)
assert actor.description == "some description" assert actor.summary == "some description"
assert actor.display_name == "some display_name" assert actor.name == "some name"
assert actor.domain == "some domain" assert actor.domain == "some domain"
assert actor.private_key == "some private_key" assert actor.keys == "some keypair"
assert actor.public_key == "some public_key"
assert actor.suspended assert actor.suspended
assert actor.uri == "some uri"
assert actor.url == "some url" assert actor.url == "some url"
assert actor.username == "some username" assert actor.preferred_username == "some username"
end end
test "create_actor/1 with invalid data returns error changeset" do test "create_actor/1 with invalid data returns error changeset" do
@ -50,15 +49,13 @@ defmodule Eventos.ActorsTest do
actor = actor_fixture() actor = actor_fixture()
assert {:ok, actor} = Actors.update_actor(actor, @update_attrs) assert {:ok, actor} = Actors.update_actor(actor, @update_attrs)
assert %Actor{} = actor assert %Actor{} = actor
assert actor.description == "some updated description" assert actor.summary == "some updated description"
assert actor.display_name == "some updated display_name" assert actor.name == "some updated name"
assert actor.domain == "some updated domain" assert actor.domain == "some updated domain"
assert actor.private_key == "some updated private_key" assert actor.keys == "some updated keys"
assert actor.public_key == "some updated public_key"
refute actor.suspended refute actor.suspended
assert actor.uri == "some updated uri"
assert actor.url == "some updated url" assert actor.url == "some updated url"
assert actor.username == "some updated username" assert actor.preferred_username == "some updated username"
end end
test "update_actor/2 with invalid data returns error changeset" do test "update_actor/2 with invalid data returns error changeset" do
@ -82,7 +79,7 @@ defmodule Eventos.ActorsTest do
describe "users" do describe "users" do
alias Eventos.Actors.{User, Actor} alias Eventos.Actors.{User, Actor}
@actor_valid_attrs %{description: "some description", display_name: "some display_name", domain: "some domain", private_key: "some private_key", public_key: "some public_key", suspended: true, uri: "some uri", url: "some url", username: "some username"} @actor_valid_attrs %{description: "some description", display_name: "some display_name", domain: "some domain", keys: "some keys", suspended: true, uri: "some uri", url: "some url", preferred_username: "some username"}
@valid_attrs %{email: "foo@bar.tld", password_hash: "some password_hash", role: 42} @valid_attrs %{email: "foo@bar.tld", password_hash: "some password_hash", role: 42}
@update_attrs %{email: "foo@fighters.tld", password_hash: "some updated password_hash", role: 43} @update_attrs %{email: "foo@fighters.tld", password_hash: "some updated password_hash", role: 43}
@invalid_attrs %{email: nil, password_hash: nil, role: nil} @invalid_attrs %{email: nil, password_hash: nil, role: nil}
@ -182,26 +179,26 @@ defmodule Eventos.ActorsTest do
@invalid_attrs %{source: nil, type: nil} @invalid_attrs %{source: nil, type: nil}
def bot_fixture(attrs \\ %{}) do def bot_fixture(attrs \\ %{}) do
{:ok, bot} = insert(:bot)
attrs
|> Enum.into(@valid_attrs)
|> Actors.create_bot()
bot
end end
test "list_bots/0 returns all bots" do test "list_bots/0 returns all bots" do
bot = bot_fixture() bot = bot_fixture()
assert Actors.list_bots() == [bot] bots = Actors.list_bots()
assert bots = [bot]
end end
test "get_bot!/1 returns the bot with given id" do test "get_bot!/1 returns the bot with given id" do
bot = bot_fixture() bot = bot_fixture()
assert Actors.get_bot!(bot.id) == bot bot_fetched = Actors.get_bot!(bot.id)
assert bot_fetched = bot
end end
test "create_bot/1 with valid data creates a bot" do test "create_bot/1 with valid data creates a bot" do
assert {:ok, %Bot{} = bot} = Actors.create_bot(@valid_attrs) attrs = @valid_attrs
|> Map.merge(%{actor_id: insert(:actor).id})
|> Map.merge(%{user_id: insert(:user).id})
assert {:ok, %Bot{} = bot} = Actors.create_bot(attrs)
assert bot.source == "some source" assert bot.source == "some source"
assert bot.type == "some type" assert bot.type == "some type"
end end
@ -221,7 +218,8 @@ defmodule Eventos.ActorsTest do
test "update_bot/2 with invalid data returns error changeset" do test "update_bot/2 with invalid data returns error changeset" do
bot = bot_fixture() bot = bot_fixture()
assert {:error, %Ecto.Changeset{}} = Actors.update_bot(bot, @invalid_attrs) assert {:error, %Ecto.Changeset{}} = Actors.update_bot(bot, @invalid_attrs)
assert bot == Actors.get_bot!(bot.id) bot_fetched = Actors.get_bot!(bot.id)
assert bot = bot_fetched
end end
test "delete_bot/1 deletes the bot" do test "delete_bot/1 deletes the bot" do

View file

@ -4,13 +4,12 @@ defmodule Eventos.EventsTest do
import Eventos.Factory import Eventos.Factory
alias Eventos.Events alias Eventos.Events
alias Eventos.Accounts alias Eventos.Actors
@account_valid_attrs %{description: "some description", display_name: "some display_name", domain: "some domain", private_key: "some private_key", public_key: "some public_key", suspended: true, uri: "some uri", url: "some url", username: "some username"}
@event_valid_attrs %{begins_on: "2010-04-17 14:00:00.000000Z", description: "some description", ends_on: "2010-04-17 14:00:00.000000Z", title: "some title"} @event_valid_attrs %{begins_on: "2010-04-17 14:00:00.000000Z", description: "some description", ends_on: "2010-04-17 14:00:00.000000Z", title: "some title"}
def account_fixture do def actor_fixture do
insert(:account) insert(:actor)
end end
def address_fixture do def address_fixture do
@ -28,7 +27,6 @@ defmodule Eventos.EventsTest do
describe "events" do describe "events" do
alias Eventos.Events.Event alias Eventos.Events.Event
@account_valid_attrs %{description: "some description", display_name: "some display_name", domain: "some domain", private_key: "some private_key", public_key: "some public_key", suspended: true, uri: "some uri", url: "some url", username: "some username"}
@valid_attrs %{begins_on: "2010-04-17 14:00:00.000000Z", description: "some description", ends_on: "2010-04-17 14:00:00.000000Z", title: "some title"} @valid_attrs %{begins_on: "2010-04-17 14:00:00.000000Z", description: "some description", ends_on: "2010-04-17 14:00:00.000000Z", title: "some title"}
@update_attrs %{begins_on: "2011-05-18 15:01:01.000000Z", description: "some updated description", ends_on: "2011-05-18 15:01:01.000000Z", title: "some updated title"} @update_attrs %{begins_on: "2011-05-18 15:01:01.000000Z", description: "some updated description", ends_on: "2011-05-18 15:01:01.000000Z", title: "some updated title"}
@invalid_attrs %{begins_on: nil, description: nil, ends_on: nil, title: nil} @invalid_attrs %{begins_on: nil, description: nil, ends_on: nil, title: nil}
@ -44,11 +42,12 @@ defmodule Eventos.EventsTest do
end end
test "create_event/1 with valid data creates a event" do test "create_event/1 with valid data creates a event" do
{:ok, account} = Accounts.create_account(@account_valid_attrs) actor = actor_fixture()
category = category_fixture() category = category_fixture()
address = address_fixture() address = address_fixture()
valid_attrs = Map.put(@event_valid_attrs, :organizer_account_id, account.id) valid_attrs = @event_valid_attrs
valid_attrs = valid_attrs |> Map.put(:organizer_actor, actor)
|> Map.put(:organizer_actor_id, actor.id)
|> Map.put(:category_id, category.id) |> Map.put(:category_id, category.id)
|> Map.put(:address_id, address.id) |> Map.put(:address_id, address.id)
assert {:ok, %Event{} = event} = Events.create_event(valid_attrs) assert {:ok, %Event{} = event} = Events.create_event(valid_attrs)
@ -90,68 +89,6 @@ defmodule Eventos.EventsTest do
end end
end end
describe "event_requests" do
alias Eventos.Events.Request
@valid_attrs %{state: 42}
@update_attrs %{state: 43}
@invalid_attrs %{state: nil}
def event_request_fixture(attrs \\ %{}) do
event = event_fixture()
valid_attrs = Map.put(@valid_attrs, :event_id, event.id)
{:ok, event_request} =
attrs
|> Enum.into(valid_attrs)
|> Events.create_request()
event_request
end
test "list_event_requests/0 returns all event_requests" do
event_request = event_request_fixture()
assert Events.list_requests() == [event_request]
end
test "get_request!/1 returns the event_request with given id" do
event_request = event_request_fixture()
assert Events.get_request!(event_request.id) == event_request
end
test "create_request/1 with valid data creates a event_request" do
assert {:ok, %Request{} = event_request} = Events.create_request(@valid_attrs)
assert event_request.state == 42
end
test "create_request/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Events.create_request(@invalid_attrs)
end
test "update_event_request/2 with valid data updates the event_request" do
event_request = event_request_fixture()
assert {:ok, event_request} = Events.update_request(event_request, @update_attrs)
assert %Request{} = event_request
assert event_request.state == 43
end
test "update_event_request/2 with invalid data returns error changeset" do
event_request = event_request_fixture()
assert {:error, %Ecto.Changeset{}} = Events.update_request(event_request, @invalid_attrs)
assert event_request == Events.get_request!(event_request.id)
end
test "delete_event_request/1 deletes the event_request" do
event_request = event_request_fixture()
assert {:ok, %Request{}} = Events.delete_request(event_request)
assert_raise Ecto.NoResultsError, fn -> Events.get_request!(event_request.id) end
end
test "change_event_request/1 returns a event_request changeset" do
event_request = event_request_fixture()
assert %Ecto.Changeset{} = Events.change_request(event_request)
end
end
describe "categories" do describe "categories" do
alias Eventos.Events.Category alias Eventos.Events.Category
@ -276,9 +213,9 @@ defmodule Eventos.EventsTest do
def participant_fixture(attrs \\ %{}) do def participant_fixture(attrs \\ %{}) do
event = event_fixture() event = event_fixture()
account = account_fixture() actor = actor_fixture()
valid_attrs = Map.put(@valid_attrs, :event_id, event.id) valid_attrs = Map.put(@valid_attrs, :event_id, event.id)
valid_attrs = Map.put(valid_attrs, :account_id, account.id) valid_attrs = Map.put(valid_attrs, :actor_id, actor.id)
{:ok, participant} = {:ok, participant} =
attrs attrs
|> Enum.into(valid_attrs) |> Enum.into(valid_attrs)
@ -298,10 +235,10 @@ defmodule Eventos.EventsTest do
# end # end
test "create_participant/1 with valid data creates a participant" do test "create_participant/1 with valid data creates a participant" do
account = account_fixture() actor = actor_fixture()
event = event_fixture() event = event_fixture()
valid_attrs = Map.put(@valid_attrs, :event_id, event.id) valid_attrs = Map.put(@valid_attrs, :event_id, event.id)
valid_attrs = Map.put(valid_attrs, :account_id, account.id) valid_attrs = Map.put(valid_attrs, :actor_id, actor.id)
assert {:ok, %Participant{} = participant} = Events.create_participant(valid_attrs) assert {:ok, %Participant{} = participant} = Events.create_participant(valid_attrs)
assert participant.role == 42 assert participant.role == 42
end end
@ -333,68 +270,6 @@ defmodule Eventos.EventsTest do
end end
end end
describe "requests" do
alias Eventos.Events.Request
@valid_attrs %{state: 42}
@update_attrs %{state: 43}
@invalid_attrs %{state: nil}
def request_fixture(attrs \\ %{}) do
event = event_fixture()
valid_attrs = Map.put(@valid_attrs, :event_id, event.id)
{:ok, request} =
attrs
|> Enum.into(valid_attrs)
|> Events.create_request()
request
end
test "list_requests/0 returns all requests" do
request = request_fixture()
assert Events.list_requests() == [request]
end
test "get_request!/1 returns the request with given id" do
request = request_fixture()
assert Events.get_request!(request.id) == request
end
test "create_request/1 with valid data creates a request" do
assert {:ok, %Request{} = request} = Events.create_request(@valid_attrs)
assert request.state == 42
end
test "create_request/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Events.create_request(@invalid_attrs)
end
test "update_request/2 with valid data updates the request" do
request = request_fixture()
assert {:ok, request} = Events.update_request(request, @update_attrs)
assert %Request{} = request
assert request.state == 43
end
test "update_request/2 with invalid data returns error changeset" do
request = request_fixture()
assert {:error, %Ecto.Changeset{}} = Events.update_request(request, @invalid_attrs)
assert request == Events.get_request!(request.id)
end
test "delete_request/1 deletes the request" do
request = request_fixture()
assert {:ok, %Request{}} = Events.delete_request(request)
assert_raise Ecto.NoResultsError, fn -> Events.get_request!(request.id) end
end
test "change_request/1 returns a request changeset" do
request = request_fixture()
assert %Ecto.Changeset{} = Events.change_request(request)
end
end
describe "sessions" do describe "sessions" do
alias Eventos.Events.Session alias Eventos.Events.Session
@ -544,33 +419,32 @@ defmodule Eventos.EventsTest do
describe "comments" do describe "comments" do
alias Eventos.Events.Comment alias Eventos.Events.Comment
@valid_attrs %{text: "some text", url: "some url"} @valid_attrs %{text: "some text"}
@update_attrs %{text: "some updated text", url: "some updated url"} @update_attrs %{text: "some updated text"}
@invalid_attrs %{text: nil, url: nil} @invalid_attrs %{text: nil, url: nil}
def comment_fixture(attrs \\ %{}) do def comment_fixture() do
{:ok, comment} = insert(:comment)
attrs
|> Enum.into(@valid_attrs)
|> Events.create_comment()
comment
end end
test "list_comments/0 returns all comments" do test "list_comments/0 returns all comments" do
comment = comment_fixture() comment = comment_fixture()
assert Events.list_comments() == [comment] comments = Events.list_comments()
assert comments = [comment]
end end
test "get_comment!/1 returns the comment with given id" do test "get_comment!/1 returns the comment with given id" do
comment = comment_fixture() comment = comment_fixture()
assert Events.get_comment!(comment.id) == comment comment_fetched = Events.get_comment!(comment.id)
assert comment_fetched = comment
end end
test "create_comment/1 with valid data creates a comment" do test "create_comment/1 with valid data creates a comment" do
assert {:ok, %Comment{} = comment} = Events.create_comment(@valid_attrs) actor = actor_fixture()
comment_data = Map.merge(@valid_attrs, %{actor_id: actor.id})
assert {:ok, %Comment{} = comment} = Events.create_comment(comment_data)
assert comment.text == "some text" assert comment.text == "some text"
assert comment.url == "some url" assert comment.actor_id == actor.id
end end
test "create_comment/1 with invalid data returns error changeset" do test "create_comment/1 with invalid data returns error changeset" do
@ -582,13 +456,13 @@ defmodule Eventos.EventsTest do
assert {:ok, comment} = Events.update_comment(comment, @update_attrs) assert {:ok, comment} = Events.update_comment(comment, @update_attrs)
assert %Comment{} = comment assert %Comment{} = comment
assert comment.text == "some updated text" assert comment.text == "some updated text"
assert comment.url == "some updated url"
end end
test "update_comment/2 with invalid data returns error changeset" do test "update_comment/2 with invalid data returns error changeset" do
comment = comment_fixture() comment = comment_fixture()
assert {:error, %Ecto.Changeset{}} = Events.update_comment(comment, @invalid_attrs) assert {:error, %Ecto.Changeset{}} = Events.update_comment(comment, @invalid_attrs)
assert comment == Events.get_comment!(comment.id) comment_fetched = Events.get_comment!(comment.id)
assert comment = comment_fetched
end end
test "delete_comment/1 deletes the comment" do test "delete_comment/1 deletes the comment" do

View file

@ -5,30 +5,30 @@ defmodule Eventos.Service.Activitypub.ActivitypubTest do
import Eventos.Factory import Eventos.Factory
alias Eventos.Events alias Eventos.Events
alias Eventos.Accounts.Account alias Eventos.Actors.Actor
alias Eventos.Service.ActivityPub alias Eventos.Service.ActivityPub
alias Eventos.Activity alias Eventos.Activity
describe "fetching account from it's url" do describe "fetching actor from it's url" do
test "returns an account" do test "returns an actor" do
assert {:ok, %Account{username: "tcit@framapiaf.org"} = account} = ActivityPub.make_account_from_nickname("tcit@framapiaf.org") assert {:ok, %Actor{preferred_username: "tcit", domain: "framapiaf.org"} = actor} = ActivityPub.make_actor_from_nickname("tcit@framapiaf.org")
end end
end end
describe "create activities" do describe "create activities" do
test "removes doubled 'to' recipients" do test "removes doubled 'to' recipients" do
account = insert(:account) actor = insert(:actor)
{:ok, activity} = {:ok, activity} =
ActivityPub.create(%{ ActivityPub.create(%{
to: ["user1", "user1", "user2"], to: ["user1", "user1", "user2"],
actor: account, actor: actor,
context: "", context: "",
object: %{} object: %{}
}) })
assert activity.data["to"] == ["user1", "user2"] assert activity.data["to"] == ["user1", "user2"]
assert activity.actor == account.url assert activity.actor == actor.url
assert activity.recipients == ["user1", "user2"] assert activity.recipients == ["user1", "user2"]
end end
end end
@ -52,7 +52,7 @@ defmodule Eventos.Service.Activitypub.ActivitypubTest do
{:ok, delete} = ActivityPub.delete(event) {:ok, delete} = ActivityPub.delete(event)
assert delete.data["type"] == "Delete" assert delete.data["type"] == "Delete"
assert delete.data["actor"] == event.organizer_account.url assert delete.data["actor"] == event.organizer_actor.url
assert delete.data["object"] == event.url assert delete.data["object"] == event.url
assert Events.get_event_by_url!(event.url) == nil assert Events.get_event_by_url!(event.url) == nil
@ -60,22 +60,22 @@ defmodule Eventos.Service.Activitypub.ActivitypubTest do
end end
describe "update" do describe "update" do
test "it creates an update activity with the new user data" do test "it creates an update activity with the new actor data" do
account = insert(:account) actor = insert(:actor)
account_data = EventosWeb.ActivityPub.UserView.render("account.json", %{account: account}) actor_data = EventosWeb.ActivityPub.ActorView.render("actor.json", %{actor: actor})
{:ok, update} = {:ok, update} =
ActivityPub.update(%{ ActivityPub.update(%{
actor: account_data["url"], actor: actor_data["url"],
to: [account.url <> "/followers"], to: [actor.url <> "/followers"],
cc: [], cc: [],
object: account_data object: actor_data
}) })
assert update.data["actor"] == account.url assert update.data["actor"] == actor.url
assert update.data["to"] == [account.url <> "/followers"] assert update.data["to"] == [actor.url <> "/followers"]
assert update.data["object"]["id"] == account_data["id"] assert update.data["object"]["id"] == actor_data["id"]
assert update.data["object"]["type"] == account_data["type"] assert update.data["object"]["type"] == actor_data["type"]
end end
end end
end end

View file

@ -13,46 +13,46 @@ defmodule Eventos.Service.WebFingerTest do
describe "incoming webfinger request" do describe "incoming webfinger request" do
test "works for fqns" do test "works for fqns" do
account = insert(:account) actor = insert(:actor)
{:ok, result} = {:ok, result} =
WebFinger.webfinger("#{account.username}@#{EventosWeb.Endpoint.host()}", "JSON") WebFinger.webfinger("#{actor.preferred_username}@#{EventosWeb.Endpoint.host()}", "JSON")
assert is_map(result) assert is_map(result)
end end
test "works for urls" do test "works for urls" do
account = insert(:account) actor = insert(:actor)
{:ok, result} = WebFinger.webfinger(account.url, "JSON") {:ok, result} = WebFinger.webfinger(actor.url, "JSON")
assert is_map(result) assert is_map(result)
end end
end end
describe "fingering" do describe "fingering" do
test "a mastodon account" do test "a mastodon actor" do
account = "tcit@social.tcit.fr" actor = "tcit@social.tcit.fr"
assert {:ok, %{"subject" => "acct:" <> account, "url" => "https://social.tcit.fr/users/tcit"}} = WebFinger.finger(account) assert {:ok, %{"subject" => "acct:" <> actor, "url" => "https://social.tcit.fr/users/tcit"}} = WebFinger.finger(actor)
end end
test "a pleroma account" do test "a pleroma actor" do
account = "@lain@pleroma.soykaf.com" actor = "@lain@pleroma.soykaf.com"
assert {:ok, %{"subject" => "acct:" <> account, "url" => "https://pleroma.soykaf.com/users/lain"}} = WebFinger.finger(account) assert {:ok, %{"subject" => "acct:" <> actor, "url" => "https://pleroma.soykaf.com/users/lain"}} = WebFinger.finger(actor)
end end
test "a peertube account" do test "a peertube actor" do
account = "framasoft@framatube.org" actor = "framasoft@framatube.org"
assert {:ok, %{"subject" => "acct:" <> account, "url" => "https://framatube.org/accounts/framasoft"}} = WebFinger.finger(account) assert {:ok, %{"subject" => "acct:" <> actor, "url" => "https://framatube.org/accounts/framasoft"}} = WebFinger.finger(actor)
end end
test "a friendica account" do test "a friendica actor" do
# Hasn't any ActivityPub # Hasn't any ActivityPub
account = "lain@squeet.me" actor = "lain@squeet.me"
assert {:ok, %{"subject" => "acct:" <> account} = data} = WebFinger.finger(account) assert {:ok, %{"subject" => "acct:" <> actor} = data} = WebFinger.finger(actor)
refute Map.has_key?(data, "url") refute Map.has_key?(data, "url")
end end
end end

View file

@ -1,60 +0,0 @@
defmodule EventosWeb.AccountControllerTest do
use EventosWeb.ConnCase
import Eventos.Factory
alias Eventos.Accounts
@create_attrs %{description: "some description", display_name: "some display_name", domain: "some domain", private_key: "some private_key", public_key: "some public_key", suspended: true, uri: "some uri", url: "some url", username: "some username"}
def fixture(:account) do
{:ok, account} = Accounts.create_account(@create_attrs)
account
end
setup %{conn: conn} do
account = insert(:account)
user = insert(:user, account: account)
{:ok, conn: conn, user: user}
end
describe "index" do
test "lists all accounts", %{conn: conn, user: user} do
conn = get conn, account_path(conn, :index)
assert hd(json_response(conn, 200)["data"])["username"] == user.account.username
end
end
describe "delete account" do
setup [:create_account]
test "deletes own account", %{conn: conn, user: user} do
conn = auth_conn(conn, user)
conn = delete conn, account_path(conn, :delete, user.account)
assert response(conn, 204)
assert_error_sent 404, fn ->
get conn, account_path(conn, :show, user.account)
end
end
test "deletes other account", %{conn: conn, account: account, user: user} do
conn = auth_conn(conn, user)
conn = delete conn, account_path(conn, :delete, account)
assert response(conn, 401)
conn = get conn, account_path(conn, :show, account)
assert response(conn, 200)
end
end
defp create_account(_) do
account = fixture(:account)
{:ok, account: account}
end
defp auth_conn(conn, %Eventos.Accounts.User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> put_req_header("authorization", "Bearer #{token}")
|> put_req_header("accept", "application/json")
end
end

View file

@ -1,44 +1,42 @@
defmodule EventosWeb.ActivityPubControllerTest do defmodule EventosWeb.ActivityPubControllerTest do
use EventosWeb.ConnCase use EventosWeb.ConnCase
import Eventos.Factory import Eventos.Factory
alias EventosWeb.ActivityPub.{AccountView, ObjectView} alias EventosWeb.ActivityPub.{ActorView, ObjectView}
alias Eventos.{Repo, Accounts, Accounts.Account} alias Eventos.{Repo, Actors, Actors.Actor}
alias Eventos.Activity alias Eventos.Activity
import Logger import Logger
describe "/@:username" do describe "/@:username" do
test "it returns a json representation of the account", %{conn: conn} do test "it returns a json representation of the actor", %{conn: conn} do
account = insert(:account) actor = insert(:actor)
conn = conn =
conn conn
|> put_req_header("accept", "application/activity+json") |> put_req_header("accept", "application/activity+json")
|> get("/@#{account.username}") |> get("/@#{actor.preferred_username}")
account = Accounts.get_account!(account.id) actor = Actors.get_actor!(actor.id)
assert json_response(conn, 200) == AccountView.render("account.json", %{account: account}) assert json_response(conn, 200) == ActorView.render("actor.json", %{actor: actor})
Logger.error(inspect AccountView.render("account.json", %{account: account})) Logger.error(inspect ActorView.render("actor.json", %{actor: actor}))
end end
end end
describe "/@username/slug" do describe "/events/uuid" do
test "it returns a json representation of the object", %{conn: conn} do test "it returns a json representation of the object", %{conn: conn} do
event = insert(:event) event = insert(:event)
{slug, parts} = List.pop_at(String.split(event.url, "/"), -1)
"@" <> username = List.last(parts)
conn = conn =
conn conn
|> put_req_header("accept", "application/activity+json") |> put_req_header("accept", "application/activity+json")
|> get("/@#{username}/#{slug}") |> get("/events/#{event.uuid}")
assert json_response(conn, 200) == ObjectView.render("event.json", %{event: event}) assert json_response(conn, 200) == ObjectView.render("event.json", %{event: event})
Logger.error(inspect ObjectView.render("event.json", %{event: event})) Logger.error(inspect ObjectView.render("event.json", %{event: event}))
end end
end end
# describe "/accounts/:username/inbox" do # describe "/actors/:username/inbox" do
# test "it inserts an incoming activity into the database", %{conn: conn} do # test "it inserts an incoming activity into the database", %{conn: conn} do
# data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() # data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
# #
@ -54,7 +52,7 @@ defmodule EventosWeb.ActivityPubControllerTest do
# end # end
# end # end
# describe "/accounts/:nickname/followers" do # describe "/actors/:nickname/followers" do
# test "it returns the followers in a collection", %{conn: conn} do # test "it returns the followers in a collection", %{conn: conn} do
# user = insert(:user) # user = insert(:user)
# user_two = insert(:user) # user_two = insert(:user)

View file

@ -0,0 +1,49 @@
defmodule EventosWeb.ActorControllerTest do
use EventosWeb.ConnCase
import Eventos.Factory
alias Eventos.Actors
setup %{conn: conn} do
actor = insert(:actor)
user = insert(:user, actor: actor)
{:ok, conn: conn, user: user}
end
describe "index" do
test "lists all actors", %{conn: conn, user: user} do
conn = get conn, actor_path(conn, :index)
assert hd(json_response(conn, 200)["data"])["username"] == user.actor.preferred_username
end
end
###
# Not possible atm
###
# describe "delete actor" do
# setup [:create_actor]
#
# test "deletes own actor", %{conn: conn, user: user} do
# conn = auth_conn(conn, user)
# conn = delete conn, actor_path(conn, :delete, user.actor)
# assert response(conn, 204)
# assert_error_sent 404, fn ->
# get conn, actor_path(conn, :show, user.actor)
# end
# end
#
# test "deletes other actor", %{conn: conn, actor: actor, user: user} do
# conn = auth_conn(conn, user)
# conn = delete conn, actor_path(conn, :delete, actor)
# assert response(conn, 401)
# conn = get conn, actor_path(conn, :show, actor)
# assert response(conn, 200)
# end
# end
defp create_actor(_) do
actor = insert(:actor)
{:ok, actor: actor}
end
end

View file

@ -16,8 +16,8 @@ defmodule EventosWeb.AddressControllerTest do
end end
setup %{conn: conn} do setup %{conn: conn} do
account = insert(:account) actor = insert(:actor)
user = insert(:user, account: account) user = insert(:user, actor: actor)
{:ok, conn: conn, user: user} {:ok, conn: conn, user: user}
end end
@ -101,11 +101,4 @@ defmodule EventosWeb.AddressControllerTest do
defp create_address(_) do defp create_address(_) do
{:ok, address: insert(:address)} {:ok, address: insert(:address)}
end end
defp auth_conn(conn, %Eventos.Accounts.User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> put_req_header("authorization", "Bearer #{token}")
|> put_req_header("accept", "application/json")
end
end end

View file

@ -1,20 +1,19 @@
defmodule EventosWeb.BotControllerTest do defmodule EventosWeb.BotControllerTest do
use EventosWeb.ConnCase use EventosWeb.ConnCase
import Eventos.Factory
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Actors.Bot alias Eventos.Actors.Bot
@create_attrs %{source: "some source", type: "some type"} @create_attrs %{source: "some source", type: "some type", name: "some name"}
@update_attrs %{source: "some updated source", type: "some updated type"} @update_attrs %{source: "some updated source", type: "some updated type", name: "some updated name"}
@invalid_attrs %{source: nil, type: nil} @invalid_attrs %{source: nil, type: nil, name: nil}
def fixture(:bot) do
{:ok, bot} = Actors.create_bot(@create_attrs)
bot
end
setup %{conn: conn} do setup %{conn: conn} do
{:ok, conn: put_req_header(conn, "accept", "application/json")} actor = insert(:actor)
user = insert(:user, actor: actor)
{:ok, conn: put_req_header(conn, "accept", "application/json"), user: user}
end end
describe "index" do describe "index" do
@ -25,7 +24,8 @@ defmodule EventosWeb.BotControllerTest do
end end
describe "create bot" do describe "create bot" do
test "renders bot when data is valid", %{conn: conn} do test "renders bot when data is valid", %{conn: conn, user: user} do
conn = auth_conn(conn, user)
conn = post conn, bot_path(conn, :create), bot: @create_attrs conn = post conn, bot_path(conn, :create), bot: @create_attrs
assert %{"id" => id} = json_response(conn, 201)["data"] assert %{"id" => id} = json_response(conn, 201)["data"]
@ -36,7 +36,8 @@ defmodule EventosWeb.BotControllerTest do
"type" => "some type"} "type" => "some type"}
end end
test "renders errors when data is invalid", %{conn: conn} do test "renders errors when data is invalid", %{conn: conn, user: user} do
conn = auth_conn(conn, user)
conn = post conn, bot_path(conn, :create), bot: @invalid_attrs conn = post conn, bot_path(conn, :create), bot: @invalid_attrs
assert json_response(conn, 422)["errors"] != %{} assert json_response(conn, 422)["errors"] != %{}
end end
@ -45,7 +46,8 @@ defmodule EventosWeb.BotControllerTest do
describe "update bot" do describe "update bot" do
setup [:create_bot] setup [:create_bot]
test "renders bot when data is valid", %{conn: conn, bot: %Bot{id: id} = bot} do test "renders bot when data is valid", %{conn: conn, bot: %Bot{id: id} = bot, user: user} do
conn = auth_conn(conn, user)
conn = put conn, bot_path(conn, :update, bot), bot: @update_attrs conn = put conn, bot_path(conn, :update, bot), bot: @update_attrs
assert %{"id" => ^id} = json_response(conn, 200)["data"] assert %{"id" => ^id} = json_response(conn, 200)["data"]
@ -56,7 +58,8 @@ defmodule EventosWeb.BotControllerTest do
"type" => "some updated type"} "type" => "some updated type"}
end end
test "renders errors when data is invalid", %{conn: conn, bot: bot} do test "renders errors when data is invalid", %{conn: conn, bot: bot, user: user} do
conn = auth_conn(conn, user)
conn = put conn, bot_path(conn, :update, bot), bot: @invalid_attrs conn = put conn, bot_path(conn, :update, bot), bot: @invalid_attrs
assert json_response(conn, 422)["errors"] != %{} assert json_response(conn, 422)["errors"] != %{}
end end
@ -65,7 +68,8 @@ defmodule EventosWeb.BotControllerTest do
describe "delete bot" do describe "delete bot" do
setup [:create_bot] setup [:create_bot]
test "deletes chosen bot", %{conn: conn, bot: bot} do test "deletes chosen bot", %{conn: conn, bot: bot, user: user} do
conn = auth_conn(conn, user)
conn = delete conn, bot_path(conn, :delete, bot) conn = delete conn, bot_path(conn, :delete, bot)
assert response(conn, 204) assert response(conn, 204)
assert_error_sent 404, fn -> assert_error_sent 404, fn ->
@ -75,7 +79,7 @@ defmodule EventosWeb.BotControllerTest do
end end
defp create_bot(_) do defp create_bot(_) do
bot = fixture(:bot) bot = insert(:bot)
{:ok, bot: bot} {:ok, bot: bot}
end end
end end

View file

@ -16,8 +16,8 @@ defmodule EventosWeb.CategoryControllerTest do
end end
setup %{conn: conn} do setup %{conn: conn} do
account = insert(:account) actor = insert(:actor)
user = insert(:user, account: account) user = insert(:user, actor: actor)
{:ok, conn: conn, user: user} {:ok, conn: conn, user: user}
end end
@ -89,11 +89,4 @@ defmodule EventosWeb.CategoryControllerTest do
category = fixture(:category) category = fixture(:category)
{:ok, category: category} {:ok, category: category}
end end
defp auth_conn(conn, %Eventos.Accounts.User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> put_req_header("authorization", "Bearer #{token}")
|> put_req_header("accept", "application/json")
end
end end

View file

@ -4,39 +4,37 @@ defmodule EventosWeb.CommentControllerTest do
alias Eventos.Events alias Eventos.Events
alias Eventos.Events.Comment alias Eventos.Events.Comment
@create_attrs %{text: "some text", url: "some url"} import Eventos.Factory
@update_attrs %{text: "some updated text", url: "some updated url"}
@create_attrs %{text: "some text"}
@update_attrs %{text: "some updated text"}
@invalid_attrs %{text: nil, url: nil} @invalid_attrs %{text: nil, url: nil}
def fixture(:comment) do
{:ok, comment} = Events.create_comment(@create_attrs)
comment
end
setup %{conn: conn} do setup %{conn: conn} do
{:ok, conn: put_req_header(conn, "accept", "application/json")} actor = insert(:actor)
end user = insert(:user, actor: actor)
{:ok, conn: put_req_header(conn, "accept", "application/json"), user: user}
describe "index" do
test "lists all comments", %{conn: conn} do
conn = get conn, comment_path(conn, :index)
assert json_response(conn, 200)["data"] == []
end
end end
describe "create comment" do describe "create comment" do
test "renders comment when data is valid", %{conn: conn} do test "renders comment when data is valid", %{conn: conn, user: user} do
conn = post conn, comment_path(conn, :create), comment: @create_attrs conn = auth_conn(conn, user)
assert %{"id" => id} = json_response(conn, 201)["data"] actor = insert(:actor)
attrs = Map.merge(@create_attrs, %{actor_id: actor.id})
conn = post conn, comment_path(conn, :create), comment: attrs
assert %{"uuid" => uuid, "id" => id} = json_response(conn, 201)["data"]
conn = get conn, comment_path(conn, :show, id) conn = get conn, comment_path(conn, :show, uuid)
assert json_response(conn, 200)["data"] == %{ assert json_response(conn, 200)["data"] == %{
"id" => id, "id" => id,
"text" => "some text", "text" => "some text",
"url" => "some url"} "uuid" => uuid,
"url" => "#{EventosWeb.Endpoint.url()}/comments/#{uuid}"
}
end end
test "renders errors when data is invalid", %{conn: conn} do test "renders errors when data is invalid", %{conn: conn, user: user} do
conn = auth_conn(conn, user)
conn = post conn, comment_path(conn, :create), comment: @invalid_attrs conn = post conn, comment_path(conn, :create), comment: @invalid_attrs
assert json_response(conn, 422)["errors"] != %{} assert json_response(conn, 422)["errors"] != %{}
end end
@ -45,19 +43,25 @@ defmodule EventosWeb.CommentControllerTest do
describe "update comment" do describe "update comment" do
setup [:create_comment] setup [:create_comment]
test "renders comment when data is valid", %{conn: conn, comment: %Comment{id: id} = comment} do test "renders comment when data is valid", %{conn: conn, comment: %Comment{id: id, uuid: uuid} = comment, user: user} do
conn = put conn, comment_path(conn, :update, comment), comment: @update_attrs conn = auth_conn(conn, user)
assert %{"id" => ^id} = json_response(conn, 200)["data"] actor = insert(:actor)
attrs = Map.merge(@update_attrs, %{actor_id: actor.id})
conn = put conn, comment_path(conn, :update, uuid), comment: attrs
assert %{"uuid" => uuid, "id" => id} = json_response(conn, 200)["data"]
conn = get conn, comment_path(conn, :show, id) conn = get conn, comment_path(conn, :show, uuid)
assert json_response(conn, 200)["data"] == %{ assert json_response(conn, 200)["data"] == %{
"id" => id, "id" => id,
"text" => "some updated text", "text" => "some updated text",
"url" => "some updated url"} "uuid" => uuid,
"url" => "#{EventosWeb.Endpoint.url()}/comments/#{uuid}"
}
end end
test "renders errors when data is invalid", %{conn: conn, comment: comment} do test "renders errors when data is invalid", %{conn: conn, comment: comment, user: user} do
conn = put conn, comment_path(conn, :update, comment), comment: @invalid_attrs conn = auth_conn(conn, user)
conn = put conn, comment_path(conn, :update, comment.uuid), comment: @invalid_attrs
assert json_response(conn, 422)["errors"] != %{} assert json_response(conn, 422)["errors"] != %{}
end end
end end
@ -65,17 +69,18 @@ defmodule EventosWeb.CommentControllerTest do
describe "delete comment" do describe "delete comment" do
setup [:create_comment] setup [:create_comment]
test "deletes chosen comment", %{conn: conn, comment: comment} do test "deletes chosen comment", %{conn: conn, comment: %Comment{uuid: uuid} = comment, user: user} do
conn = delete conn, comment_path(conn, :delete, comment) conn = auth_conn(conn, user)
conn = delete conn, comment_path(conn, :delete, uuid)
assert response(conn, 204) assert response(conn, 204)
assert_error_sent 404, fn -> assert_error_sent 404, fn ->
get conn, comment_path(conn, :show, comment) get conn, comment_path(conn, :show, uuid)
end end
end end
end end
defp create_comment(_) do defp create_comment(_) do
comment = fixture(:comment) comment = insert(:comment)
{:ok, comment: comment} {:ok, comment: comment}
end end
end end

View file

@ -21,8 +21,8 @@ defmodule EventosWeb.EventControllerTest do
end end
setup %{conn: conn} do setup %{conn: conn} do
account = insert(:account) actor = insert(:actor)
user = insert(:user, account: account) user = insert(:user, actor: actor)
{:ok, conn: conn, user: user} {:ok, conn: conn, user: user}
end end
@ -35,30 +35,21 @@ defmodule EventosWeb.EventControllerTest do
describe "create event" do describe "create event" do
test "renders event when data is valid", %{conn: conn, user: user} do test "renders event when data is valid", %{conn: conn, user: user} do
attrs = Map.put(@create_attrs, :organizer_account_id, user.account.id) attrs = Map.put(@create_attrs, :organizer_actor_id, user.actor.id)
attrs = Map.put(attrs, :address, @create_address_attrs) attrs = Map.put(attrs, :address, @create_address_attrs)
category = insert(:category) category = insert(:category)
attrs = Map.put(attrs, :category_id, category.id) attrs = Map.put(attrs, :category_id, category.id)
conn = auth_conn(conn, user) conn = auth_conn(conn, user)
conn = post conn, event_path(conn, :create), event: attrs conn = post conn, event_path(conn, :create), event: attrs
assert %{"id" => id} = json_response(conn, 201)["data"] assert %{"uuid" => uuid} = json_response(conn, 201)["data"]
conn = get conn, event_path(conn, :show, id) conn = get conn, event_path(conn, :show, uuid)
assert %{ assert %{
"begins_on" => "2010-04-17T14:00:00Z", "begins_on" => "2010-04-17T14:00:00Z",
"description" => "some description", "description" => "some description",
"ends_on" => "2010-04-17T14:00:00Z", "ends_on" => "2010-04-17T14:00:00Z",
"title" => "some title", "title" => "some title",
"group" => nil,
"organizer" => %{
"description" => nil,
"display_name" => nil,
"domain" => nil,
"suspended" => false,
"uri" => "https://",
"url" => "https://",
},
"participants" => [], "participants" => [],
"address" => %{"addressCountry" => "some addressCountry", "addressLocality" => "some addressLocality", "addressRegion" => "some addressRegion", "floor" => "some floor", "geom" => %{"data" => %{"latitude" => -20.0, "longitude" => 30.0}, "type" => "point"}, "postalCode" => "some postalCode", "streetAddress" => "some streetAddress"} "address" => %{"addressCountry" => "some addressCountry", "addressLocality" => "some addressLocality", "addressRegion" => "some addressRegion", "floor" => "some floor", "geom" => %{"data" => %{"latitude" => -20.0, "longitude" => 30.0}, "type" => "point"}, "postalCode" => "some postalCode", "streetAddress" => "some streetAddress"}
} = json_response(conn, 200)["data"] } = json_response(conn, 200)["data"]
@ -66,7 +57,7 @@ defmodule EventosWeb.EventControllerTest do
test "renders errors when data is invalid", %{conn: conn, user: user} do test "renders errors when data is invalid", %{conn: conn, user: user} do
conn = auth_conn(conn, user) conn = auth_conn(conn, user)
attrs = Map.put(@invalid_attrs, :organizer_account_id, user.account.id) attrs = Map.put(@invalid_attrs, :organizer_actor_id, user.actor.id)
attrs = Map.put(attrs, :address, @create_address_attrs) attrs = Map.put(attrs, :address, @create_address_attrs)
conn = post conn, event_path(conn, :create), event: attrs conn = post conn, event_path(conn, :create), event: attrs
assert json_response(conn, 422)["errors"] != %{} assert json_response(conn, 422)["errors"] != %{}
@ -76,9 +67,9 @@ defmodule EventosWeb.EventControllerTest do
describe "export event" do describe "export event" do
setup [:create_event] setup [:create_event]
test "renders ics export of event", %{conn: conn, event: %Event{id: id} = event, user: user} do test "renders ics export of event", %{conn: conn, event: %Event{uuid: uuid} = event, user: user} do
conn = auth_conn(conn, user) conn = auth_conn(conn, user)
conn = get conn, event_path(conn, :export_to_ics, id) conn = get conn, event_path(conn, :export_to_ics, uuid)
exported_event = ICalendar.export_event(event) exported_event = ICalendar.export_event(event)
assert exported_event == response(conn, 200) assert exported_event == response(conn, 200)
end end
@ -87,38 +78,29 @@ defmodule EventosWeb.EventControllerTest do
describe "update event" do describe "update event" do
setup [:create_event] setup [:create_event]
test "renders event when data is valid", %{conn: conn, event: %Event{id: id} = event, user: user} do test "renders event when data is valid", %{conn: conn, event: %Event{uuid: uuid} = event, user: user} do
conn = auth_conn(conn, user) conn = auth_conn(conn, user)
address = address_fixture() address = address_fixture()
attrs = Map.put(@update_attrs, :organizer_account_id, user.account.id) attrs = Map.put(@update_attrs, :organizer_actor_id, user.actor.id)
attrs = Map.put(attrs, :address_id, address.id) attrs = Map.put(attrs, :address_id, address.id)
conn = put conn, event_path(conn, :update, event), event: attrs conn = put conn, event_path(conn, :update, uuid), event: attrs
assert %{"id" => ^id} = json_response(conn, 200)["data"] assert %{"uuid" => uuid} = json_response(conn, 200)["data"]
conn = get conn, event_path(conn, :show, id) conn = get conn, event_path(conn, :show, uuid)
assert %{ assert %{
"begins_on" => "2011-05-18T15:01:01Z", "begins_on" => "2011-05-18T15:01:01Z",
"description" => "some updated description", "description" => "some updated description",
"ends_on" => "2011-05-18T15:01:01Z", "ends_on" => "2011-05-18T15:01:01Z",
"title" => "some updated title", "title" => "some updated title",
"group" => nil,
"organizer" => %{
"description" => nil,
"display_name" => nil,
"domain" => nil,
"suspended" => false,
"uri" => "https://",
"url" => "https://",
},
"participants" => [], "participants" => [],
"address" => %{"addressCountry" => "My Country", "addressLocality" => "My Locality", "addressRegion" => "My Region", "floor" => "Myfloor", "geom" => %{"data" => %{"latitude" => 30.0, "longitude" => -90.0}, "type" => "point"}, "postalCode" => "My Postal Code", "streetAddress" => "My Street Address"} "address" => %{"addressCountry" => "My Country", "addressLocality" => "My Locality", "addressRegion" => "My Region", "floor" => "Myfloor", "geom" => %{"data" => %{"latitude" => 30.0, "longitude" => -90.0}, "type" => "point"}, "postalCode" => "My Postal Code", "streetAddress" => "My Street Address"}
} = json_response(conn, 200)["data"] } = json_response(conn, 200)["data"]
end end
test "renders errors when data is invalid", %{conn: conn, event: event, user: user} do test "renders errors when data is invalid", %{conn: conn, event: %Event{uuid: uuid} = event, user: user} do
conn = auth_conn(conn, user) conn = auth_conn(conn, user)
attrs = Map.put(@invalid_attrs, :organizer_account_id, user.account.id) attrs = Map.put(@invalid_attrs, :organizer_actor_id, user.actor.id)
conn = put conn, event_path(conn, :update, event), event: attrs conn = put conn, event_path(conn, :update, uuid), event: attrs
assert json_response(conn, 422)["errors"] != %{} assert json_response(conn, 422)["errors"] != %{}
end end
end end
@ -126,26 +108,18 @@ defmodule EventosWeb.EventControllerTest do
describe "delete event" do describe "delete event" do
setup [:create_event] setup [:create_event]
test "deletes chosen event", %{conn: conn, event: event, user: user} do test "deletes chosen event", %{conn: conn, event: %Event{uuid: uuid} = event, user: user} do
conn = auth_conn(conn, user) conn = auth_conn(conn, user)
conn = delete conn, event_path(conn, :delete, event) conn = delete conn, event_path(conn, :delete, uuid)
assert response(conn, 204) assert response(conn, 204)
assert_error_sent 404, fn -> conn = get conn, event_path(conn, :show, uuid)
get conn, event_path(conn, :show, event) assert response(conn, 404)
end
end end
end end
defp create_event(_) do defp create_event(_) do
account = insert(:account) actor = insert(:actor)
event = insert(:event, organizer_account: account) event = insert(:event, organizer_actor: actor)
{:ok, event: event, account: account} {:ok, event: event, actor: actor}
end
defp auth_conn(conn, %Eventos.Accounts.User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> put_req_header("authorization", "Bearer #{token}")
|> put_req_header("accept", "application/json")
end end
end end

View file

@ -1,109 +0,0 @@
defmodule EventosWeb.GroupControllerTest do
use EventosWeb.ConnCase
import Eventos.Factory
alias Eventos.Groups
alias Eventos.Groups.Group
@create_attrs %{description: "some description", suspended: true, title: "some title", uri: "some uri", url: "some url"}
@update_attrs %{description: "some updated description", suspended: false, title: "some updated title", uri: "some updated uri", url: "some updated url"}
@invalid_attrs %{description: nil, suspended: nil, title: nil, uri: nil, url: nil}
def fixture(:group) do
{:ok, group} = Groups.create_group(@create_attrs)
group
end
setup %{conn: conn} do
account = insert(:account)
user = insert(:user, account: account)
{:ok, conn: conn, user: user}
end
describe "index" do
test "lists all groups", %{conn: conn} do
conn = get conn, group_path(conn, :index)
assert json_response(conn, 200)["data"] == []
end
end
describe "create group" do
test "renders group when data is valid", %{conn: conn, user: user} do
conn = auth_conn(conn, user)
conn = post conn, group_path(conn, :create), group: @create_attrs
assert %{"id" => id} = json_response(conn, 201)["data"]
conn = get conn, group_path(conn, :show, id)
assert json_response(conn, 200)["data"] == %{
"id" => id,
"description" => "some description",
"suspended" => true,
"title" => "some title",
"uri" => "h",
"url" => "h",
"events" => [],
"members" => []
}
end
test "renders errors when data is invalid", %{conn: conn, user: user} do
conn = auth_conn(conn, user)
conn = post conn, group_path(conn, :create), group: @invalid_attrs
assert json_response(conn, 422)["errors"] != %{}
end
end
describe "update group" do
setup [:create_group]
test "renders group when data is valid", %{conn: conn, group: %Group{id: id} = group, user: user} do
conn = auth_conn(conn, user)
conn = put conn, group_path(conn, :update, group), group: @update_attrs
assert %{"id" => ^id} = json_response(conn, 200)["data"]
conn = get conn, group_path(conn, :show, id)
assert json_response(conn, 200)["data"] == %{
"id" => id,
"description" => "some updated description",
"suspended" => false,
"title" => "some updated title",
"uri" => "some updated uri",
"url" => "some updated url",
"events" => [],
"members" => []
}
end
test "renders errors when data is invalid", %{conn: conn, group: group, user: user} do
conn = auth_conn(conn, user)
conn = put conn, group_path(conn, :update, group), group: @invalid_attrs
assert json_response(conn, 422)["errors"] != %{}
end
end
describe "delete group" do
setup [:create_group]
test "deletes chosen group", %{conn: conn, group: group, user: user} do
conn = auth_conn(conn, user)
conn = delete conn, group_path(conn, :delete, group)
assert response(conn, 204)
assert_error_sent 404, fn ->
get conn, group_path(conn, :show, group)
end
end
end
defp create_group(_) do
group = fixture(:group)
{:ok, group: group}
end
defp auth_conn(conn, %Eventos.Accounts.User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> put_req_header("authorization", "Bearer #{token}")
|> put_req_header("accept", "application/json")
end
end

View file

@ -16,9 +16,9 @@ defmodule EventosWeb.SessionControllerTest do
end end
setup %{conn: conn} do setup %{conn: conn} do
account = insert(:account) actor = insert(:actor)
user = insert(:user, account: account) user = insert(:user, actor: actor)
event = insert(:event, organizer_account: account) event = insert(:event, organizer_actor: actor)
{:ok, conn: conn, user: user, event: event} {:ok, conn: conn, user: user, event: event}
end end
@ -37,7 +37,7 @@ defmodule EventosWeb.SessionControllerTest do
conn = post conn, session_path(conn, :create), session: attrs conn = post conn, session_path(conn, :create), session: attrs
assert %{"id" => id} = json_response(conn, 201)["data"] assert %{"id" => id} = json_response(conn, 201)["data"]
conn = get conn, "/api/events/" <> Integer.to_string(event_id) <> "/sessions" conn = get conn, session_path(conn, :show_sessions_for_event, event.uuid)
assert hd(json_response(conn, 200)["data"])["id"] == id assert hd(json_response(conn, 200)["data"])["id"] == id
conn = get conn, session_path(conn, :show, id) conn = get conn, session_path(conn, :show, id)
@ -107,11 +107,4 @@ defmodule EventosWeb.SessionControllerTest do
session = insert(:session) session = insert(:session)
{:ok, session: session} {:ok, session: session}
end end
defp auth_conn(conn, %Eventos.Accounts.User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> put_req_header("authorization", "Bearer #{token}")
|> put_req_header("accept", "application/json")
end
end end

View file

@ -16,8 +16,8 @@ defmodule EventosWeb.TagControllerTest do
end end
setup %{conn: conn} do setup %{conn: conn} do
account = insert(:account) actor = insert(:actor)
user = insert(:user, account: account) user = insert(:user, actor: actor)
{:ok, conn: conn, user: user} {:ok, conn: conn, user: user}
end end
@ -85,11 +85,4 @@ defmodule EventosWeb.TagControllerTest do
tag = fixture(:tag) tag = fixture(:tag)
{:ok, tag: tag} {:ok, tag: tag}
end end
defp auth_conn(conn, %Eventos.Accounts.User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> put_req_header("authorization", "Bearer #{token}")
|> put_req_header("accept", "application/json")
end
end end

View file

@ -16,9 +16,9 @@ defmodule EventosWeb.TrackControllerTest do
end end
setup %{conn: conn} do setup %{conn: conn} do
account = insert(:account) actor = insert(:actor)
user = insert(:user, account: account) user = insert(:user, actor: actor)
event = insert(:event, organizer_account: account) event = insert(:event, organizer_actor: actor)
{:ok, conn: conn, user: user, event: event} {:ok, conn: conn, user: user, event: event}
end end
@ -94,11 +94,4 @@ defmodule EventosWeb.TrackControllerTest do
track = insert(:track) track = insert(:track)
{:ok, track: track} {:ok, track: track}
end end
defp auth_conn(conn, %Eventos.Accounts.User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> put_req_header("authorization", "Bearer #{token}")
|> put_req_header("accept", "application/json")
end
end end

View file

@ -3,21 +3,21 @@ defmodule EventosWeb.UserControllerTest do
import Eventos.Factory import Eventos.Factory
alias Eventos.Accounts alias Eventos.Actors
alias Eventos.Accounts.User alias Eventos.Actors.User
@create_attrs %{email: "foo@bar.tld", password: "some password_hash", username: "some username"} @create_attrs %{email: "foo@bar.tld", password: "some password_hash", username: "some username"}
# @update_attrs %{email: "foo@fighters.tld", password: "some updated password_hash", username: "some updated username"} # @update_attrs %{email: "foo@fighters.tld", password: "some updated password_hash", username: "some updated username"}
@invalid_attrs %{email: "not an email", password: nil, username: nil} @invalid_attrs %{email: "not an email", password: nil, username: nil}
def fixture(:user) do def fixture(:user) do
{:ok, user} = Accounts.create_user(@create_attrs) {:ok, user} = Actors.create_user(@create_attrs)
user user
end end
setup %{conn: conn} do setup %{conn: conn} do
account = insert(:account) actor = insert(:actor)
user = insert(:user, account: account) user = insert(:user, actor: actor)
{:ok, conn: conn, user: user} {:ok, conn: conn, user: user}
end end
@ -32,20 +32,20 @@ defmodule EventosWeb.UserControllerTest do
describe "create user" do describe "create user" do
test "renders user when data is valid", %{conn: conn} do test "renders user when data is valid", %{conn: conn} do
conn = post conn, user_path(conn, :create), @create_attrs conn = post conn, user_path(conn, :create), @create_attrs
assert %{"user" => %{"id" => id, "account" => %{"avatar_url" => avatar_url}}} = json_response(conn, 201) assert %{"user" => %{"id" => id, "actor" => %{"avatar" => avatar_url}}} = json_response(conn, 201)
assert id > 0 assert id > 0
assert avatar_url == nil assert avatar_url == nil
end end
test "renders errors when data is invalid", %{conn: conn} do test "renders errors when data is invalid", %{conn: conn} do
conn = post conn, user_path(conn, :create), @invalid_attrs conn = post conn, user_path(conn, :create), @invalid_attrs
assert json_response(conn, 400)["msg"] != %{} assert json_response(conn, 422)["errors"] != %{}
end end
test "renders user with avatar when email is valid", %{conn: conn} do test "renders user with avatar when email is valid", %{conn: conn} do
attrs = %{email: "contact@framasoft.org", password: "some password_hash", username: "framasoft"} attrs = %{email: "contact@framasoft.org", password: "some password_hash", username: "framasoft"}
conn = post conn, user_path(conn, :create), attrs conn = post conn, user_path(conn, :create), attrs
assert %{"user" => %{"id" => id, "account" => %{"avatar_url" => avatar_url}}} = json_response(conn, 201) assert %{"user" => %{"id" => id, "actor" => %{"avatar" => avatar_url}}} = json_response(conn, 201)
assert id > 0 assert id > 0
assert avatar_url == "https://secure.gravatar.com/avatar/68b2910a6bb84a482d920e1057533100?default=404" assert avatar_url == "https://secure.gravatar.com/avatar/68b2910a6bb84a482d920e1057533100?default=404"
end end
@ -88,11 +88,4 @@ defmodule EventosWeb.UserControllerTest do
user = insert(:user) user = insert(:user)
{:ok, user: user} {:ok, user: user}
end end
defp auth_conn(conn, %User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> put_req_header("authorization", "Bearer #{token}")
|> put_req_header("accept", "application/json")
end
end end

View file

@ -23,6 +23,13 @@ defmodule EventosWeb.ConnCase do
# The default endpoint for testing # The default endpoint for testing
@endpoint EventosWeb.Endpoint @endpoint EventosWeb.Endpoint
def auth_conn(%Plug.Conn{} = conn, %Eventos.Actors.User{} = user) do
{:ok, token, _claims} = EventosWeb.Guardian.encode_and_sign(user)
conn
|> Plug.Conn.put_req_header("authorization", "Bearer #{token}")
|> Plug.Conn.put_req_header("accept", "application/json")
end
end end
end end

View file

@ -38,7 +38,7 @@ defmodule Eventos.DataCase do
@doc """ @doc """
A helper that transform changeset errors to a map of messages. A helper that transform changeset errors to a map of messages.
assert {:error, changeset} = Accounts.create_user(%{password: "short"}) assert {:error, changeset} = Actors.create_user(%{password: "short"})
assert "password is too short" in errors_on(changeset).password assert "password is too short" in errors_on(changeset).password
assert %{password: ["password is too short"]} = errors_on(changeset) assert %{password: ["password is too short"]} = errors_on(changeset)

View file

@ -15,13 +15,17 @@ defmodule Eventos.Factory do
end end
def actor_factory do def actor_factory do
{:ok, {_, pubkey}} = RsaEx.generate_keypair("4096") key = :public_key.generate_key({:rsa, 2048, 65_537})
username = sequence("thomas") entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
pem = [entry] |> :public_key.pem_encode() |> String.trim_trailing()
preferred_username = sequence("thomas")
%Eventos.Actors.Actor{ %Eventos.Actors.Actor{
preferred_username: username, preferred_username: preferred_username,
domain: nil, domain: nil,
public_key: pubkey, keys: pem,
url: EventosWeb.Endpoint.url() <> "/@#{username}" url: EventosWeb.Endpoint.url() <> "/@#{preferred_username}"
} }
end end
@ -45,6 +49,15 @@ defmodule Eventos.Factory do
} }
end end
def comment_factory do
%Eventos.Events.Comment{
text: "My Comment",
actor: build(:actor),
event: build(:event),
uuid: Ecto.UUID.generate(),
}
end
def event_factory do def event_factory do
actor = build(:actor) actor = build(:actor)
slug = sequence("my-event") slug = sequence("my-event")
@ -58,7 +71,7 @@ defmodule Eventos.Factory do
organizer_actor: actor, organizer_actor: actor,
category: build(:category), category: build(:category),
address: build(:address), address: build(:address),
url: EventosWeb.Endpoint.url() <> "/@" <> actor.username <> "/" <> slug url: "#{EventosWeb.Endpoint.url()}/@#{actor.url}/#{Ecto.UUID.generate()}"
} }
end end
@ -77,14 +90,12 @@ defmodule Eventos.Factory do
} }
end end
def group_factory do def bot_factory do
username = sequence("My Group") %Eventos.Actors.Bot{
%Eventos.Actors.Actor{ source: "https://mysource.tld/feed.ics",
preferred_username: username, type: "ics",
summary: "My group", user: build(:user),
suspended: false, actor: build(:actor),
url: EventosWeb.Endpoint.url() <> "/@#{username}",
type: "Group",
} }
end end
end end