fix n+1 queries with invite card
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
shibao 2023-03-18 13:46:26 -04:00
parent 8f288afeb9
commit 0d4deb6805
16 changed files with 164 additions and 293 deletions

View File

@ -100,13 +100,23 @@ defmodule Memex.Accounts.Invites do
end
end
@spec get_use_count(Invite.t(), User.t()) :: non_neg_integer()
def get_use_count(%Invite{id: invite_id}, %User{role: :admin}) do
Repo.one(
@spec get_use_count(Invite.t(), User.t()) :: non_neg_integer() | nil
def get_use_count(%Invite{id: invite_id} = invite, user) do
[invite] |> get_use_counts(user) |> Map.get(invite_id)
end
@spec get_use_counts([Invite.t()], User.t()) ::
%{optional(Invite.id()) => non_neg_integer()}
def get_use_counts(invites, %User{role: :admin}) do
invite_ids = invites |> Enum.map(fn %{id: invite_id} -> invite_id end)
Repo.all(
from u in User,
where: u.invite_id == ^invite_id,
select: count(u.id)
where: u.invite_id in ^invite_ids,
group_by: u.invite_id,
select: {u.invite_id, count(u.id)}
)
|> Map.new()
end
@spec decrement_invite_changeset(Invite.t()) :: Invite.changeset()

View File

@ -4,7 +4,7 @@ defmodule MemexWeb.CoreComponents do
"""
use Phoenix.Component
import MemexWeb.{Gettext, ViewHelpers}
alias Memex.{Accounts, Accounts.Invite, Accounts.Invites, Accounts.User}
alias Memex.{Accounts, Accounts.Invite, Accounts.User}
alias Memex.Contexts.Context
alias Memex.Notes.Note
alias Memex.Pipelines.Steps.Step
@ -77,64 +77,12 @@ defmodule MemexWeb.CoreComponents do
def user_card(assigns)
attr :invite, Invite, required: true
attr :use_count, :integer, default: nil
attr :current_user, User, required: true
slot(:inner_block)
slot(:code_actions)
def invite_card(%{invite: invite, current_user: current_user} = assigns) do
assigns = assigns |> assign(:use_count, Invites.get_use_count(invite, current_user))
~H"""
<div class="px-8 py-4 flex flex-col justify-center items-center space-y-4
bg-primary-900
border border-gray-400 rounded-lg shadow-lg hover:shadow-md
transition-all duration-300 ease-in-out">
<h1 class="title text-xl">
<%= @invite.name %>
</h1>
<%= if @invite.disabled_at |> is_nil() do %>
<h2 class="title text-md">
<%= if @invite.uses_left do %>
<%= gettext(
"uses left: %{uses_left_count}",
uses_left_count: @invite.uses_left
) %>
<% else %>
<%= gettext("uses left: unlimited") %>
<% end %>
</h2>
<% else %>
<h2 class="title text-md">
<%= gettext("invite disabled") %>
</h2>
<% end %>
<.qr_code
content={Routes.user_registration_url(Endpoint, :new, invite: @invite.token)}
filename={@invite.name}
/>
<h2 :if={@use_count != 0} class="title text-md">
<%= gettext("uses: %{uses_count}", uses_count: @use_count) %>
</h2>
<div class="flex flex-row flex-wrap justify-center items-center">
<code
id={"code-#{@invite.id}"}
class="mx-2 my-1 text-xs px-4 py-2 rounded-lg text-center break-all
text-primary-400 bg-primary-800"
phx-no-format
><%= Routes.user_registration_url(Endpoint, :new, invite: @invite.token) %></code>
<%= if @code_actions, do: render_slot(@code_actions) %>
</div>
<div :if={@inner_block} class="flex space-x-4 justify-center items-center">
<%= render_slot(@inner_block) %>
</div>
</div>
"""
end
def invite_card(assigns)
attr :id, :string, required: true
attr :datetime, :any, required: true, doc: "A `DateTime` struct or nil"

View File

@ -0,0 +1,48 @@
<div class="px-8 py-4 flex flex-col justify-center items-center space-y-4
bg-primary-900
border border-gray-400 rounded-lg shadow-lg hover:shadow-md
transition-all duration-300 ease-in-out">
<h1 class="title text-xl">
<%= @invite.name %>
</h1>
<%= if @invite.disabled_at |> is_nil() do %>
<h2 class="title text-md">
<%= if @invite.uses_left do %>
<%= gettext(
"uses left: %{uses_left_count}",
uses_left_count: @invite.uses_left
) %>
<% else %>
<%= gettext("uses left: unlimited") %>
<% end %>
</h2>
<% else %>
<h2 class="title text-md">
<%= gettext("invite disabled") %>
</h2>
<% end %>
<.qr_code
content={Routes.user_registration_url(Endpoint, :new, invite: @invite.token)}
filename={@invite.name}
/>
<h2 :if={@use_count && @use_count != 0} class="title text-md">
<%= gettext("uses: %{uses_count}", uses_count: @use_count) %>
</h2>
<div class="flex flex-row flex-wrap justify-center items-center">
<code
id={"code-#{@invite.id}"}
class="mx-2 my-1 text-xs px-4 py-2 rounded-lg text-center break-all
text-primary-400 bg-primary-800"
phx-no-format
><%= Routes.user_registration_url(Endpoint, :new, invite: @invite.token) %></code>
<%= if @code_actions, do: render_slot(@code_actions) %>
</div>
<div :if={@inner_block} class="flex space-x-4 justify-center items-center">
<%= render_slot(@inner_block) %>
</div>
</div>

View File

@ -148,7 +148,9 @@ defmodule MemexWeb.InviteLive.Index do
|> Map.get(:admin, [])
|> Enum.reject(fn %{id: user_id} -> user_id == current_user.id end)
use_counts = invites |> Invites.get_use_counts(current_user)
users = all_users |> Map.get(:user, [])
socket |> assign(invites: invites, admins: admins, users: users)
socket |> assign(invites: invites, use_counts: use_counts, admins: admins, users: users)
end
end

View File

@ -14,7 +14,12 @@
<% end %>
<div class="flex flex-col justify-center items-stretch space-y-4">
<.invite_card :for={invite <- @invites} invite={invite} current_user={@current_user}>
<.invite_card
:for={invite <- @invites}
invite={invite}
current_user={@current_user}
use_count={Map.get(@use_counts, invite.id)}
>
<:code_actions>
<form phx-submit="copy_to_clipboard">
<button

View File

@ -45,7 +45,7 @@ msgstr ""
msgid "change password"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:89
#: lib/memex_web/live/invite_live/index.html.heex:94
#, elixir-autogen, elixir-format
msgid "create invite"
msgstr ""
@ -156,12 +156,12 @@ msgstr ""
msgid "export data as json"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:28
#: lib/memex_web/live/invite_live/index.html.heex:33
#, elixir-autogen, elixir-format
msgid "copy"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:25
#: lib/memex_web/live/invite_live/index.html.heex:30
#, elixir-autogen, elixir-format
msgid "copy invite link for %{invite_name}"
msgstr ""
@ -189,7 +189,7 @@ msgstr ""
msgid "delete %{step_title}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:53
#: lib/memex_web/live/invite_live/index.html.heex:58
#, elixir-autogen, elixir-format
msgid "delete invite for %{invite_name}"
msgstr ""
@ -214,7 +214,7 @@ msgstr ""
msgid "edit %{step_title}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:36
#: lib/memex_web/live/invite_live/index.html.heex:41
#, elixir-autogen, elixir-format
msgid "edit invite for %{invite_name}"
msgstr ""

View File

@ -45,7 +45,7 @@ msgstr ""
msgid "change password"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:89
#: lib/memex_web/live/invite_live/index.html.heex:94
#, elixir-autogen, elixir-format
msgid "create invite"
msgstr ""
@ -156,12 +156,12 @@ msgstr ""
msgid "export data as json"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:28
#: lib/memex_web/live/invite_live/index.html.heex:33
#, elixir-autogen, elixir-format
msgid "copy"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:25
#: lib/memex_web/live/invite_live/index.html.heex:30
#, elixir-autogen, elixir-format
msgid "copy invite link for %{invite_name}"
msgstr ""
@ -189,7 +189,7 @@ msgstr ""
msgid "delete %{step_title}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:53
#: lib/memex_web/live/invite_live/index.html.heex:58
#, elixir-autogen, elixir-format
msgid "delete invite for %{invite_name}"
msgstr ""
@ -214,7 +214,7 @@ msgstr ""
msgid "edit %{step_title}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:36
#: lib/memex_web/live/invite_live/index.html.heex:41
#, elixir-autogen, elixir-format
msgid "edit invite for %{invite_name}"
msgstr ""

View File

@ -84,7 +84,7 @@ msgstr ""
msgid "current password"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:65
#: lib/memex_web/live/invite_live/index.html.heex:70
#, elixir-autogen, elixir-format
msgid "disable"
msgstr ""
@ -117,7 +117,7 @@ msgstr ""
msgid "email unconfirmed"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:65
#: lib/memex_web/live/invite_live/index.html.heex:70
#, elixir-autogen, elixir-format
msgid "enable"
msgstr ""
@ -143,7 +143,7 @@ msgstr ""
msgid "instance information"
msgstr ""
#: lib/memex_web/components/core_components.ex:109
#: lib/memex_web/components/core_components/invite_card.html.heex:22
#, elixir-autogen, elixir-format
msgid "invite disabled"
msgstr ""
@ -267,7 +267,7 @@ msgstr ""
msgid "select privacy"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:80
#: lib/memex_web/live/invite_live/index.html.heex:85
#, elixir-autogen, elixir-format
msgid "set unlimited"
msgstr ""
@ -292,7 +292,7 @@ msgstr ""
msgid "tags"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:125
#: lib/memex_web/live/invite_live/index.html.heex:130
#, elixir-autogen, elixir-format
msgid "users"
msgstr ""
@ -614,7 +614,7 @@ msgstr ""
msgid "user registered on%{registered_datetime}"
msgstr ""
#: lib/memex_web/components/core_components.ex:104
#: lib/memex_web/components/core_components/invite_card.html.heex:17
#, elixir-autogen, elixir-format
msgid "uses left: unlimited"
msgstr ""
@ -624,12 +624,12 @@ msgstr ""
msgid "read more on how to use memEx"
msgstr ""
#: lib/memex_web/components/core_components.ex:99
#: lib/memex_web/components/core_components/invite_card.html.heex:12
#, elixir-autogen, elixir-format, fuzzy
msgid "uses left: %{uses_left_count}"
msgstr ""
#: lib/memex_web/components/core_components.ex:119
#: lib/memex_web/components/core_components/invite_card.html.heex:32
#, elixir-autogen, elixir-format
msgid "uses: %{uses_count}"
msgstr ""
@ -649,7 +649,7 @@ msgstr ""
msgid "Leave \"Uses left\" blank to make invite unlimited"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:97
#: lib/memex_web/live/invite_live/index.html.heex:102
#, elixir-autogen, elixir-format, fuzzy
msgid "admins"
msgstr ""

View File

@ -65,7 +65,7 @@ msgstr ""
msgid "are you sure you want to change your language?"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:48
#: lib/memex_web/live/invite_live/index.html.heex:53
#, elixir-autogen, elixir-format
msgid "are you sure you want to delete the invite for %{invite_name}?"
msgstr ""
@ -80,7 +80,7 @@ msgstr ""
msgid "are you sure you want to log out?"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:75
#: lib/memex_web/live/invite_live/index.html.heex:80
#, elixir-autogen, elixir-format
msgid "are you sure you want to make %{invite_name} unlimited?"
msgstr ""
@ -151,8 +151,8 @@ msgstr ""
msgid "your account has been deleted"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:108
#: lib/memex_web/live/invite_live/index.html.heex:136
#: lib/memex_web/live/invite_live/index.html.heex:113
#: lib/memex_web/live/invite_live/index.html.heex:141
#, elixir-autogen, elixir-format, fuzzy
msgid "are you sure you want to delete %{email}? this action is permanent!"
msgstr ""

View File

@ -82,7 +82,7 @@ msgstr ""
msgid "current password"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:65
#: lib/memex_web/live/invite_live/index.html.heex:70
#, elixir-autogen, elixir-format
msgid "disable"
msgstr ""
@ -115,7 +115,7 @@ msgstr ""
msgid "email unconfirmed"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:65
#: lib/memex_web/live/invite_live/index.html.heex:70
#, elixir-autogen, elixir-format
msgid "enable"
msgstr ""
@ -141,7 +141,7 @@ msgstr ""
msgid "instance information"
msgstr ""
#: lib/memex_web/components/core_components.ex:109
#: lib/memex_web/components/core_components/invite_card.html.heex:22
#, elixir-autogen, elixir-format
msgid "invite disabled"
msgstr ""
@ -265,7 +265,7 @@ msgstr ""
msgid "select privacy"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:80
#: lib/memex_web/live/invite_live/index.html.heex:85
#, elixir-autogen, elixir-format
msgid "set unlimited"
msgstr ""
@ -290,7 +290,7 @@ msgstr ""
msgid "tags"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:125
#: lib/memex_web/live/invite_live/index.html.heex:130
#, elixir-autogen, elixir-format
msgid "users"
msgstr ""
@ -612,7 +612,7 @@ msgstr ""
msgid "user registered on%{registered_datetime}"
msgstr ""
#: lib/memex_web/components/core_components.ex:104
#: lib/memex_web/components/core_components/invite_card.html.heex:17
#, elixir-autogen, elixir-format
msgid "uses left: unlimited"
msgstr ""
@ -622,12 +622,12 @@ msgstr ""
msgid "read more on how to use memEx"
msgstr ""
#: lib/memex_web/components/core_components.ex:99
#: lib/memex_web/components/core_components/invite_card.html.heex:12
#, elixir-autogen, elixir-format
msgid "uses left: %{uses_left_count}"
msgstr ""
#: lib/memex_web/components/core_components.ex:119
#: lib/memex_web/components/core_components/invite_card.html.heex:32
#, elixir-autogen, elixir-format
msgid "uses: %{uses_count}"
msgstr ""
@ -647,7 +647,7 @@ msgstr ""
msgid "Leave \"Uses left\" blank to make invite unlimited"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:97
#: lib/memex_web/live/invite_live/index.html.heex:102
#, elixir-autogen, elixir-format
msgid "admins"
msgstr ""

View File

@ -46,7 +46,7 @@ msgstr ""
msgid "change password"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:89
#: lib/memex_web/live/invite_live/index.html.heex:94
#, elixir-autogen, elixir-format
msgid "create invite"
msgstr ""
@ -157,12 +157,12 @@ msgstr ""
msgid "export data as json"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:28
#: lib/memex_web/live/invite_live/index.html.heex:33
#, elixir-autogen, elixir-format
msgid "copy"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:25
#: lib/memex_web/live/invite_live/index.html.heex:30
#, elixir-autogen, elixir-format
msgid "copy invite link for %{invite_name}"
msgstr ""
@ -190,7 +190,7 @@ msgstr ""
msgid "delete %{step_title}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:53
#: lib/memex_web/live/invite_live/index.html.heex:58
#, elixir-autogen, elixir-format
msgid "delete invite for %{invite_name}"
msgstr ""
@ -215,7 +215,7 @@ msgstr ""
msgid "edit %{step_title}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:36
#: lib/memex_web/live/invite_live/index.html.heex:41
#, elixir-autogen, elixir-format
msgid "edit invite for %{invite_name}"
msgstr ""

View File

@ -83,7 +83,7 @@ msgstr ""
msgid "current password"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:65
#: lib/memex_web/live/invite_live/index.html.heex:70
#, elixir-autogen, elixir-format
msgid "disable"
msgstr ""
@ -116,7 +116,7 @@ msgstr ""
msgid "email unconfirmed"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:65
#: lib/memex_web/live/invite_live/index.html.heex:70
#, elixir-autogen, elixir-format
msgid "enable"
msgstr ""
@ -142,7 +142,7 @@ msgstr ""
msgid "instance information"
msgstr ""
#: lib/memex_web/components/core_components.ex:109
#: lib/memex_web/components/core_components/invite_card.html.heex:22
#, elixir-autogen, elixir-format
msgid "invite disabled"
msgstr ""
@ -266,7 +266,7 @@ msgstr ""
msgid "select privacy"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:80
#: lib/memex_web/live/invite_live/index.html.heex:85
#, elixir-autogen, elixir-format
msgid "set unlimited"
msgstr ""
@ -291,7 +291,7 @@ msgstr ""
msgid "tags"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:125
#: lib/memex_web/live/invite_live/index.html.heex:130
#, elixir-autogen, elixir-format
msgid "users"
msgstr ""
@ -613,7 +613,7 @@ msgstr ""
msgid "user registered on%{registered_datetime}"
msgstr ""
#: lib/memex_web/components/core_components.ex:104
#: lib/memex_web/components/core_components/invite_card.html.heex:17
#, elixir-autogen, elixir-format
msgid "uses left: unlimited"
msgstr ""
@ -623,12 +623,12 @@ msgstr ""
msgid "read more on how to use memEx"
msgstr ""
#: lib/memex_web/components/core_components.ex:99
#: lib/memex_web/components/core_components/invite_card.html.heex:12
#, elixir-autogen, elixir-format
msgid "uses left: %{uses_left_count}"
msgstr ""
#: lib/memex_web/components/core_components.ex:119
#: lib/memex_web/components/core_components/invite_card.html.heex:32
#, elixir-autogen, elixir-format
msgid "uses: %{uses_count}"
msgstr ""
@ -648,7 +648,7 @@ msgstr ""
msgid "Leave \"Uses left\" blank to make invite unlimited"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:97
#: lib/memex_web/live/invite_live/index.html.heex:102
#, elixir-autogen, elixir-format
msgid "admins"
msgstr ""

View File

@ -66,7 +66,7 @@ msgstr ""
msgid "are you sure you want to change your language?"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:48
#: lib/memex_web/live/invite_live/index.html.heex:53
#, elixir-autogen, elixir-format
msgid "are you sure you want to delete the invite for %{invite_name}?"
msgstr ""
@ -81,7 +81,7 @@ msgstr ""
msgid "are you sure you want to log out?"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:75
#: lib/memex_web/live/invite_live/index.html.heex:80
#, elixir-autogen, elixir-format
msgid "are you sure you want to make %{invite_name} unlimited?"
msgstr ""
@ -152,8 +152,8 @@ msgstr ""
msgid "your account has been deleted"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:108
#: lib/memex_web/live/invite_live/index.html.heex:136
#: lib/memex_web/live/invite_live/index.html.heex:113
#: lib/memex_web/live/invite_live/index.html.heex:141
#, elixir-autogen, elixir-format
msgid "are you sure you want to delete %{email}? this action is permanent!"
msgstr ""

View File

@ -65,7 +65,7 @@ msgstr ""
msgid "are you sure you want to change your language?"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:48
#: lib/memex_web/live/invite_live/index.html.heex:53
#, elixir-autogen, elixir-format
msgid "are you sure you want to delete the invite for %{invite_name}?"
msgstr ""
@ -80,7 +80,7 @@ msgstr ""
msgid "are you sure you want to log out?"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:75
#: lib/memex_web/live/invite_live/index.html.heex:80
#, elixir-autogen, elixir-format
msgid "are you sure you want to make %{invite_name} unlimited?"
msgstr ""
@ -151,8 +151,8 @@ msgstr ""
msgid "your account has been deleted"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:108
#: lib/memex_web/live/invite_live/index.html.heex:136
#: lib/memex_web/live/invite_live/index.html.heex:113
#: lib/memex_web/live/invite_live/index.html.heex:141
#, elixir-autogen, elixir-format
msgid "are you sure you want to delete %{email}? this action is permanent!"
msgstr ""

View File

@ -53,7 +53,7 @@ defmodule Memex.InvitesTest do
test "get_use_count/2 returns the correct invite usage",
%{invite: %{token: token} = invite, current_user: current_user} do
assert 0 == Invites.get_use_count(invite, current_user)
assert Invites.get_use_count(invite, current_user) |> is_nil()
assert {:ok, _user} =
Accounts.register_user(
@ -72,6 +72,40 @@ defmodule Memex.InvitesTest do
assert 2 == Invites.get_use_count(invite, current_user)
end
test "get_use_counts/2 returns the correct invite usage",
%{invite: %{id: invite_id, token: token} = invite, current_user: current_user} do
{:ok, %{id: another_invite_id, token: another_token} = another_invite} =
Invites.create_invite(current_user, @valid_attrs)
assert [invite, another_invite] |> Invites.get_use_counts(current_user) == %{}
assert {:ok, _user} =
Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()},
token
)
assert {:ok, _user} =
Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()},
another_token
)
use_counts = [invite, another_invite] |> Invites.get_use_counts(current_user)
assert %{^invite_id => 1} = use_counts
assert %{^another_invite_id => 1} = use_counts
assert {:ok, _user} =
Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()},
token
)
use_counts = [invite, another_invite] |> Invites.get_use_counts(current_user)
assert %{^invite_id => 2} = use_counts
assert %{^another_invite_id => 1} = use_counts
end
test "use_invite/1 successfully uses an unlimited invite",
%{invite: %{token: token} = invite, current_user: current_user} do
{:ok, invite} = Invites.update_invite(invite, %{uses_left: nil}, current_user)

View File

@ -1,176 +0,0 @@
defmodule Memex.InvitesTest do
@moduledoc """
This module tests the Memex.Accounts.Invites context
"""
use Memex.DataCase
alias Ecto.Changeset
alias Memex.Accounts
alias Memex.Accounts.{Invite, Invites}
@moduletag :invites_test
@valid_attrs %{
"name" => "some name"
}
@invalid_attrs %{
"name" => nil,
"token" => nil
}
describe "invites" do
setup do
current_user = admin_fixture()
{:ok, invite} = Invites.create_invite(current_user, @valid_attrs)
[invite: invite, current_user: current_user]
end
test "list_invites/0 returns all invites", %{invite: invite, current_user: current_user} do
assert Invites.list_invites(current_user) == [invite]
end
test "get_invite!/1 returns the invite with given id",
%{invite: invite, current_user: current_user} do
assert Invites.get_invite!(invite.id, current_user) == invite
end
test "valid_invite_token? returns for valid and invalid invite tokens",
%{invite: %{token: token}} do
refute Invites.valid_invite_token?(nil)
refute Invites.valid_invite_token?("")
assert Invites.valid_invite_token?(token)
end
test "valid_invite_token? does not return true for a disabled invite by token",
%{invite: %{token: token} = invite, current_user: current_user} do
assert Invites.valid_invite_token?(token)
{:ok, _invite} = Invites.update_invite(invite, %{uses_left: 1}, current_user)
{:ok, _invite} = Invites.use_invite(token)
refute Invites.valid_invite_token?(token)
end
test "get_use_count/2 returns the correct invite usage",
%{invite: %{token: token} = invite, current_user: current_user} do
assert 0 == Invites.get_use_count(invite, current_user)
assert {:ok, _user} =
Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()},
token
)
assert 1 == Invites.get_use_count(invite, current_user)
assert {:ok, _user} =
Accounts.register_user(
%{"email" => unique_user_email(), "password" => valid_user_password()},
token
)
assert 2 == Invites.get_use_count(invite, current_user)
end
test "use_invite/1 successfully uses an unlimited invite",
%{invite: %{token: token} = invite, current_user: current_user} do
{:ok, invite} = Invites.update_invite(invite, %{uses_left: nil}, current_user)
assert {:ok, ^invite} = Invites.use_invite(token)
assert {:ok, ^invite} = Invites.use_invite(token)
assert {:ok, ^invite} = Invites.use_invite(token)
end
test "use_invite/1 successfully decrements an invite",
%{invite: %{token: token} = invite, current_user: current_user} do
{:ok, _invite} = Invites.update_invite(invite, %{uses_left: 10}, current_user)
assert {:ok, %{uses_left: 9}} = Invites.use_invite(token)
assert {:ok, %{uses_left: 8}} = Invites.use_invite(token)
assert {:ok, %{uses_left: 7}} = Invites.use_invite(token)
end
test "use_invite/1 successfully disactivates an invite",
%{invite: %{token: token} = invite, current_user: current_user} do
{:ok, _invite} = Invites.update_invite(invite, %{uses_left: 1}, current_user)
assert {:ok, %{uses_left: 0, disabled_at: disabled_at}} = Invites.use_invite(token)
assert not is_nil(disabled_at)
end
test "use_invite/1 does not work on disactivated invite",
%{invite: %{token: token} = invite, current_user: current_user} do
{:ok, _invite} = Invites.update_invite(invite, %{uses_left: 1}, current_user)
{:ok, _invite} = Invites.use_invite(token)
assert {:error, :invalid_token} = Invites.use_invite(token)
end
test "create_invite/1 with valid data creates an unlimited invite",
%{current_user: current_user} do
assert {:ok, %Invite{} = invite} =
Invites.create_invite(current_user, %{
"name" => "some name"
})
assert invite.name == "some name"
end
test "create_invite/1 with valid data creates a limited invite",
%{current_user: current_user} do
assert {:ok, %Invite{} = invite} =
Invites.create_invite(current_user, %{
"name" => "some name",
"uses_left" => 10
})
assert invite.name == "some name"
assert invite.uses_left == 10
end
test "create_invite/1 with invalid data returns error changeset",
%{current_user: current_user} do
assert {:error, %Changeset{}} = Invites.create_invite(current_user, @invalid_attrs)
end
test "update_invite/2 can set an invite to be limited",
%{invite: invite, current_user: current_user} do
assert {:ok, %Invite{} = new_invite} =
Invites.update_invite(
invite,
%{"name" => "some updated name", "uses_left" => 5},
current_user
)
assert new_invite.name == "some updated name"
assert new_invite.uses_left == 5
end
test "update_invite/2 can set an invite to be unlimited",
%{invite: invite, current_user: current_user} do
{:ok, invite} = Invites.update_invite(invite, %{"uses_left" => 5}, current_user)
assert {:ok, %Invite{} = new_invite} =
Invites.update_invite(
invite,
%{"name" => "some updated name", "uses_left" => nil},
current_user
)
assert new_invite.name == "some updated name"
assert new_invite.uses_left |> is_nil()
end
test "update_invite/2 with invalid data returns error changeset",
%{invite: invite, current_user: current_user} do
assert {:error, %Changeset{}} = Invites.update_invite(invite, @invalid_attrs, current_user)
assert invite == Invites.get_invite!(invite.id, current_user)
end
test "delete_invite/1 deletes the invite", %{invite: invite, current_user: current_user} do
assert {:ok, %Invite{}} = Invites.delete_invite(invite, current_user)
assert_raise Ecto.NoResultsError, fn -> Invites.get_invite!(invite.id, current_user) end
end
test "delete_invite!/1 deletes the invite", %{invite: invite, current_user: current_user} do
assert %Invite{} = Invites.delete_invite!(invite, current_user)
assert_raise Ecto.NoResultsError, fn -> Invites.get_invite!(invite.id, current_user) end
end
end
end