towerops/test/towerops_web/controllers/invitation_controller_test.exs

112 lines
3.5 KiB
Elixir

defmodule ToweropsWeb.InvitationControllerTest do
use ToweropsWeb.ConnCase, async: true
import Towerops.AccountsFixtures
import Towerops.OrganizationsFixtures
alias Towerops.Organizations
setup do
user = user_fixture()
organization = organization_fixture(user.id)
%{user: user, organization: organization}
end
defp create_invitation(org, user, attrs \\ %{}) do
defaults = %{
organization_id: org.id,
invited_by_id: user.id,
email: "invited-#{System.unique_integer()}@example.com",
role: "member"
}
{:ok, invitation} = Organizations.create_invitation(Map.merge(defaults, attrs))
invitation
end
describe "GET /invitations/:token" do
test "redirects to registration with token when not logged in", %{
conn: conn,
organization: organization,
user: user
} do
invitation = create_invitation(organization, user)
conn = get(conn, ~p"/invitations/#{invitation.token}")
assert redirected_to(conn) =~ ~p"/users/register"
assert redirected_to(conn) =~ "invitation_token=#{invitation.token}"
end
test "accepts invitation and redirects when logged in", %{
conn: conn,
organization: organization,
user: inviter
} do
invitation = create_invitation(organization, inviter)
# Log in as a different user who will accept the invitation
accepting_user = user_fixture()
conn = log_in_user(conn, accepting_user)
conn = get(conn, ~p"/invitations/#{invitation.token}")
assert redirected_to(conn) =~ "/settings"
assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "joined"
end
test "handles invalid/non-existent token", %{conn: conn} do
conn = get(conn, ~p"/invitations/invalid-token-abc123")
assert redirected_to(conn) == ~p"/"
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "invalid"
end
test "handles expired invitation", %{conn: conn, organization: organization, user: user} do
# Create an invitation with an already-passed expiration
invitation = create_invitation(organization, user)
# Manually expire it
invitation
|> Ecto.Changeset.change(%{expires_at: DateTime.utc_now() |> DateTime.add(-1, :day) |> DateTime.truncate(:second)})
|> Towerops.Repo.update!()
conn = get(conn, ~p"/invitations/#{invitation.token}")
assert redirected_to(conn) == ~p"/"
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "invalid"
end
test "handles already-accepted invitation", %{conn: conn, organization: organization, user: user} do
invitation = create_invitation(organization, user)
# Mark as accepted
invitation
|> Ecto.Changeset.change(%{accepted_at: DateTime.utc_now() |> DateTime.truncate(:second)})
|> Towerops.Repo.update!()
conn = get(conn, ~p"/invitations/#{invitation.token}")
assert redirected_to(conn) == ~p"/"
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "invalid"
end
test "shows error when user is already a member", %{
conn: conn,
organization: organization,
user: inviter
} do
invitation = create_invitation(organization, inviter)
# The inviter is already a member, so accepting should fail
conn = log_in_user(conn, inviter)
conn = get(conn, ~p"/invitations/#{invitation.token}")
# Should redirect with an error about already being a member
assert redirected_to(conn) =~ "/dashboard"
assert Phoenix.Flash.get(conn.assigns.flash, :error) =~ "already"
end
end
end