towerops/test/towerops/organizations_test.exs
Graham McIntire 7d4f7dd7d4
Add active page navigation highlighting
- Add active_page attribute to authenticated layout
- Update nav_link component to show active state with border and text styling
- Update all LiveView templates to pass active_page parameter
- Fix datetime truncation issues in tests and schemas for :utc_datetime fields
- Fix dashboard test assertions for equipment status text
2026-01-03 12:28:26 -06:00

394 lines
14 KiB
Elixir

defmodule Towerops.OrganizationsTest do
use Towerops.DataCase
import Towerops.AccountsFixtures
alias Towerops.Organizations
describe "organizations" do
setup do
user = user_fixture()
%{user: user}
end
test "list_user_organizations/1 returns all organizations for a user", %{user: user} do
{:ok, org1} = Organizations.create_organization(%{name: "Org 1"}, user.id)
{:ok, org2} = Organizations.create_organization(%{name: "Org 2"}, user.id)
orgs = Organizations.list_user_organizations(user.id)
assert length(orgs) == 2
org_ids = Enum.map(orgs, & &1.id)
assert org1.id in org_ids
assert org2.id in org_ids
end
test "list_user_organizations/1 returns empty list for user with no orgs" do
user = user_fixture()
assert Organizations.list_user_organizations(user.id) == []
end
test "get_organization!/1 returns the organization with given id", %{user: user} do
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
assert Organizations.get_organization!(organization.id).id == organization.id
end
test "get_organization_by_slug!/1 returns the organization with given slug", %{user: user} do
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
assert Organizations.get_organization_by_slug!(organization.slug).id == organization.id
end
test "create_organization/2 with valid data creates organization", %{user: user} do
valid_attrs = %{name: "New Organization"}
assert {:ok, organization} = Organizations.create_organization(valid_attrs, user.id)
assert organization.name == "New Organization"
assert organization.slug
end
test "create_organization/2 creates owner membership", %{user: user} do
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
membership = Organizations.get_membership(organization.id, user.id)
assert membership.role == :owner
end
test "create_organization/2 with invalid data returns error changeset" do
user = user_fixture()
assert {:error, changeset} = Organizations.create_organization(%{name: nil}, user.id)
assert "can't be blank" in errors_on(changeset).name
end
test "update_organization/2 with valid data updates the organization", %{user: user} do
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
update_attrs = %{name: "Updated Org"}
assert {:ok, updated} = Organizations.update_organization(organization, update_attrs)
assert updated.name == "Updated Org"
end
test "update_organization/2 with invalid data returns error changeset", %{user: user} do
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
assert {:error, changeset} = Organizations.update_organization(organization, %{name: nil})
assert "can't be blank" in errors_on(changeset).name
end
test "delete_organization/1 deletes the organization", %{user: user} do
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
assert {:ok, _} = Organizations.delete_organization(organization)
assert_raise Ecto.NoResultsError, fn -> Organizations.get_organization!(organization.id) end
end
test "change_organization/1 returns an organization changeset", %{user: user} do
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
assert %Ecto.Changeset{} = Organizations.change_organization(organization)
end
end
describe "memberships" do
setup do
user = user_fixture()
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
%{user: user, organization: organization}
end
test "get_membership/2 returns the membership", %{user: user, organization: organization} do
membership = Organizations.get_membership(organization.id, user.id)
assert membership.user_id == user.id
assert membership.organization_id == organization.id
end
test "get_membership/2 returns nil when membership doesn't exist", %{organization: organization} do
other_user = user_fixture()
assert Organizations.get_membership(organization.id, other_user.id) == nil
end
test "get_membership!/2 returns the membership", %{user: user, organization: organization} do
membership = Organizations.get_membership!(organization.id, user.id)
assert membership.user_id == user.id
end
test "get_membership!/2 raises when membership doesn't exist", %{organization: organization} do
other_user = user_fixture()
assert_raise Ecto.NoResultsError, fn ->
Organizations.get_membership!(organization.id, other_user.id)
end
end
test "list_organization_memberships/1 returns all memberships", %{
user: user,
organization: organization
} do
# Add another member
other_user = user_fixture()
{:ok, _membership} =
Organizations.create_membership(%{
organization_id: organization.id,
user_id: other_user.id,
role: :member
})
memberships = Organizations.list_organization_memberships(organization.id)
assert length(memberships) == 2
user_ids = Enum.map(memberships, & &1.user_id)
assert user.id in user_ids
assert other_user.id in user_ids
end
test "list_organization_notification_recipients/1 returns owners and admins", %{
organization: organization
} do
admin = user_fixture()
member = user_fixture()
{:ok, _admin_membership} =
Organizations.create_membership(%{
organization_id: organization.id,
user_id: admin.id,
role: :admin
})
{:ok, _member_membership} =
Organizations.create_membership(%{
organization_id: organization.id,
user_id: member.id,
role: :member
})
recipients = Organizations.list_organization_notification_recipients(organization.id)
recipient_ids = Enum.map(recipients, & &1.id)
# Owner and admin should be in recipients
assert admin.id in recipient_ids
# Member should NOT be in recipients
refute member.id in recipient_ids
end
test "create_membership/1 with valid data creates membership", %{organization: organization} do
new_user = user_fixture()
valid_attrs = %{
organization_id: organization.id,
user_id: new_user.id,
role: :member
}
assert {:ok, membership} = Organizations.create_membership(valid_attrs)
assert membership.role == :member
assert membership.user_id == new_user.id
end
test "create_membership/1 with invalid data returns error changeset" do
assert {:error, changeset} = Organizations.create_membership(%{})
assert "can't be blank" in errors_on(changeset).organization_id
end
test "update_membership/2 changes user role", %{user: user, organization: organization} do
membership = Organizations.get_membership!(organization.id, user.id)
assert {:ok, updated} = Organizations.update_membership(membership, %{role: :admin})
assert updated.role == :admin
end
test "delete_membership/1 deletes the membership", %{organization: organization} do
new_user = user_fixture()
{:ok, membership} =
Organizations.create_membership(%{
organization_id: organization.id,
user_id: new_user.id,
role: :member
})
assert {:ok, _} = Organizations.delete_membership(membership)
assert Organizations.get_membership(organization.id, new_user.id) == nil
end
end
describe "invitations" do
setup do
owner = user_fixture()
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, owner.id)
%{owner: owner, organization: organization}
end
test "list_pending_invitations/1 returns pending invitations", %{
owner: owner,
organization: organization
} do
{:ok, invitation} =
Organizations.create_invitation(%{
organization_id: organization.id,
email: "invited@example.com",
role: :member,
invited_by_id: owner.id,
token: "test-token-#{System.unique_integer()}",
expires_at: DateTime.truncate(DateTime.add(DateTime.utc_now(), 7, :day), :second)
})
invitations = Organizations.list_pending_invitations(organization.id)
assert length(invitations) == 1
assert hd(invitations).id == invitation.id
end
test "list_pending_invitations/1 excludes accepted invitations", %{
owner: owner,
organization: organization
} do
{:ok, invitation} =
Organizations.create_invitation(%{
organization_id: organization.id,
email: "invited@example.com",
role: :member,
invited_by_id: owner.id,
token: "test-token-#{System.unique_integer()}",
expires_at: DateTime.truncate(DateTime.add(DateTime.utc_now(), 7, :day), :second),
accepted_at: DateTime.truncate(DateTime.utc_now(), :second)
})
invitations = Organizations.list_pending_invitations(organization.id)
refute Enum.any?(invitations, &(&1.id == invitation.id))
end
test "list_pending_invitations/1 excludes expired invitations", %{
owner: owner,
organization: organization
} do
{:ok, invitation} =
Organizations.create_invitation(%{
organization_id: organization.id,
email: "invited@example.com",
role: :member,
invited_by_id: owner.id,
token: "test-token-#{System.unique_integer()}",
expires_at: DateTime.truncate(DateTime.add(DateTime.utc_now(), -1, :day), :second)
})
invitations = Organizations.list_pending_invitations(organization.id)
refute Enum.any?(invitations, &(&1.id == invitation.id))
end
test "create_invitation/1 with valid data creates invitation", %{
owner: owner,
organization: organization
} do
valid_attrs = %{
organization_id: organization.id,
email: "test@example.com",
role: :member,
invited_by_id: owner.id,
token: "unique-token-#{System.unique_integer()}",
expires_at: DateTime.truncate(DateTime.add(DateTime.utc_now(), 7, :day), :second)
}
assert {:ok, invitation} = Organizations.create_invitation(valid_attrs)
assert invitation.email == "test@example.com"
assert invitation.role == :member
end
test "get_invitation_by_token/1 returns invitation", %{
owner: owner,
organization: organization
} do
token = "test-token-#{System.unique_integer()}"
{:ok, _invitation} =
Organizations.create_invitation(%{
organization_id: organization.id,
email: "test@example.com",
role: :member,
invited_by_id: owner.id,
token: token,
expires_at: DateTime.truncate(DateTime.add(DateTime.utc_now(), 7, :day), :second)
})
invitation = Organizations.get_invitation_by_token(token)
assert invitation.token == token
end
test "get_invitation_by_token/1 returns nil for accepted invitation", %{
owner: owner,
organization: organization
} do
token = "test-token-#{System.unique_integer()}"
{:ok, _invitation} =
Organizations.create_invitation(%{
organization_id: organization.id,
email: "test@example.com",
role: :member,
invited_by_id: owner.id,
token: token,
expires_at: DateTime.truncate(DateTime.add(DateTime.utc_now(), 7, :day), :second),
accepted_at: DateTime.truncate(DateTime.utc_now(), :second)
})
assert Organizations.get_invitation_by_token(token) == nil
end
test "accept_invitation/2 creates membership and marks invitation accepted", %{
owner: owner,
organization: organization
} do
token = "test-token-#{System.unique_integer()}"
{:ok, invitation} =
Organizations.create_invitation(%{
organization_id: organization.id,
email: "test@example.com",
role: :member,
invited_by_id: owner.id,
token: token,
expires_at: DateTime.truncate(DateTime.add(DateTime.utc_now(), 7, :day), :second)
})
new_user = user_fixture()
assert {:ok, membership} = Organizations.accept_invitation(invitation, new_user.id)
assert membership.user_id == new_user.id
assert membership.role == :member
# Invitation should no longer be retrievable
assert Organizations.get_invitation_by_token(token) == nil
end
test "delete_invitation/1 deletes the invitation", %{
owner: owner,
organization: organization
} do
{:ok, invitation} =
Organizations.create_invitation(%{
organization_id: organization.id,
email: "test@example.com",
role: :member,
invited_by_id: owner.id,
token: "test-token-#{System.unique_integer()}",
expires_at: DateTime.truncate(DateTime.add(DateTime.utc_now(), 7, :day), :second)
})
assert {:ok, _} = Organizations.delete_invitation(invitation)
assert Organizations.list_pending_invitations(organization.id) == []
end
end
describe "authorization" do
setup do
user = user_fixture()
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
membership = Organizations.get_membership!(organization.id, user.id)
%{membership: membership}
end
test "can?/3 delegates to Policy module", %{membership: membership} do
# Owner can delete organization
assert Organizations.can?(membership, :delete, :organization)
# Change to member role
{:ok, member_membership} = Organizations.update_membership(membership, %{role: :member})
# Member cannot delete organization
refute Organizations.can?(member_membership, :delete, :organization)
end
end
end