From 682d6a38e963b6863b90bd651af0ef6cfd53b0ca Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Wed, 1 Aug 2018 14:45:18 +0200 Subject: [PATCH] Introduce follower, add tests Signed-off-by: Thomas Citharel --- .gitignore | 3 +- lib/eventos/actors/actor.ex | 62 +--------- lib/eventos/actors/actors.ex | 115 ++++++++++++++---- lib/eventos/actors/follower.ex | 6 +- .../controllers/follower_controller.ex | 43 +++++++ .../controllers/group_controller.ex | 19 ++- lib/eventos_web/router.ex | 5 +- .../views/activity_pub/actor_view.ex | 3 +- lib/eventos_web/views/follower_view.ex | 18 +++ .../http_signatures/http_signatures.ex | 30 +++-- test/eventos/actors/actors_test.exs | 114 +++++++++++++---- .../controllers/actor_controller_test.exs | 71 +++++++++-- .../controllers/follower_controller_test.exs | 76 ++++++++++++ .../controllers/nodeinfo_controller_test.exs | 33 +++++ test/support/factory.ex | 17 +++ 15 files changed, 477 insertions(+), 138 deletions(-) create mode 100644 lib/eventos_web/controllers/follower_controller.ex create mode 100644 lib/eventos_web/views/follower_view.ex create mode 100644 test/eventos_web/controllers/follower_controller_test.exs create mode 100644 test/eventos_web/controllers/nodeinfo_controller_test.exs diff --git a/.gitignore b/.gitignore index e17e12332..3b2f5561e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ erl_crash.dump .elixir_ls /doc priv/static/GeoLite2-City.mmdb -.vscode/ \ No newline at end of file +.vscode/ +cover/ \ No newline at end of file diff --git a/lib/eventos/actors/actor.ex b/lib/eventos/actors/actor.ex index e98ded408..ab564693b 100644 --- a/lib/eventos/actors/actor.ex +++ b/lib/eventos/actors/actor.ex @@ -1,36 +1,3 @@ -defmodule Eventos.Actors.Actor.TitleSlug do - @moduledoc """ - Slug generation for groups - """ - alias Eventos.Actors.Actor - import Ecto.Query - alias Eventos.Repo - use EctoAutoslugField.Slug, from: :title, to: :slug - - def build_slug(sources, changeset) do - slug = super(sources, changeset) - build_unique_slug(slug, changeset) - end - - defp build_unique_slug(slug, changeset) do - query = - from( - a in Actor, - where: a.slug == ^slug - ) - - case Repo.one(query) do - nil -> - slug - - _story -> - slug - |> Eventos.Slug.increment_slug() - |> build_unique_slug(changeset) - end - end -end - import EctoEnum defenum(Eventos.Actors.ActorTypeEnum, :actor_type, [ @@ -131,6 +98,7 @@ defmodule Eventos.Actors.Actor do |> validate_required([:preferred_username, :keys, :suspended, :url, :type]) end + # TODO : Use me ! @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 changes = @@ -219,37 +187,17 @@ defmodule Eventos.Actors.Actor do end end - # @spec get_public_key_for_url(Actor.t) :: {:ok, String.t} + @spec get_public_key_for_url(String.t()) :: {:ok, String.t()} def get_public_key_for_url(url) do with %Actor{} = actor <- get_or_fetch_by_url(url) do - actor - |> get_keys_for_actor + actor.keys |> Eventos.Service.ActivityPub.Utils.pem_to_public_key() else _ -> :error end end - @deprecated "Use get_keys_for_actor/1 instead" - # @spec get_public_key_for_actor(Actor.t) :: {:ok, String.t} - def get_public_key_for_actor(%Actor{} = actor) do - {:ok, actor.keys} - 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} - def get_private_key_for_actor(%Actor{} = actor) do - actor.keys - end - - def get_followers(%Actor{id: actor_id} = actor) do + def get_followers(%Actor{id: actor_id} = _actor) do Repo.all( from( a in Actor, @@ -260,7 +208,7 @@ defmodule Eventos.Actors.Actor do ) end - def get_followings(%Actor{id: actor_id} = actor) do + def get_followings(%Actor{id: actor_id} = _actor) do Repo.all( from( a in Actor, diff --git a/lib/eventos/actors/actors.ex b/lib/eventos/actors/actors.ex index e8ed9ece7..0b0755e3e 100644 --- a/lib/eventos/actors/actors.ex +++ b/lib/eventos/actors/actors.ex @@ -112,36 +112,21 @@ defmodule Eventos.Actors do Actor.changeset(actor, %{}) end - @doc """ - Returns a text representation of a local actor like user@domain.tld - """ - def actor_to_local_name_and_domain(actor) do - "#{actor.preferred_username}@#{Application.get_env(:my, EventosWeb.Endpoint)[:url][:host]}" - end - - @doc """ - Returns a webfinger representation of an actor - """ - def actor_to_webfinger_s(actor) do - "acct:#{actor_to_local_name_and_domain(actor)}" - end - @doc """ List the groups """ def list_groups do - Repo.all(from(a in Actor, where: a.type == "Group")) + Repo.all(from(a in Actor, where: a.type == ^:Group)) end def get_group_by_name(name) do - actor = - case String.split(name, "@") do - [name] -> - Repo.get_by(Actor, preferred_username: name, type: :Group) + case String.split(name, "@") do + [name] -> + Repo.get_by(Actor, preferred_username: name, type: :Group) - [name, domain] -> - Repo.get_by(Actor, preferred_username: name, domain: domain, type: :Group) - end + [name, domain] -> + Repo.get_by(Actor, preferred_username: name, domain: domain, type: :Group) + end end @doc """ @@ -736,4 +721,90 @@ defmodule Eventos.Actors do def change_bot(%Bot{} = bot) do Bot.changeset(bot, %{}) end + + alias Eventos.Actors.Follower + + @doc """ + Gets a single follower. + + Raises `Ecto.NoResultsError` if the Follower does not exist. + + ## Examples + + iex> get_follower!(123) + %Follower{} + + iex> get_follower!(456) + ** (Ecto.NoResultsError) + + """ + def get_follower!(id) do + Repo.get!(Follower, id) + |> Repo.preload([:actor, :target_actor]) + end + + @doc """ + Creates a follower. + + ## Examples + + iex> create_follower(%{field: value}) + {:ok, %Follower{}} + + iex> create_follower(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_follower(attrs \\ %{}) do + %Follower{} + |> Follower.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a follower. + + ## Examples + + iex> update_follower(follower, %{field: new_value}) + {:ok, %Follower{}} + + iex> update_follower(follower, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_follower(%Follower{} = follower, attrs) do + follower + |> Follower.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a Follower. + + ## Examples + + iex> delete_follower(follower) + {:ok, %Follower{}} + + iex> delete_follower(follower) + {:error, %Ecto.Changeset{}} + + """ + def delete_follower(%Follower{} = follower) do + Repo.delete(follower) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking follower changes. + + ## Examples + + iex> change_follower(follower) + %Ecto.Changeset{source: %Follower{}} + + """ + def change_follower(%Follower{} = follower) do + Follower.changeset(follower, %{}) + end end diff --git a/lib/eventos/actors/follower.ex b/lib/eventos/actors/follower.ex index c6386679b..8f07cdfb2 100644 --- a/lib/eventos/actors/follower.ex +++ b/lib/eventos/actors/follower.ex @@ -12,14 +12,12 @@ defmodule Eventos.Actors.Follower do field(:score, :integer, default: 1000) belongs_to(:target_actor, Actor) belongs_to(:actor, Actor) - - timestamps() end @doc false def changeset(%Follower{} = member, attrs) do member - |> cast(attrs, [:role, :approved, :target_actor_id, :actor_id]) - |> validate_required([:role, :approved, :target_actor_id, :actor_id]) + |> cast(attrs, [:score, :approved, :target_actor_id, :actor_id]) + |> validate_required([:score, :approved, :target_actor_id, :actor_id]) end end diff --git a/lib/eventos_web/controllers/follower_controller.ex b/lib/eventos_web/controllers/follower_controller.ex new file mode 100644 index 000000000..ae5c9612b --- /dev/null +++ b/lib/eventos_web/controllers/follower_controller.ex @@ -0,0 +1,43 @@ +defmodule EventosWeb.FollowerController do + use EventosWeb, :controller + + alias Eventos.Actors + alias Eventos.Actors.Follower + + action_fallback(EventosWeb.FallbackController) + + def index(conn, _params) do + followers = Actors.list_followers() + render(conn, "index.json", followers: followers) + end + + def create(conn, %{"follower" => follower_params}) do + with {:ok, %Follower{} = follower} <- Actors.create_follower(follower_params) do + conn + |> put_status(:created) + |> put_resp_header("location", follower_path(conn, :show, follower)) + |> render("show.json", follower: follower) + end + end + + def show(conn, %{"id" => id}) do + follower = Actors.get_follower!(id) + render(conn, "show.json", follower: follower) + end + + def update(conn, %{"id" => id, "follower" => follower_params}) do + follower = Actors.get_follower!(id) + + with {:ok, %Follower{} = follower} <- Actors.update_follower(follower, follower_params) do + render(conn, "show.json", follower: follower) + end + end + + def delete(conn, %{"id" => id}) do + follower = Actors.get_follower!(id) + + with {:ok, %Follower{}} <- Actors.delete_follower(follower) do + send_resp(conn, :no_content, "") + end + end +end diff --git a/lib/eventos_web/controllers/group_controller.ex b/lib/eventos_web/controllers/group_controller.ex index ad4eb4cbe..c8eec69a8 100644 --- a/lib/eventos_web/controllers/group_controller.ex +++ b/lib/eventos_web/controllers/group_controller.ex @@ -15,8 +15,15 @@ defmodule EventosWeb.GroupController do end def create(conn, %{"group" => group_params}) do - with actor_admin = Guardian.Plug.current_resource(conn).actor, - {:ok, %Actor{} = group} <- Actors.create_group(group_params) do + with {:ok, %Actor{} = group} <- Actors.create_group(group_params) do + %Member{} = + _member = + Actors.create_member(%{ + "parent_id" => group.id, + "actor_id" => Actors.get_local_actor_by_name(group_params["actor_admin"]).id, + "role" => 2 + }) + conn |> put_status(:created) |> put_resp_header("location", actor_path(conn, :show, group)) @@ -24,9 +31,9 @@ defmodule EventosWeb.GroupController do end end - def join(conn, %{"name" => group_name}) do - with actor = Guardian.Plug.current_resource(conn).actor, - group <- Actors.get_group_by_name(group_name), + def join(conn, %{"name" => group_name, "actor_name" => actor_name}) do + with group <- Actors.get_group_by_name(group_name), + actor <- Actors.get_local_actor_by_name(actor_name), %Member{} = member <- Actors.create_member(%{"parent_id" => group.id, "actor_id" => actor.id}) do conn @@ -34,7 +41,7 @@ defmodule EventosWeb.GroupController do |> render(EventosWeb.MemberView, "member.json", member: member) else err -> - import Logger + require Logger Logger.debug(inspect(err)) end end diff --git a/lib/eventos_web/router.ex b/lib/eventos_web/router.ex index f90ea4fae..d35888192 100644 --- a/lib/eventos_web/router.ex +++ b/lib/eventos_web/router.ex @@ -57,6 +57,9 @@ defmodule EventosWeb.Router do get("/actors", ActorController, :index) get("/actors/search/:name", ActorController, :search) get("/actors/:name", ActorController, :show) + + resources("/followers", FollowerController, except: [:new, :edit]) + resources("/tags", TagController, only: [:index, :show]) resources("/categories", CategoryController, only: [:index, :show]) resources("/sessions", SessionController, only: [:index, :show]) @@ -67,7 +70,7 @@ defmodule EventosWeb.Router do end end - # Other scopes may use custom stacks. + # Authentificated API scope "/api", EventosWeb do pipe_through(:api_auth) diff --git a/lib/eventos_web/views/activity_pub/actor_view.ex b/lib/eventos_web/views/activity_pub/actor_view.ex index 79dff4bc5..cab69b96e 100644 --- a/lib/eventos_web/views/activity_pub/actor_view.ex +++ b/lib/eventos_web/views/activity_pub/actor_view.ex @@ -13,8 +13,7 @@ defmodule EventosWeb.ActivityPub.ActorView do import Ecto.Query def render("actor.json", %{actor: actor}) do - pem = Actor.get_keys_for_actor(actor) - public_key = Eventos.Service.ActivityPub.Utils.pem_to_public_key_pem(pem) + public_key = Eventos.Service.ActivityPub.Utils.pem_to_public_key_pem(actor.keys) %{ "id" => actor.url, diff --git a/lib/eventos_web/views/follower_view.ex b/lib/eventos_web/views/follower_view.ex new file mode 100644 index 000000000..05652b781 --- /dev/null +++ b/lib/eventos_web/views/follower_view.ex @@ -0,0 +1,18 @@ +defmodule EventosWeb.FollowerView do + use EventosWeb, :view + alias EventosWeb.FollowerView + + def render("index.json", %{followers: followers}) do + %{data: render_many(followers, FollowerView, "follower.json")} + end + + def render("show.json", %{follower: follower}) do + %{data: render_one(follower, FollowerView, "follower.json")} + end + + def render("follower.json", %{follower: follower}) do + %{id: follower.id, + approved: follower.approved, + score: follower.score} + end +end diff --git a/lib/service/http_signatures/http_signatures.ex b/lib/service/http_signatures/http_signatures.ex index f722bbb85..20ff31e81 100644 --- a/lib/service/http_signatures/http_signatures.ex +++ b/lib/service/http_signatures/http_signatures.ex @@ -46,8 +46,8 @@ defmodule Eventos.Service.HTTPSignatures do # For now, fetch the key for the actor. with actor_id <- conn.params["actor"], 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_entry_decode(public_key) do + [public_key] <- :public_key.pem_decode(public_key_code), + public_key <- :public_key.pem_entry_decode(public_key) do if validate_conn(conn, public_key) do true else @@ -56,8 +56,8 @@ defmodule Eventos.Service.HTTPSignatures do with actor_id <- conn.params["actor"], {:ok, _actor} <- ActivityPub.make_actor_from_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_entry_decode(public_key) do + [public_key] <- :public_key.pem_decode(public_key_code), + public_key <- :public_key.pem_entry_decode(public_key) do validate_conn(conn, public_key) end end @@ -84,19 +84,17 @@ defmodule Eventos.Service.HTTPSignatures do end def sign(%Actor{} = actor, headers) do - with private_key = Actor.get_keys_for_actor(actor) do - sigstring = build_signing_string(headers, Map.keys(headers)) + sigstring = build_signing_string(headers, Map.keys(headers)) - signature = sigstring |> :public_key.sign(:sha256, private_key) |> Base.encode64() + signature = sigstring |> :public_key.sign(:sha256, actor.keys) |> Base.encode64() - [ - keyId: actor.url <> "#main-key", - algorithm: "rsa-sha256", - headers: headers |> Map.keys() |> Enum.join(" "), - signature: signature - ] - |> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end) - |> Enum.join(",") - end + [ + keyId: actor.url <> "#main-key", + algorithm: "rsa-sha256", + headers: headers |> Map.keys() |> Enum.join(" "), + signature: signature + ] + |> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end) + |> Enum.join(",") end end diff --git a/test/eventos/actors/actors_test.exs b/test/eventos/actors/actors_test.exs index f7e08b843..b2b3f042b 100644 --- a/test/eventos/actors/actors_test.exs +++ b/test/eventos/actors/actors_test.exs @@ -38,23 +38,30 @@ defmodule Eventos.ActorsTest do preferred_username: nil } - def actor_fixture(attrs \\ %{}) do - {:ok, actor} = - attrs - |> Enum.into(@valid_attrs) - |> Actors.create_actor() + setup do + user = insert(:user) + actor = insert(:actor, user: user) - actor + {:ok, actor: actor} end - test "list_actors/0 returns all actors" do - actor = actor_fixture() - assert Actors.list_actors() == [actor] + test "list_actors/0 returns all actors", %{actor: actor} do + actors = Actors.list_actors() + assert actors = [actor] end - test "get_actor!/1 returns the actor with given id" do - actor = actor_fixture() - assert Actors.get_actor!(actor.id) == actor + test "get_actor!/1 returns the actor with given id", %{actor: actor} do + actor_fetched = Actors.get_actor!(actor.id) + assert actor_fetched = actor + end + + test "get_actor_with_everything!/1 returns the actor with it's organized events", %{ + actor: actor + } do + assert Actors.get_actor_with_everything!(actor.id).organized_events == [] + event = insert(:event, organizer_actor: actor) + events = Actors.get_actor_with_everything!(actor.id).organized_events + assert events = [event] end test "create_actor/1 with valid data creates a actor" do @@ -71,8 +78,7 @@ defmodule Eventos.ActorsTest do assert {:error, %Ecto.Changeset{}} = Actors.create_actor(@invalid_attrs) end - test "update_actor/2 with valid data updates the actor" do - actor = actor_fixture() + test "update_actor/2 with valid data updates the actor", %{actor: actor} do assert {:ok, actor} = Actors.update_actor(actor, @update_attrs) assert %Actor{} = actor assert actor.summary == "some updated description" @@ -83,20 +89,18 @@ defmodule Eventos.ActorsTest do assert actor.preferred_username == "some updated username" end - test "update_actor/2 with invalid data returns error changeset" do - actor = actor_fixture() + test "update_actor/2 with invalid data returns error changeset", %{actor: actor} do assert {:error, %Ecto.Changeset{}} = Actors.update_actor(actor, @invalid_attrs) - assert actor == Actors.get_actor!(actor.id) + actor_fetched = Actors.get_actor!(actor.id) + assert actor = actor_fetched end - test "delete_actor/1 deletes the actor" do - actor = actor_fixture() + test "delete_actor/1 deletes the actor", %{actor: actor} do assert {:ok, %Actor{}} = Actors.delete_actor(actor) assert_raise Ecto.NoResultsError, fn -> Actors.get_actor!(actor.id) end end - test "change_actor/1 returns a actor changeset" do - actor = actor_fixture() + test "change_actor/1 returns a actor changeset", %{actor: actor} do assert %Ecto.Changeset{} = Actors.change_actor(actor) end end @@ -269,4 +273,72 @@ defmodule Eventos.ActorsTest do assert %Ecto.Changeset{} = Actors.change_bot(bot) end end + + describe "followers" do + alias Eventos.Actors.Follower + + @valid_attrs %{approved: true, score: 42} + @update_attrs %{approved: false, score: 43} + @invalid_attrs %{approved: nil, score: nil} + + setup do + actor = insert(:actor) + target_actor = insert(:actor) + follower = insert(:follower, actor: actor, target_actor: target_actor) + {:ok, follower: follower, actor: actor, target_actor: target_actor} + end + + test "get_follower!/1 returns the follower with given id", %{follower: follower} do + follower_fetched = Actors.get_follower!(follower.id) + assert follower_fetched = follower + end + + test "create_follower/1 with valid data creates a follower", %{ + actor: actor, + target_actor: target_actor + } do + valid_attrs = + @valid_attrs + |> Map.put(:actor_id, actor.id) + |> Map.put(:target_actor_id, target_actor.id) + + assert {:ok, %Follower{} = follower} = Actors.create_follower(valid_attrs) + assert follower.approved == true + assert follower.score == 42 + end + + test "create_follower/1 with invalid data returns error changeset", %{ + actor: actor, + target_actor: target_actor + } do + invalid_attrs = + @invalid_attrs + |> Map.put(:actor_id, actor.id) + |> Map.put(:target_actor_id, target_actor.id) + + assert {:error, %Ecto.Changeset{}} = Actors.create_follower(invalid_attrs) + end + + test "update_follower/2 with valid data updates the follower", %{follower: follower} do + assert {:ok, follower} = Actors.update_follower(follower, @update_attrs) + assert %Follower{} = follower + assert follower.approved == false + assert follower.score == 43 + end + + test "update_follower/2 with invalid data returns error changeset", %{follower: follower} do + assert {:error, %Ecto.Changeset{}} = Actors.update_follower(follower, @invalid_attrs) + follower_fetched = Actors.get_follower!(follower.id) + assert follower = follower_fetched + end + + test "delete_follower/1 deletes the follower", %{follower: follower} do + assert {:ok, %Follower{}} = Actors.delete_follower(follower) + assert_raise Ecto.NoResultsError, fn -> Actors.get_follower!(follower.id) end + end + + test "change_follower/1 returns a follower changeset", %{follower: follower} do + assert %Ecto.Changeset{} = Actors.change_follower(follower) + end + end end diff --git a/test/eventos_web/controllers/actor_controller_test.exs b/test/eventos_web/controllers/actor_controller_test.exs index 7daed0bdc..fb168b21b 100644 --- a/test/eventos_web/controllers/actor_controller_test.exs +++ b/test/eventos_web/controllers/actor_controller_test.exs @@ -11,16 +11,9 @@ defmodule EventosWeb.ActorControllerTest do {:ok, conn: conn, user: user, actor: actor} end - key = :public_key.generate_key({:rsa, 2048, 65_537}) - entry = :public_key.pem_entry_encode(:RSAPrivateKey, key) - pem = [entry] |> :public_key.pem_encode() |> String.trim_trailing() - @create_attrs %{ preferred_username: "otheridentity", - summary: "This is my other identity", - domain: nil, - keys: pem, - user: nil + summary: "This is my other identity" } describe "index" do @@ -61,4 +54,66 @@ defmodule EventosWeb.ActorControllerTest do # assert response(conn, 200) # end # end + + @create_group_attrs %{ + preferred_username: "mygroup", + summary: "This is my awesome group", + name: "My Group" + } + + describe "index groups" do + test "lists all actor groups", %{conn: conn} do + conn = get(conn, group_path(conn, :index)) + assert json_response(conn, 200)["data"] == [] + end + + test "after creating a group", %{conn: conn, user: user, actor: actor} do + # create group + conn = auth_conn(conn, user) + create_group_attrs = Map.put(@create_group_attrs, :actor_admin, actor.preferred_username) + conn = post(conn, group_path(conn, :create), group: create_group_attrs) + + group_res = json_response(conn, 201) + assert group_res["username"] == @create_group_attrs.preferred_username + + conn = get(conn, group_path(conn, :index)) + assert json_response(conn, 200)["data"] == [group_res] + end + end + + describe "create group" do + test "with valid attributes", %{conn: conn, user: user, actor: actor} do + conn = auth_conn(conn, user) + create_group_attrs = Map.put(@create_group_attrs, :actor_admin, actor.preferred_username) + conn = post(conn, group_path(conn, :create), group: create_group_attrs) + + assert json_response(conn, 201)["username"] == @create_group_attrs.preferred_username + end + end + + describe "join group" do + setup %{conn: conn} do + user = insert(:user) + actor = insert(:actor, user: user) + group = insert(:group, preferred_username: "mygroup") + {:ok, conn: conn, user: user, actor: actor, group: group} + end + + test "from valid account", %{conn: conn, user: user, actor: actor, group: group} do + conn = auth_conn(conn, user) + + conn = + post(conn, group_path(conn, :join, group.preferred_username), %{ + "actor_name" => actor.preferred_username + }) + + resp = json_response(conn, 201) + + assert resp = %{ + "actor" => %{"username" => actor.preferred_username}, + "group" => %{"username" => group.preferred_username}, + "role" => 0 + } + end + end end diff --git a/test/eventos_web/controllers/follower_controller_test.exs b/test/eventos_web/controllers/follower_controller_test.exs new file mode 100644 index 000000000..9543472d9 --- /dev/null +++ b/test/eventos_web/controllers/follower_controller_test.exs @@ -0,0 +1,76 @@ +defmodule EventosWeb.FollowerControllerTest do + use EventosWeb.ConnCase + + alias Eventos.Actors + alias Eventos.Actors.Follower + import Eventos.Factory + + @create_attrs %{approved: true, score: 42} + @update_attrs %{approved: false, score: 43} + @invalid_attrs %{approved: nil, score: nil} + + setup %{conn: conn} do + actor = insert(:actor) + target_actor = insert(:actor) + follower = insert(:follower, actor: actor, target_actor: target_actor) + + {:ok, + conn: put_req_header(conn, "accept", "application/json"), + actor: actor, + target_actor: target_actor, + follower: follower} + end + + describe "create follower" do + test "renders follower when data is valid", %{ + conn: conn, + actor: actor, + target_actor: target_actor + } do + create_attrs = + @create_attrs + |> Map.put(:actor_id, actor.id) + |> Map.put(:target_actor_id, target_actor.id) + + conn = post(conn, follower_path(conn, :create), follower: create_attrs) + assert %{"id" => id} = json_response(conn, 201)["data"] + + conn = get(conn, follower_path(conn, :show, id)) + assert json_response(conn, 200)["data"] == %{"id" => id, "approved" => true, "score" => 42} + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post(conn, follower_path(conn, :create), follower: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "update follower" do + test "renders follower when data is valid", %{ + conn: conn, + follower: %Follower{id: id} = follower + } do + conn = put(conn, follower_path(conn, :update, follower), follower: @update_attrs) + assert %{"id" => ^id} = json_response(conn, 200)["data"] + + conn = get(conn, follower_path(conn, :show, id)) + assert json_response(conn, 200)["data"] == %{"id" => id, "approved" => false, "score" => 43} + end + + test "renders errors when data is invalid", %{conn: conn, follower: follower} do + conn = put(conn, follower_path(conn, :update, follower), follower: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "delete follower" do + test "deletes chosen follower", %{conn: conn, follower: follower} do + conn = delete(conn, follower_path(conn, :delete, follower)) + assert response(conn, 204) + + assert_error_sent(404, fn -> + get(conn, follower_path(conn, :show, follower)) + end) + end + end +end diff --git a/test/eventos_web/controllers/nodeinfo_controller_test.exs b/test/eventos_web/controllers/nodeinfo_controller_test.exs new file mode 100644 index 000000000..b29baa453 --- /dev/null +++ b/test/eventos_web/controllers/nodeinfo_controller_test.exs @@ -0,0 +1,33 @@ +defmodule EventosWeb.NodeinfoControllerTest do + use EventosWeb.ConnCase + + @instance Application.get_env(:eventos, :instance) + + test "Get node info schemas", %{conn: conn} do + conn = get(conn, nodeinfo_path(conn, :schemas)) + + assert json_response(conn, 200) == %{ + "links" => [ + %{ + "href" => EventosWeb.Endpoint.url() <> "/nodeinfo/2.0.json", + "rel" => "http://nodeinfo.diaspora.software/ns/schema/2.0" + } + ] + } + end + + test "Get node info", %{conn: conn} do + conn = get(conn, nodeinfo_path(conn, :nodeinfo, "2.0.json")) + + resp = json_response(conn, 200) + + assert resp = %{ + "metadata" => %{"nodeName" => Keyword.get(@instance, :name)}, + "openRegistrations" => Keyword.get(@instance, :registrations_open), + "protocols" => ["activitypub"], + "services" => %{"inbound" => [], "outbound" => []}, + "software" => %{"name" => "eventos", "version" => Keyword.get(@instance, :version)}, + "version" => "2.0" + } + end +end diff --git a/test/support/factory.ex b/test/support/factory.ex index 1cce9cd09..41059a7e3 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -24,11 +24,28 @@ defmodule Eventos.Factory do preferred_username: preferred_username, domain: nil, keys: pem, + type: :Person, url: EventosWeb.Endpoint.url() <> "/@#{preferred_username}", user: nil } end + def group_factory do + struct!( + actor_factory(), + %{ + type: :Group + } + ) + end + + def follower_factory do + %Eventos.Actors.Follower{ + target_actor: build(:actor), + actor: build(:actor) + } + end + def category_factory do %Eventos.Events.Category{ title: sequence("MyCategory"),