Merge branch 'chore-test-coverage' into 'master'

chore: Increase test coverage throughout the framework

See merge request mythic-insight/legendary!119
This commit is contained in:
Robert Prehn 2021-09-06 19:43:16 +00:00
commit 6893fc8d23
35 changed files with 573 additions and 126 deletions

View file

@ -9,10 +9,11 @@ defmodule AppWeb.LiveHelpers do
end
def require_auth(socket) do
if socket.assigns.current_user do
socket
else
redirect(socket, to: "/")
case socket.assigns do
%{current_user: user} when not is_nil(user) ->
socket
_ ->
redirect(socket, to: "/")
end
end

View file

@ -51,7 +51,7 @@ defmodule AppWeb.Router do
forward "/sent_emails", Bamboo.SentEmailViewerPlug
end
if Mix.env() == :e2e do
if Mix.env() in [:e2e, :test] do
forward("/end-to-end", Legendary.CoreWeb.Plug.TestEndToEnd, otp_app: :app)
end

View file

@ -8,10 +8,12 @@ defmodule AppWeb.ErrorHelpers do
@doc """
Generates tag for inlined form input errors.
"""
def error_tag(form, field) do
def error_tag(form, field, opts \\ []) do
{extra_classes, _rest_opts} = Keyword.pop(opts, :class, "")
Enum.map(Keyword.get_values(form.errors, field), fn error ->
content_tag(:span, translate_error(error),
class: "invalid-feedback",
class: "invalid-feedback #{extra_classes}",
phx_feedback_for: input_id(form, field)
)
end)

View file

@ -0,0 +1,29 @@
defmodule AppWeb.ErrorHelpersTest do
use AppWeb.ConnCase
import Phoenix.HTML, only: [safe_to_string: 1]
import Phoenix.HTML.Form, only: [form_for: 3]
import AppWeb.ErrorHelpers
def form do
:example
|> form_for(
"/example",
as: :test_params,
errors: [error_field: {"is an error", []}]
)
end
describe "error_tag/2" do
test "generates a span with an invalid-feedback class" do
[safe] = error_tag(form(), :error_field)
assert safe_to_string(safe) =~ "invalid-feedback"
end
test "error_tag/3" do
[safe] = error_tag(form(), :error_field, class: "test-class")
assert safe_to_string(safe) =~ "test-class"
end
end
end

View file

@ -0,0 +1,46 @@
defmodule AppWeb.LiveHelpersText do
use AppWeb.ConnCase
import Mock
import AppWeb.LiveHelpers
describe "assign_defaults/2" do
test "sets current_user" do
{store, _store_config} = Pow.Plug.Base.store(Application.get_env(:core, :pow))
socket = %Phoenix.LiveView.Socket{endpoint: AppWeb.Endpoint}
with_mock Pow.Plug, [verify_token: fn (_, _, _, _) -> {:ok, "h3110"} end] do
with_mock store, [get: fn (_config, _token) -> {%{id: 1234}, nil} end] do
new_socket = assign_defaults(socket, %{"core_auth" => "h3ll0"})
assert %{assigns: %{current_user: %{id: 1234}}} = new_socket
end
end
end
end
describe "require_auth/1" do
test "with user" do
user = %{id: 4567}
socket =
%Phoenix.LiveView.Socket{assigns: %{current_user: user}}
|> require_auth()
assert !socket.redirected
end
test "without user" do
socket = %Phoenix.LiveView.Socket{} |> require_auth()
assert socket.redirected
end
test "without nil user" do
socket =
%Phoenix.LiveView.Socket{assigns: %{current_user: nil}}
|> require_auth()
assert socket.redirected
end
end
end

View file

@ -116,15 +116,17 @@ defmodule Legendary.Content.Post do
def maybe_put_guid(changeset) do
import Legendary.Content.Router.Helpers, only: [url: 1, posts_url: 3]
slug = changeset |> get_field(:name)
guid = changeset |> get_field(:guid)
case slug do
nil -> changeset
_ ->
case guid do
nil ->
base = url(Legendary.CoreWeb.Endpoint)
slug = changeset |> get_field(:name)
changeset
|> put_default(:guid, posts_url(URI.merge(base, "/pages"), :show, slug))
_ ->
changeset
end
end
end

View file

@ -21,6 +21,7 @@ defmodule Legendary.Content.Sitemaps do
generate()
end
@spec generate :: :ok
def generate do
create do
add "", priority: 0.5, changefreq: "hourly", expires: nil

View file

@ -67,9 +67,7 @@ defmodule Legendary.Content.PostsController do
conn |> show_one(post, page_string)
end
end
def show(conn, %{"id" => id, "page" => page_string}) when is_list(id) do
show(conn, %{"id" => Enum.join(id, "/"), "page" => page_string})
end
def show(conn, %{"id" => id} = params) when is_list(id), do: show(conn, Map.merge(params, %{"id" => Enum.join(id, "/")}))
def show(conn, %{"id" => id}), do: show(conn, %{"id" => id, "page" => "1"})
defp try_static_post(conn, id) do

View file

@ -7,6 +7,7 @@
<atom:link href="<%= Legendary.Content.Router.Helpers.url(Legendary.CoreWeb.Endpoint) %><%= @feed_url %>" rel="self" type="application/rss+xml" />
<%= for post <- @posts do %>
<%= if unauthenticated_post?(post) do %>
<item>
<title><%= post.title |> HtmlSanitizeEx.strip_tags() %></title>
<description>
@ -19,6 +20,7 @@
%></pubDate>
<guid isPermaLink="true"><%= post.guid %></guid>
</item>
<% end %>
<% end %>
</channel>
</rss>

View file

@ -8,10 +8,12 @@ defmodule Legendary.Content.ErrorHelpers do
@doc """
Generates tag for inlined form input errors.
"""
def error_tag(form, field) do
def error_tag(form, field, opts \\ []) do
{extra_classes, _rest_opts} = Keyword.pop(opts, :class, "")
Enum.map(Keyword.get_values(form.errors, field), fn error ->
content_tag(:span, translate_error(error),
class: "invalid-feedback",
class: "invalid-feedback #{extra_classes}",
phx_feedback_for: input_id(form, field)
)
end)

View file

@ -1,71 +1,10 @@
defmodule Legendary.Content.FeedsView do
use Legendary.Content, :view
use Phoenix.HTML
alias Phoenix.HTML
alias Phoenix.HTML.Tag
import Legendary.Content.LayoutView, only: [title: 3, excerpt: 3]
def gravatar_url_for_email(email) do
email
|> Kernel.||("noreply@example.com")
|> String.trim()
|> String.downcase()
|> (&(:crypto.hash(:md5, &1))).()
|> Base.encode16()
|> String.downcase()
|> (&("https://www.gravatar.com/avatar/#{&1}")).()
end
def auto_paragraph_tags(string) do
string
|> Kernel.||("")
|> String.split(["\n\n", "\r\n\r\n"], trim: true)
|> Enum.map(fn text ->
[Tag.content_tag(:p, text |> HTML.raw(), []), ?\n]
end)
|> HTML.html_escape()
end
def post_class(post) do
sticky =
if post.sticky do
"sticky"
end
"post post-#{post.id} #{sticky}"
end
def post_topmatter(conn, post) do
author =
post.author ||
%Legendary.Auth.User{
email: "example@example.org",
display_name: "Anonymous",
homepage_url: "#"
}
assigns = %{post: post, author: author, conn: conn}
~E"""
<% _ = assigns # suppress unused assigns warning %>
<div class="Comment-topmatter">
<h4>
<%= link to: author.homepage_url || "#", rel: "author", class: "p-author h-card" do %>
<%= author.display_name %>
<%= img_tag gravatar_url_for_email(author.email), alt: "Photo of #{author.display_name}", class: "Gravatar u-photo" %>
<% end %>
</h4>
<h5>
<%= link to: Routes.posts_path(conn, :show, post) do %>
<time class="dt-published" datetime="<%= post.post %>">
<%= post.post |> Timex.format!("%F", :strftime) %>
</time>
<% end %>
</h5>
</div>
"""
end
def unauthenticated_post?(_conn, post) do
def unauthenticated_post?(post) do
post.password == nil || String.length(post.password) == 0
end
end

View file

@ -45,8 +45,32 @@ defmodule Legendary.Content.PostsTest do
assert %Post{} = Posts.get_post_with_drafts!(Integer.to_string(id))
end
test "update_posts/2", %{public_post: post} do
assert {:ok, %Post{content: "boop"}} = Posts.update_posts(post, %{content: "boop"})
describe "update_posts/2" do
setup do
post_with_guid =
%Post{
title: "Post with guid",
name: "post-with-guid",
status: "publish",
type: "post",
guid: "/beep",
date: ~N[2020-01-01T00:00:00],
}
|> Repo.insert!()
%{
post_with_guid: post_with_guid
}
end
test "with no guid", %{public_post: post} do
assert {:ok, %Post{content: "boop"}} = Posts.update_posts(post, %{content: "boop"})
end
test "with an existing guid", %{post_with_guid: post} do
revised_post = Posts.update_posts(post, %{content: "boop"})
assert {:ok, %Post{content: "boop", guid: "/beep"}} = revised_post
end
end
test "delete_posts/1", %{public_post: post} do

View file

@ -0,0 +1,38 @@
defmodule Legendary.Content.SitemapStorageTest do
use Legendary.Content.DataCase
import Legendary.Content.SitemapStorage
alias Sitemap.Location
alias Legendary.Content.{Post, Repo}
test "creates a post with the content" do
data = "<hello />"
content = data |> :zlib.gzip() |> Base.encode64
write(:file, data)
path = Location.filename(:file)
post = from(p in Post, where: p.name == ^path) |> Repo.one()
assert post.content == content
end
test "updates an existing sitemap" do
path = Location.filename(:file)
%Post{
content: "<hello />" |> :zlib.gzip() |> Base.encode64,
name: path
}
|> Repo.insert!()
new_data = "<world />"
new_content = new_data |> :zlib.gzip() |> Base.encode64
write(:file, new_data)
post = from(p in Post, where: p.name == ^path) |> Repo.one()
assert post.content == new_content
end
end

View file

@ -0,0 +1,78 @@
defmodule Legendary.Content.SitemapsTest do
use Legendary.Content.DataCase
alias Legendary.Content.{Post, Repo}
import Mock
import Legendary.Content.Sitemaps
@xml ~s(
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'
xmlns:geo='http://www.google.com/geo/schemas/sitemap/1.0'
xmlns:news='http://www.google.com/schemas/sitemap-news/0.9'
xmlns:image='http://www.google.com/schemas/sitemap-image/1.1'
xmlns:video='http://www.google.com/schemas/sitemap-video/1.1'
xmlns:mobile='http://www.google.com/schemas/sitemap-mobile/1.0'
xmlns:pagemap='http://www.google.com/schemas/sitemap-pagemap/1.0'
xmlns:xhtml='http://www.w3.org/1999/xhtml'
>
<url>
<loc>https://localhost</loc>
<lastmod>2021-08-19T21:44:37Z</lastmod>
<changefreq>hourly</changefreq>
<priority>0.5</priority>
</url><url>
<loc>https://localhost/public-post</loc>
<lastmod>2021-08-19T21:44:37Z</lastmod>
<changefreq>hourly</changefreq>
<priority>0.5</priority>
</url><url>
<loc>https://localhost/public-post?page=2</loc>
<lastmod>2021-08-19T21:44:37Z</lastmod>
<changefreq>hourly</changefreq>
<priority>0.5</priority>
</url></urlset>
)
describe "generate/0" do
setup do
public_post =
%Post{
title: "Public post",
name: "public-post",
status: "publish",
type: "post",
date: ~N[2020-01-01T00:00:00],
content: """
Page 1
<!--nextpage-->
Page 2
"""
}
|> Repo.insert!()
%{
public_post: public_post,
}
end
test "generates results" do
with_mock Sitemap.Funcs, [
iso8601: fn -> "2021-08-19T21:44:37Z" end,
iso8601: & &1,
eraser: fn (elm) -> passthrough([elm]) end
] do
with_mock Legendary.Content.SitemapStorage, [write: fn (_name, _data) -> :ok end] do
assert :ok = perform(%{})
assert_called Legendary.Content.SitemapStorage.write(:file, String.trim(@xml))
end
end
end
end
end

View file

@ -1,7 +1,7 @@
defmodule Legendary.Content.PostsControllerTest do
use Legendary.Content.ConnCase
alias Legendary.Content.{Comment, Options, Posts, Repo, Term, TermRelationship, TermTaxonomy}
alias Legendary.Content.{Comment, Options, Post, Posts, Repo, Term, TermRelationship, TermTaxonomy}
@create_attrs %{
id: 123,
@ -165,6 +165,21 @@ defmodule Legendary.Content.PostsControllerTest do
assert html_response(conn, 200)
end
test "shows the post if the id has slashes", %{conn: conn} do
%Post{
name: "a/b/c",
content: "slashed id",
status: "publish",
type: "post",
date: ~N[2020-01-01T00:00:00]
}
|> Repo.insert!()
conn = get conn, Routes.nested_posts_path(conn, :show, ["a", "b", "c"])
assert html_response(conn, 200)
end
test "show a 404 if there's no match", %{conn: conn} do
assert_raise Phoenix.Router.NoRouteError, fn ->
get conn, Routes.posts_path(conn, :show, "blooper")

View file

@ -0,0 +1,29 @@
defmodule Legendary.Content.ErrorHelpersTest do
use Legendary.Content.DataCase
import Phoenix.HTML, only: [safe_to_string: 1]
import Phoenix.HTML.Form, only: [form_for: 3]
import Legendary.Content.ErrorHelpers
def form do
:example
|> form_for(
"/example",
as: :test_params,
errors: [error_field: {"is an error", []}]
)
end
describe "error_tag/2" do
test "generates a span with an invalid-feedback class" do
[safe] = error_tag(form(), :error_field)
assert safe_to_string(safe) =~ "invalid-feedback"
end
test "error_tag/3" do
[safe] = error_tag(form(), :error_field, class: "test-class")
assert safe_to_string(safe) =~ "test-class"
end
end
end

View file

@ -0,0 +1,17 @@
defmodule Legendary.Content.FeedsViewTest do
use Legendary.Content.DataCase
import Legendary.Content.FeedsView
alias Legendary.Content.Post
describe "unauthenticated_post?/1" do
test "with post password" do
refute unauthenticated_post?(%Post{password: "password"})
end
test "without post password" do
assert unauthenticated_post?(%Post{})
end
end
end

View file

@ -4,6 +4,8 @@ defmodule Legendary.Content.PostsViewTest do
import Legendary.Content.PostsView
import Phoenix.HTML, only: [safe_to_string: 1]
alias Legendary.Content.Post
test "auto_paragraph_tags/1 with nil" do
assert safe_to_string(auto_paragraph_tags(nil)) =~ ""
end
@ -11,4 +13,22 @@ defmodule Legendary.Content.PostsViewTest do
test "auto_paragraph_tags/1 with text" do
assert safe_to_string(auto_paragraph_tags("Bloop\n\nBloop")) =~ "<p>Bloop</p>\n<p>Bloop</p>"
end
describe "authenticated_for_post?/2" do
test "without password" do
assert authenticated_for_post?(nil, %Post{})
end
test "with post password that matches", %{conn: conn} do
with_mock Plug.Conn, [get_session: fn (_conn, :post_password) -> "password" end] do
assert authenticated_for_post?(conn, %Post{password: "password"})
end
end
test "with post password that does not match", %{conn: conn} do
with_mock Plug.Conn, [get_session: fn (_conn, :post_password) -> "password" end] do
refute authenticated_for_post?(conn, %Post{password: "password2"})
end
end
end
end

View file

@ -1,4 +1,4 @@
defmodule AuthWeb.Pow.ControllerCallbacks do
defmodule Legendary.AuthWeb.Pow.ControllerCallbacks do
@moduledoc """
Hook into Pow Controllers to provide additional framework feature. In particular,
we disconnect any active live views when a user logs out. This will cause the
@ -36,6 +36,7 @@ defmodule AuthWeb.Pow.ControllerCallbacks do
conn =
conn
|> Conn.delete_session(:live_socket_id)
|> Conn.delete_session(:current_user_id)
ControllerCallbacks.before_respond(
Pow.Phoenix.SessionController,

View file

@ -16,8 +16,12 @@ defmodule Legendary.CoreWeb.Plug.TestEndToEnd do
send_resp(conn, 200, "connection has already been checked out")
else
{:ok, _pid} = Agent.start_link(&checkout_shared_db_conn/0, name: :db_owner_agent)
{:ok, _} = load_test_seeds(conn)
send_resp(conn, 200, "connection checked out")
case load_test_seeds(conn) do
{:ok, _} ->
send_resp(conn, 200, "connection checked out")
{:error, msg} ->
send_resp(conn, 500, msg)
end
end
end
@ -40,7 +44,10 @@ defmodule Legendary.CoreWeb.Plug.TestEndToEnd do
Ecto.Repo.all_running()
|> Enum.map(fn repo ->
:ok = Ecto.Adapters.SQL.Sandbox.checkout(repo, ownership_timeout: :infinity)
:ok = Ecto.Adapters.SQL.Sandbox.mode(repo, {:shared, self()})
case Ecto.Adapters.SQL.Sandbox.mode(repo, {:shared, self()}) do
:ok -> :ok
:already_shared -> :ok
end
end)
end
@ -59,14 +66,16 @@ defmodule Legendary.CoreWeb.Plug.TestEndToEnd do
{:app, {:ok, app}} <- {:app, Map.fetch(conn.body_params, "app")},
true <- String.match?(seed_set, @valid_seed_set_characters),
true <- String.match?(app, @valid_app_characters) do
seed_path = "apps/#{app}/test/seed_sets/#{seed_set}.exs"
project_base = Path.expand(Path.join(__DIR__, "../../../../.."))
seed_path = Path.join(project_base, "apps/#{app}/test/seed_sets/#{seed_set}.exs")
try do
{result, _} = Code.eval_file(seed_path)
{:ok, result}
rescue
e in Code.LoadError ->
{:error, "could not load a seed set at #{seed_path}: #{e}"}
{:error, e.message}
end
else
{:app, :error} -> {:error, "app parameter is required if seed set is set"}

View file

@ -31,4 +31,8 @@ defmodule Legendary.CoreWeb.Router do
live_dashboard "/dashboard", metrics: Legendary.CoreWeb.Telemetry
end
end
if Mix.env() in [:e2e, :test] do
forward("/end-to-end", Legendary.CoreWeb.Plug.TestEndToEnd, otp_app: :app)
end
end

View file

@ -48,7 +48,7 @@ defmodule Legendary.CoreWeb.Helpers do
{type, rest_opts} = Keyword.pop(opts, :type, input_type(f, field))
{classes, rest_opts} = Keyword.pop(rest_opts, :class, default_classes_for_type(type))
{label_text, rest_opts} = Keyword.pop(rest_opts, :label)
{input_helper, _rest_opts} = Keyword.pop(rest_opts, :input_helper, input_type(f, field))
{input_helper, _rest_opts} = Keyword.pop(rest_opts, :input_helper, type)
error_classes =
if Keyword.get_values(f.errors, field) |> Enum.any?() do

View file

@ -9,11 +9,13 @@ defmodule Mix.Tasks.Legendary.CreateAdmin do
alias Ecto.Changeset
@shortdoc "Create an admin user."
def run(_) do
def run(args) do
Application.ensure_all_started(:core)
email = ExPrompt.string_required("Email: ")
password = ExPrompt.password("Password: ")
{switches, _, _} = OptionParser.parse(args, strict: [email: :string, password: :string])
email = Keyword.get_lazy(switches, :email, fn -> ExPrompt.string_required("Email: ") end)
password = Keyword.get_lazy(switches, :password, fn -> ExPrompt.password("Password: ") end)
params = %{
email: email,
@ -27,7 +29,7 @@ defmodule Mix.Tasks.Legendary.CreateAdmin do
|> Repo.insert!()
end
def maybe_confirm_email(changeset) do
defp maybe_confirm_email(changeset) do
field_list = User.__schema__(:fields)
case Enum.any?(field_list, &(&1 == :email_confirmed_at)) do

View file

@ -0,0 +1,33 @@
defmodule Legendary.AuthWeb.Pow.ControllerCallbacksTest do
use Legendary.CoreWeb.ConnCase, async: true
import Legendary.AuthWeb.Pow.ControllerCallbacks
import Plug.Conn, only: [assign: 3, get_session: 2]
alias Legendary.Auth.User
describe "before_respond/4" do
setup %{conn: conn} do
conn =
conn
|> init_test_session([])
|> assign(:current_user, %User{id: 123})
%{conn: conn}
end
test "sets the live_socket_id in session upon sign in", %{conn: conn} do
{:ok, conn} = before_respond(Pow.Phoenix.SessionController, :create, {:ok, conn}, [])
assert get_session(conn, "live_socket_id") == "users_sockets:123"
assert get_session(conn, "current_user_id") == 123
end
test "removes the live_socket_id and broadcasts a disconnect signal upon sign out", %{conn: conn, } do
{:ok, conn} = before_respond(Pow.Phoenix.SessionController, :delete, {:ok, conn}, [])
assert get_session(conn, "live_socket_id") == nil
assert get_session(conn, "current_user_id") == nil
end
end
end

View file

@ -1,8 +1,15 @@
defmodule Legendary.AuthWeb.HelpersTest do
use Legendary.CoreWeb.ConnCase
use Legendary.CoreWeb.ConnCase, async: true
import Legendary.AuthWeb.Helpers
alias Phoenix.LiveView.Socket
describe "current_user/1" do
test "can get a user from the assigns in a socket", do: assert current_user(%Socket{assigns: %{current_user: %{id: 867}}}).id == 867
test "can get a user from the __assigns__ in a socket", do: assert current_user(%Socket{assigns: %{__assigns__: %{current_user: %{id: 867}}}}).id == 867
end
describe "has_role?/2" do
test "with a user", %{conn: conn} do
conn =

View file

@ -1,5 +1,5 @@
defmodule Legendary.AuthWeb.Plugs.RequireAdminTest do
use Legendary.CoreWeb.ConnCase
use Legendary.CoreWeb.ConnCase, async: true
alias Legendary.AuthWeb.Plugs.RequireAdmin
alias Legendary.Auth.User

View file

@ -0,0 +1,38 @@
defmodule Legendary.CoreWeb.Plug.TestEndToEndTest do
use Legendary.CoreWeb.ConnCase
test "/db/setup can check out a connection", %{conn: conn} do
conn = post conn, "/end-to-end/db/setup", %{seed_set: "test_end_to_end_test", app: "core"}
assert response(conn, 200) =~ "connection checked out"
end
test "/db/setup twice is a no-op", %{conn: conn} do
conn = post conn, "/end-to-end/db/setup", %{seed_set: "test_end_to_end_test", app: "core"}
conn = post conn, "/end-to-end/db/setup", %{seed_set: "test_end_to_end_test", app: "core"}
assert response(conn, 200) =~ "connection has already been checked out"
end
test "/db/setup with no-existent seed set is an error", %{conn: conn} do
conn = post conn, "/end-to-end/db/setup", %{seed_set: "oops", app: "core"}
assert response(conn, 500) =~ "could not load"
assert response(conn, 500) =~ "oops.exs"
end
test "/db/teardown can check in a connection", %{conn: conn} do
conn = post conn, "/end-to-end/db/setup", %{seed_set: "test_end_to_end_test", app: "core"}
conn = post conn, "/end-to-end/db/teardown"
assert response(conn, 200) =~ "checked in database connection"
end
test "/db/teardown checking in twice is a no-op", %{conn: conn} do
conn = post conn, "/end-to-end/db/setup", %{seed_set: "test_end_to_end_test", app: "core"}
conn = post conn, "/end-to-end/db/teardown"
conn = post conn, "/end-to-end/db/teardown"
assert response(conn, 200) =~ "connection has already been checked back in"
end
end

View file

@ -1,5 +1,5 @@
defmodule Legendary.CoreWeb.HelpersTest do
use Legendary.CoreWeb.ConnCase
use Legendary.CoreWeb.ConnCase, async: true
import Legendary.CoreWeb.Helpers
import Ecto.Changeset,
@ -35,6 +35,17 @@ defmodule Legendary.CoreWeb.HelpersTest do
changeset
end
describe "current_user?/1" do
test "returns nil for a conn with no user", %{conn: conn}, do: refute conn |> setup_pow() |> current_user()
test "returns a user for a conn with a user", %{conn: conn} do
conn =
conn
|> setup_user(id: 456)
assert current_user(conn).id == 456
end
end
describe "has_role?/2" do
test "with a user", %{conn: conn} do
conn =
@ -105,6 +116,87 @@ defmodule Legendary.CoreWeb.HelpersTest do
assert markup =~ "<label"
end
test "styled_input for date_select" do
markup =
form()
|> styled_input(:no_error_field, type: :date_select)
|> safe_to_string()
assert markup =~ "<select"
end
describe "styled_button/1" do
test "makes a button with content" do
markup =
styled_button("Push Me")
|> safe_to_string()
assert markup =~ ">Push Me"
assert markup =~ "<button"
end
end
describe "styled_button_link/2" do
test "makes a link with content and attributes" do
markup =
styled_button_link("Push Me", to: "#anchor")
|> safe_to_string()
assert markup =~ ">Push Me"
assert markup =~ "<a"
assert markup =~ ~s(href="#anchor")
end
end
describe "paginator/3" do
def reflector(range, current), do: {range, current}
test "works with only one page" do
assert paginator(1..1, 1, &reflector/2) == [{1..1, 1}]
end
test "works with two pages" do
assert paginator(1..2, 1, &reflector/2) == [{1..2, 1}, {1..2, 2}]
end
test "works with three pages" do
assert paginator(1..3, 3, &reflector/2) == [{1..3, 1}, {1..3, 2}, {1..3, 3}]
end
test "works with many pages" do
assert paginator(1..10, 7, &reflector/2) == [{1..10, 1}, {1..10, 6}, {1..10, 7}, {1..10, 8}, {1..10, 10}]
end
end
describe "group_rounding_class/3" do
test "handles the only element", do: assert group_rounding_class(1..1, 1) == "rounded-l rounded-r"
test "handles the first element", do: assert group_rounding_class(1..2, 1) == "rounded-l"
test "handles the last element", do: assert group_rounding_class(1..2, 2) == "rounded-r"
test "handles middle elements", do: assert group_rounding_class(1..3, 2) == ""
test "handles custom classes", do: assert group_rounding_class(1..3, 1, ["custom", "", ""]) == "custom"
end
describe "floating_form/3" do
test "includes title and content" do
markup =
floating_form("Test Title", %{action: "test"}, do: "Test Content")
|> safe_to_string()
assert markup =~ "Test Title"
assert markup =~ "Test Content"
end
end
describe "floating_page_wrapper/1" do
test "includes content" do
markup =
floating_page_wrapper(do: "Test Content")
|> safe_to_string()
assert markup =~ "Test Content"
end
end
test "pow_extension_enabled?/1" do
assert pow_extension_enabled?(PowEmailConfirmation) == true
assert pow_extension_enabled?(:donkdonk) == false

View file

@ -0,0 +1,17 @@
defmodule Mix.Tasks.Legendary.CreateAdminTest do
use Legendary.Core.DataCase
import Mix.Tasks.Legendary.CreateAdmin
alias Legendary.Auth.User
describe "run/1" do
test "creates an admin user" do
run(["--email=test@example.com", "--password=openseasame"])
user = from(u in User, where: u.email == "test@example.com") |> Repo.one()
assert %User{email: "test@example.com"} = user
end
end
end

View file

@ -0,0 +1,14 @@
defmodule Legendary.Core.SharedDBConnectionPoolTest do
use Legendary.Core.DataCase
import Legendary.Core.SharedDBConnectionPool
test "child_spec/2" do
spec = child_spec({Postgrex.Protocol, []})
assert %{id: Legendary.Core.SharedDBConnectionPool, start: start} = spec
assert {Legendary.Core.SharedDBConnectionPool, :start_link, [{Postgrex.Protocol, opts}]} = start
assert [{:name, hashed_name}] = opts
assert Atom.to_string(hashed_name) =~ "Legendary.Core.SharedDBConnectionPool."
end
end

View file

@ -28,6 +28,22 @@ defmodule Legendary.CoreWeb.ConnCase do
# The default endpoint for testing
@endpoint Legendary.CoreWeb.Endpoint
def setup_pow(conn) do
conn
|> Pow.Plug.put_config(current_user_assigns_key: :current_user)
end
def setup_user(conn, attrs \\ []) do
user =
attrs
|> Enum.into(%{})
|> (& struct(Legendary.Auth.User, &1)).()
conn
|> setup_pow()
|> Pow.Plug.assign_current_user(user, [])
end
end
end

View file

@ -1,24 +0,0 @@
defmodule Legendary.Core.Cluster.EmptyClusterStrategy do
@moduledoc """
A libcluster nil strategy that always returns no nodes.
"""
use GenServer
use Cluster.Strategy
alias Cluster.Strategy.State
def start_link([%State{} = state]) do
new_state = %State{state | :meta => []}
GenServer.start_link(__MODULE__, [new_state])
end
@impl true
def init([state]) do
{:ok, state, :infinity}
end
@impl true
def handle_info(_, state) do
{:noreply, state, :infinity}
end
end

View file

@ -40,7 +40,7 @@ config :core, :pow,
user: Legendary.Auth.User,
repo: Legendary.Core.Repo,
extensions: [PowEmailConfirmation, PowPersistentSession, PowResetPassword],
controller_callbacks: AuthWeb.Pow.ControllerCallbacks,
controller_callbacks: Legendary.AuthWeb.Pow.ControllerCallbacks,
mailer_backend: Legendary.AuthWeb.Pow.Mailer,
web_mailer_module: Legendary.AuthWeb,
web_module: Legendary.AuthWeb,

View file

@ -42,9 +42,4 @@ config :content, Oban, crontab: false, queues: false, plugins: false
config :logger, level: :warn
config :libcluster,
topologies: [
# erlang_hosts: [
# strategy: Legendary.Core.Cluster.EmptyClusterStrategy
# ]
]
config :libcluster, topologies: []