Add start of controller testing
This commit is contained in:
parent
598af7a884
commit
096c99df92
14 changed files with 106 additions and 87 deletions
|
@ -6,8 +6,8 @@ use Mix.Config
|
||||||
# to provide built-in test partitioning in CI environment.
|
# to provide built-in test partitioning in CI environment.
|
||||||
# Run `mix help test` for more information.
|
# Run `mix help test` for more information.
|
||||||
config :matrix_server, MatrixServer.Repo,
|
config :matrix_server, MatrixServer.Repo,
|
||||||
username: "postgres",
|
username: "matrix_server",
|
||||||
password: "postgres",
|
password: "matrix_server",
|
||||||
database: "matrix_server_test#{System.get_env("MIX_TEST_PARTITION")}",
|
database: "matrix_server_test#{System.get_env("MIX_TEST_PARTITION")}",
|
||||||
hostname: "localhost",
|
hostname: "localhost",
|
||||||
pool: Ecto.Adapters.SQL.Sandbox
|
pool: Ecto.Adapters.SQL.Sandbox
|
||||||
|
@ -20,3 +20,5 @@ config :matrix_server, MatrixServerWeb.Endpoint,
|
||||||
|
|
||||||
# Print only warnings and errors during test
|
# Print only warnings and errors during test
|
||||||
config :logger, level: :warn
|
config :logger, level: :warn
|
||||||
|
|
||||||
|
config :matrix_server, :server_name, "localhost"
|
||||||
|
|
|
@ -38,9 +38,13 @@ defmodule MatrixServer.Account do
|
||||||
|> Multi.insert(:device, fn %{account: account} ->
|
|> Multi.insert(:device, fn %{account: account} ->
|
||||||
device_id = Device.generate_device_id(account.localpart)
|
device_id = Device.generate_device_id(account.localpart)
|
||||||
|
|
||||||
# TODO: fix device_id with UUID
|
params =
|
||||||
|
Map.update(params, :device_id, device_id, fn
|
||||||
|
nil -> device_id
|
||||||
|
x -> x
|
||||||
|
end)
|
||||||
|
|
||||||
Ecto.build_assoc(account, :devices)
|
Ecto.build_assoc(account, :devices)
|
||||||
|> Map.put(:device_id, device_id)
|
|
||||||
|> Device.changeset(params)
|
|> Device.changeset(params)
|
||||||
end)
|
end)
|
||||||
|> Multi.run(:device_with_access_token, &Device.insert_new_access_token/2)
|
|> Multi.run(:device_with_access_token, &Device.insert_new_access_token/2)
|
||||||
|
|
|
@ -40,6 +40,7 @@ defmodule MatrixServer.Device do
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_device_id(localpart) do
|
def generate_device_id(localpart) do
|
||||||
|
# TODO: use random string instead
|
||||||
time_string =
|
time_string =
|
||||||
DateTime.utc_now()
|
DateTime.utc_now()
|
||||||
|> DateTime.to_unix()
|
|> DateTime.to_unix()
|
||||||
|
|
|
@ -1,44 +1,31 @@
|
||||||
# https://gist.github.com/char0n/6fca76e886a2cfbd3aaa05526f287728
|
|
||||||
defmodule MatrixServerWeb.API.Login do
|
defmodule MatrixServerWeb.API.Login do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
# TODO: Maybe use inline embedded schema here
|
|
||||||
# https://hexdocs.pm/ecto/Ecto.Schema.html#embeds_one/3
|
|
||||||
defmodule MatrixServerWeb.API.Login.Identifier do
|
|
||||||
use Ecto.Schema
|
|
||||||
|
|
||||||
import Ecto.Changeset
|
|
||||||
|
|
||||||
@primary_key false
|
|
||||||
embedded_schema do
|
|
||||||
field :type, :string
|
|
||||||
field :user, :string
|
|
||||||
end
|
|
||||||
|
|
||||||
def changeset(identifier, params) do
|
|
||||||
identifier
|
|
||||||
|> cast(params, [:type, :user])
|
|
||||||
|> validate_required([:type, :user])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
alias MatrixServerWeb.API.Login.Identifier
|
|
||||||
|
|
||||||
@primary_key false
|
@primary_key false
|
||||||
embedded_schema do
|
embedded_schema do
|
||||||
field :type, :string
|
field :type, :string
|
||||||
field :password, :string
|
field :password, :string
|
||||||
field :device_id, :string
|
field :device_id, :string
|
||||||
field :initial_device_display_name, :string
|
field :initial_device_display_name, :string
|
||||||
embeds_one :identifier, Identifier
|
|
||||||
|
embeds_one :identifier, Identifier, primary_key: false do
|
||||||
|
field :type, :string
|
||||||
|
field :user, :string
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(params) do
|
def changeset(params) do
|
||||||
%__MODULE__{}
|
%__MODULE__{}
|
||||||
|> cast(params, [:type, :password, :device_id, :initial_device_display_name])
|
|> cast(params, [:type, :password, :device_id, :initial_device_display_name])
|
||||||
|> cast_embed(:identifier, with: &Identifier.changeset/2, required: true)
|
|> cast_embed(:identifier, with: &identifier_changeset/2, required: true)
|
||||||
|> validate_required([:type, :password])
|
|> validate_required([:type, :password])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def identifier_changeset(identifier, params) do
|
||||||
|
identifier
|
||||||
|
|> cast(params, [:type, :user])
|
||||||
|
|> validate_required([:type, :user])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule MatrixServerWeb.AuthController do
|
||||||
def register(conn, %{"auth" => %{"type" => @register_type}} = params) do
|
def register(conn, %{"auth" => %{"type" => @register_type}} = params) do
|
||||||
case Register.changeset(params) do
|
case Register.changeset(params) do
|
||||||
%Changeset{valid?: true} = cs ->
|
%Changeset{valid?: true} = cs ->
|
||||||
|
# TODO: refactor this
|
||||||
input =
|
input =
|
||||||
apply_changes(cs)
|
apply_changes(cs)
|
||||||
|> Map.from_struct()
|
|> Map.from_struct()
|
||||||
|
|
|
@ -15,7 +15,7 @@ defmodule MatrixServerWeb.Router do
|
||||||
scope "/_matrix", MatrixServerWeb do
|
scope "/_matrix", MatrixServerWeb do
|
||||||
pipe_through :public
|
pipe_through :public
|
||||||
|
|
||||||
scope "/client/r0", as: :client do
|
scope "/client/r0" do
|
||||||
post "/register", AuthController, :register
|
post "/register", AuthController, :register
|
||||||
get "/register/available", AccountController, :available
|
get "/register/available", AccountController, :available
|
||||||
get "/login", AuthController, :login_types
|
get "/login", AuthController, :login_types
|
||||||
|
@ -28,7 +28,7 @@ defmodule MatrixServerWeb.Router do
|
||||||
scope "/_matrix", MatrixServerWeb do
|
scope "/_matrix", MatrixServerWeb do
|
||||||
pipe_through :authenticated
|
pipe_through :authenticated
|
||||||
|
|
||||||
scope "/client/r0", as: :client do
|
scope "/client/r0" do
|
||||||
get "/account/whoami", AccountController, :whoami
|
get "/account/whoami", AccountController, :whoami
|
||||||
post "/logout", AccountController, :logout
|
post "/logout", AccountController, :logout
|
||||||
post "/logout/all", AccountController, :logout_all
|
post "/logout/all", AccountController, :logout_all
|
||||||
|
|
3
mix.exs
3
mix.exs
|
@ -42,7 +42,8 @@ defmodule MatrixServer.MixProject do
|
||||||
{:jason, "~> 1.0"},
|
{:jason, "~> 1.0"},
|
||||||
{:plug_cowboy, "~> 2.0"},
|
{:plug_cowboy, "~> 2.0"},
|
||||||
{:bcrypt_elixir, "~> 2.3"},
|
{:bcrypt_elixir, "~> 2.3"},
|
||||||
{:cors_plug, "~> 2.0"}
|
{:cors_plug, "~> 2.0"},
|
||||||
|
{:ex_machina, "~> 2.7", only: :test}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -11,6 +11,7 @@
|
||||||
"ecto": {:hex, :ecto, "3.6.2", "efdf52acfc4ce29249bab5417415bd50abd62db7b0603b8bab0d7b996548c2bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efad6dfb04e6f986b8a3047822b0f826d9affe8e4ebdd2aeedbfcb14fd48884e"},
|
"ecto": {:hex, :ecto, "3.6.2", "efdf52acfc4ce29249bab5417415bd50abd62db7b0603b8bab0d7b996548c2bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efad6dfb04e6f986b8a3047822b0f826d9affe8e4ebdd2aeedbfcb14fd48884e"},
|
||||||
"ecto_sql": {:hex, :ecto_sql, "3.6.2", "9526b5f691701a5181427634c30655ac33d11e17e4069eff3ae1176c764e0ba3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ec9d7e6f742ea39b63aceaea9ac1d1773d574ea40df5a53ef8afbd9242fdb6b"},
|
"ecto_sql": {:hex, :ecto_sql, "3.6.2", "9526b5f691701a5181427634c30655ac33d11e17e4069eff3ae1176c764e0ba3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ec9d7e6f742ea39b63aceaea9ac1d1773d574ea40df5a53ef8afbd9242fdb6b"},
|
||||||
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
|
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
|
||||||
|
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
|
||||||
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
|
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
|
||||||
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
|
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
|
||||||
"phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"},
|
"phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"},
|
||||||
|
|
29
test/controllers/auth_controller_test.exs
Normal file
29
test/controllers/auth_controller_test.exs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
defmodule MatrixServerWeb.AuthControllerTest do
|
||||||
|
use MatrixServerWeb.ConnCase
|
||||||
|
|
||||||
|
alias MatrixServerWeb.Endpoint
|
||||||
|
|
||||||
|
describe "register endpoint" do
|
||||||
|
test "renders the auth flow when no auth parameter is given", %{conn: conn} do
|
||||||
|
conn = post(conn, Routes.auth_path(conn, :register))
|
||||||
|
|
||||||
|
assert %{"flows" => flows, "params" => _} = json_response(conn, 401)
|
||||||
|
assert is_list(flows)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "registers account with minimal information", %{conn: conn} do
|
||||||
|
params = %{
|
||||||
|
"username" => "user",
|
||||||
|
"password" => "lemmein",
|
||||||
|
"auth" => %{"type" => "m.login.dummy"}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = post_json(conn, Routes.auth_path(Endpoint, :register), params)
|
||||||
|
|
||||||
|
user_id = MatrixServer.get_mxid("user")
|
||||||
|
|
||||||
|
assert %{"access_token" => _, "device_id" => _, "user_id" => ^user_id} =
|
||||||
|
json_response(conn, 200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
test/controllers/info_controller_test.exs
Normal file
16
test/controllers/info_controller_test.exs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule MatrixServerWeb.InfoControllerTest do
|
||||||
|
use MatrixServerWeb.ConnCase
|
||||||
|
|
||||||
|
test "versions endpoint returns a list of supported Matrix spec versions", %{conn: conn} do
|
||||||
|
conn = get(conn, Routes.info_path(conn, :versions))
|
||||||
|
|
||||||
|
assert %{"versions" => versions} = json_response(conn, 200)
|
||||||
|
assert is_list(versions)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "unrecognized route renders M_UNRECOGNIZED error", %{conn: conn} do
|
||||||
|
conn = get(conn, MatrixServerWeb.Endpoint.url() <> "/sneed")
|
||||||
|
|
||||||
|
assert %{"errcode" => "M_UNRECOGNIZED"} = json_response(conn, 400)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,15 +0,0 @@
|
||||||
defmodule MatrixServerWeb.ErrorViewTest do
|
|
||||||
use MatrixServerWeb.ConnCase, async: true
|
|
||||||
|
|
||||||
# Bring render/3 and render_to_string/3 for testing custom views
|
|
||||||
import Phoenix.View
|
|
||||||
|
|
||||||
test "renders 404.json" do
|
|
||||||
assert render(MatrixServerWeb.ErrorView, "404.json", []) == %{errors: %{detail: "Not Found"}}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "renders 500.json" do
|
|
||||||
assert render(MatrixServerWeb.ErrorView, "500.json", []) ==
|
|
||||||
%{errors: %{detail: "Internal Server Error"}}
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,40 +0,0 @@
|
||||||
defmodule MatrixServerWeb.ChannelCase do
|
|
||||||
@moduledoc """
|
|
||||||
This module defines the test case to be used by
|
|
||||||
channel tests.
|
|
||||||
|
|
||||||
Such tests rely on `Phoenix.ChannelTest` and also
|
|
||||||
import other functionality to make it easier
|
|
||||||
to build common data structures and query the data layer.
|
|
||||||
|
|
||||||
Finally, if the test case interacts with the database,
|
|
||||||
we enable the SQL sandbox, so changes done to the database
|
|
||||||
are reverted at the end of every test. If you are using
|
|
||||||
PostgreSQL, you can even run database tests asynchronously
|
|
||||||
by setting `use MatrixServerWeb.ChannelCase, async: true`, although
|
|
||||||
this option is not recommended for other databases.
|
|
||||||
"""
|
|
||||||
|
|
||||||
use ExUnit.CaseTemplate
|
|
||||||
|
|
||||||
using do
|
|
||||||
quote do
|
|
||||||
# Import conveniences for testing with channels
|
|
||||||
import Phoenix.ChannelTest
|
|
||||||
import MatrixServerWeb.ChannelCase
|
|
||||||
|
|
||||||
# The default endpoint for testing
|
|
||||||
@endpoint MatrixServerWeb.Endpoint
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
setup tags do
|
|
||||||
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MatrixServer.Repo)
|
|
||||||
|
|
||||||
unless tags[:async] do
|
|
||||||
Ecto.Adapters.SQL.Sandbox.mode(MatrixServer.Repo, {:shared, self()})
|
|
||||||
end
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -40,4 +40,12 @@ defmodule MatrixServerWeb.ConnCase do
|
||||||
|
|
||||||
{:ok, conn: Phoenix.ConnTest.build_conn()}
|
{:ok, conn: Phoenix.ConnTest.build_conn()}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmacro post_json(conn, path, params) do
|
||||||
|
quote do
|
||||||
|
unquote(conn)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post(unquote(path), Jason.encode!(unquote(params)))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
24
test/support/factory.ex
Normal file
24
test/support/factory.ex
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
defmodule MatrixServer.Factory do
|
||||||
|
use ExMachina.Ecto, repo: MatrixServer.Repo
|
||||||
|
|
||||||
|
alias MatrixServer.{Account, Device}
|
||||||
|
|
||||||
|
def account_factory do
|
||||||
|
%Account{
|
||||||
|
localpart: sequence(:localpart, &"account#{&1}"),
|
||||||
|
password_hash: Bcrypt.hash_pwd_salt("lemmein")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def device_factory do
|
||||||
|
%Account{localpart: localpart} = account = build(:account)
|
||||||
|
device_id = sequence(:device_id, &"device#{&1}")
|
||||||
|
|
||||||
|
%Device{
|
||||||
|
device_id: device_id,
|
||||||
|
access_token: Device.generate_access_token(localpart, device_id),
|
||||||
|
display_name: sequence(:display_name, &"Device #{&1}"),
|
||||||
|
account: account
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue