856 lines
29 KiB
Elixir
856 lines
29 KiB
Elixir
defmodule Towerops.OrganizationsTest do
|
|
use Towerops.DataCase
|
|
|
|
import Towerops.AccountsFixtures
|
|
|
|
alias Towerops.Devices.Device
|
|
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)
|
|
# Use bypass_limits to create second org for testing
|
|
{:ok, org2} = Organizations.create_organization(%{name: "Org 2"}, user.id, bypass_limits: true)
|
|
|
|
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 "user_has_access?/2" do
|
|
test "returns true when user is member of organization" do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
assert Organizations.user_has_access?(user.id, organization.id)
|
|
end
|
|
|
|
test "returns false when user is not member of organization" do
|
|
user1 = user_fixture()
|
|
user2 = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user1.id)
|
|
|
|
refute Organizations.user_has_access?(user2.id, 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
|
|
|
|
describe "apply_snmp_config_to_all_equipment/1" do
|
|
test "updates SNMP config for all devices in organization" do
|
|
user = user_fixture()
|
|
|
|
{:ok, organization} =
|
|
Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
{:ok, organization} =
|
|
Organizations.update_organization(organization, %{
|
|
snmp_version: "2c",
|
|
snmp_community: "new-community"
|
|
})
|
|
|
|
{:ok, site} =
|
|
Towerops.Sites.create_site(%{
|
|
name: "Test Site",
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{:ok, device1} =
|
|
Towerops.Devices.create_device(%{
|
|
name: "Device 1",
|
|
ip_address: "192.168.1.1",
|
|
snmp_enabled: true,
|
|
snmp_version: "2c",
|
|
snmp_community: "public",
|
|
site_id: site.id,
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{:ok, device2} =
|
|
Towerops.Devices.create_device(%{
|
|
name: "Device 2",
|
|
ip_address: "192.168.1.2",
|
|
snmp_enabled: true,
|
|
snmp_version: "2c",
|
|
snmp_community: "public",
|
|
site_id: site.id,
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{count, _} = Organizations.apply_snmp_config_to_all_equipment(organization.id)
|
|
assert count == 2
|
|
|
|
updated_device1 = Repo.get!(Device, device1.id)
|
|
assert updated_device1.snmp_version == "2c"
|
|
assert updated_device1.snmp_community == "new-community"
|
|
|
|
updated_device2 = Repo.get!(Device, device2.id)
|
|
assert updated_device2.snmp_version == "2c"
|
|
assert updated_device2.snmp_community == "new-community"
|
|
end
|
|
|
|
test "returns 0 when organization has no devices" do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
{count, _} = Organizations.apply_snmp_config_to_all_equipment(organization.id)
|
|
assert count == 0
|
|
end
|
|
end
|
|
|
|
describe "apply_agent_to_all_equipment/1" do
|
|
test "creates agent assignments for all devices when default agent is set" do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
{:ok, agent_token, _token} =
|
|
Towerops.Agents.create_agent_token(organization.id, "Test Agent")
|
|
|
|
{:ok, organization} =
|
|
Organizations.update_organization(organization, %{
|
|
default_agent_token_id: agent_token.id
|
|
})
|
|
|
|
{:ok, site} =
|
|
Towerops.Sites.create_site(%{
|
|
name: "Test Site",
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{:ok, device1} =
|
|
Towerops.Devices.create_device(%{
|
|
name: "Device 1",
|
|
ip_address: "192.168.1.1",
|
|
site_id: site.id,
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{:ok, device2} =
|
|
Towerops.Devices.create_device(%{
|
|
name: "Device 2",
|
|
ip_address: "192.168.1.2",
|
|
site_id: site.id,
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{count, _} = Organizations.apply_agent_to_all_equipment(organization.id)
|
|
assert count == 2
|
|
|
|
assignment1 = Towerops.Agents.get_device_assignment(device1.id)
|
|
assert assignment1.agent_token_id == agent_token.id
|
|
|
|
assignment2 = Towerops.Agents.get_device_assignment(device2.id)
|
|
assert assignment2.agent_token_id == agent_token.id
|
|
end
|
|
|
|
test "deletes agent assignments when default agent is nil" do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
{:ok, site} =
|
|
Towerops.Sites.create_site(%{
|
|
name: "Test Site",
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{:ok, device} =
|
|
Towerops.Devices.create_device(%{
|
|
name: "Test Device",
|
|
ip_address: "192.168.1.1",
|
|
site_id: site.id,
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{:ok, agent_token, _token} =
|
|
Towerops.Agents.create_agent_token(organization.id, "Test Agent")
|
|
|
|
Towerops.Agents.assign_device_to_agent(agent_token.id, device.id)
|
|
|
|
# Organization has no default agent set (nil)
|
|
Organizations.apply_agent_to_all_equipment(organization.id)
|
|
|
|
assert Towerops.Agents.get_device_assignment(device.id) == nil
|
|
end
|
|
|
|
test "replaces existing agent assignments" do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
{:ok, old_agent, _token1} =
|
|
Towerops.Agents.create_agent_token(organization.id, "Old Agent")
|
|
|
|
{:ok, new_agent, _token2} =
|
|
Towerops.Agents.create_agent_token(organization.id, "New Agent")
|
|
|
|
{:ok, site} =
|
|
Towerops.Sites.create_site(%{
|
|
name: "Test Site",
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{:ok, device} =
|
|
Towerops.Devices.create_device(%{
|
|
name: "Test Device",
|
|
ip_address: "192.168.1.1",
|
|
site_id: site.id,
|
|
organization_id: organization.id
|
|
})
|
|
|
|
Towerops.Agents.assign_device_to_agent(old_agent.id, device.id)
|
|
|
|
{:ok, organization} =
|
|
Organizations.update_organization(organization, %{
|
|
default_agent_token_id: new_agent.id
|
|
})
|
|
|
|
Organizations.apply_agent_to_all_equipment(organization.id)
|
|
|
|
assignment = Towerops.Agents.get_device_assignment(device.id)
|
|
assert assignment.agent_token_id == new_agent.id
|
|
end
|
|
|
|
test "returns 0 when organization has no devices" do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
{:ok, agent_token, _token} =
|
|
Towerops.Agents.create_agent_token(organization.id, "Test Agent")
|
|
|
|
{:ok, organization} =
|
|
Organizations.update_organization(organization, %{
|
|
default_agent_token_id: agent_token.id
|
|
})
|
|
|
|
{count, _} = Organizations.apply_agent_to_all_equipment(organization.id)
|
|
assert count == 0
|
|
end
|
|
end
|
|
|
|
describe "organization creation with subscription limits" do
|
|
test "blocks creating second free organization" do
|
|
user = user_fixture()
|
|
{:ok, _org1} = Organizations.create_organization(%{name: "Free Org 1"}, user.id)
|
|
|
|
assert {:error, changeset} = Organizations.create_organization(%{name: "Free Org 2"}, user.id)
|
|
|
|
assert %{
|
|
base: [
|
|
"You already have a free organization. Upgrade your existing organization to create additional ones."
|
|
]
|
|
} =
|
|
errors_on(changeset)
|
|
end
|
|
|
|
test "allows creating first free organization" do
|
|
user = user_fixture()
|
|
|
|
assert {:ok, org} = Organizations.create_organization(%{name: "Free Org"}, user.id)
|
|
assert org.subscription_plan == "free"
|
|
end
|
|
|
|
test "bypasses limit with bypass_limits option" do
|
|
user = user_fixture()
|
|
{:ok, _org1} = Organizations.create_organization(%{name: "Free Org 1"}, user.id)
|
|
|
|
# Second free org succeeds with bypass
|
|
assert {:ok, _org2} =
|
|
Organizations.create_organization(
|
|
%{name: "Free Org 2"},
|
|
user.id,
|
|
bypass_limits: true
|
|
)
|
|
end
|
|
|
|
test "invited members can create their own free organization" do
|
|
owner = user_fixture()
|
|
member = user_fixture()
|
|
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Owner's Org"}, owner.id)
|
|
|
|
# Add member to organization
|
|
{:ok, _membership} =
|
|
Organizations.create_membership(%{
|
|
user_id: member.id,
|
|
organization_id: organization.id,
|
|
role: :member
|
|
})
|
|
|
|
# Member can still create their own free org
|
|
assert {:ok, _org} = Organizations.create_organization(%{name: "Member's Org"}, member.id)
|
|
end
|
|
|
|
test "create_organization/3 with explicit free subscription plan" do
|
|
user = user_fixture()
|
|
|
|
# Explicitly specify "free" subscription plan
|
|
assert {:ok, org} =
|
|
Organizations.create_organization(
|
|
%{name: "Free Org", subscription_plan: "free"},
|
|
user.id
|
|
)
|
|
|
|
assert org.subscription_plan == "free"
|
|
end
|
|
end
|
|
|
|
describe "update_organization/2 MikroTik propagation" do
|
|
test "propagates MikroTik settings when changed" do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
|
|
{:ok, site} =
|
|
Towerops.Sites.create_site(%{
|
|
name: "Test Site",
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{:ok, device} =
|
|
Towerops.Devices.create_device(%{
|
|
name: "MikroTik Device",
|
|
ip_address: "192.168.1.1",
|
|
site_id: site.id,
|
|
organization_id: organization.id,
|
|
manufacturer: "MikroTik"
|
|
})
|
|
|
|
# Update organization MikroTik settings
|
|
{:ok, updated_org} =
|
|
Organizations.update_organization(organization, %{
|
|
mikrotik_username: "admin",
|
|
mikrotik_password: "password123",
|
|
mikrotik_port: 8729,
|
|
mikrotik_use_ssl: true,
|
|
mikrotik_enabled: true
|
|
})
|
|
|
|
assert updated_org.mikrotik_username == "admin"
|
|
assert updated_org.mikrotik_port == 8729
|
|
assert updated_org.mikrotik_use_ssl == true
|
|
assert updated_org.mikrotik_enabled == true
|
|
|
|
# Verify device credential source is "organization" (inherits directly from org)
|
|
updated_device = Repo.get!(Device, device.id)
|
|
assert updated_device.mikrotik_credential_source == "organization"
|
|
|
|
# Verify the device received the organization's MikroTik settings through propagation
|
|
assert updated_device.mikrotik_username == "admin"
|
|
assert updated_device.mikrotik_port == 8729
|
|
assert updated_device.mikrotik_use_ssl == true
|
|
assert updated_device.mikrotik_enabled == true
|
|
end
|
|
|
|
test "propagates when individual MikroTik fields change" do
|
|
user = user_fixture()
|
|
|
|
{:ok, organization} =
|
|
Organizations.create_organization(
|
|
%{
|
|
name: "Test Org",
|
|
mikrotik_username: "original",
|
|
mikrotik_password: "oldpass"
|
|
},
|
|
user.id
|
|
)
|
|
|
|
{:ok, site} =
|
|
Towerops.Sites.create_site(%{
|
|
name: "Test Site",
|
|
organization_id: organization.id
|
|
})
|
|
|
|
{:ok, _device} =
|
|
Towerops.Devices.create_device(%{
|
|
name: "MikroTik Device",
|
|
ip_address: "192.168.1.1",
|
|
site_id: site.id,
|
|
organization_id: organization.id,
|
|
manufacturer: "MikroTik"
|
|
})
|
|
|
|
# Change only username - should still propagate
|
|
{:ok, _updated} =
|
|
Organizations.update_organization(organization, %{
|
|
mikrotik_username: "newuser"
|
|
})
|
|
|
|
# Change only password - should propagate
|
|
organization = Organizations.get_organization!(organization.id)
|
|
|
|
{:ok, _updated} =
|
|
Organizations.update_organization(organization, %{
|
|
mikrotik_password: "newpass"
|
|
})
|
|
|
|
# Change only port - should propagate
|
|
organization = Organizations.get_organization!(organization.id)
|
|
|
|
{:ok, _updated} =
|
|
Organizations.update_organization(organization, %{
|
|
mikrotik_port: 8728
|
|
})
|
|
|
|
# Change only use_ssl - should propagate
|
|
organization = Organizations.get_organization!(organization.id)
|
|
|
|
{:ok, _updated} =
|
|
Organizations.update_organization(organization, %{
|
|
mikrotik_use_ssl: false
|
|
})
|
|
|
|
# Change only enabled - should propagate
|
|
organization = Organizations.get_organization!(organization.id)
|
|
|
|
{:ok, _updated} =
|
|
Organizations.update_organization(organization, %{
|
|
mikrotik_enabled: false
|
|
})
|
|
end
|
|
end
|
|
|
|
describe "get_default_membership/1" do
|
|
test "returns the default membership for a user" do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Default Org"}, user.id)
|
|
|
|
membership = Organizations.get_default_membership(user.id)
|
|
assert membership.organization_id == organization.id
|
|
assert membership.is_default == true
|
|
assert membership.organization.id == organization.id
|
|
end
|
|
|
|
test "returns nil when user has no default membership" do
|
|
user = user_fixture()
|
|
assert Organizations.get_default_membership(user.id) == nil
|
|
end
|
|
end
|
|
|
|
describe "set_default_organization/2" do
|
|
test "sets an organization as the user's default" do
|
|
user = user_fixture()
|
|
{:ok, _org1} = Organizations.create_organization(%{name: "Org 1"}, user.id)
|
|
{:ok, org2} = Organizations.create_organization(%{name: "Org 2"}, user.id, bypass_limits: true)
|
|
|
|
assert {:ok, returned_org} = Organizations.set_default_organization(user.id, org2.id)
|
|
assert returned_org.id == org2.id
|
|
|
|
# Verify org2 is now default
|
|
membership = Organizations.get_default_membership(user.id)
|
|
assert membership.organization_id == org2.id
|
|
end
|
|
|
|
test "returns error when user has no access to organization" do
|
|
user1 = user_fixture()
|
|
user2 = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user1.id)
|
|
|
|
assert {:error, :not_found} = Organizations.set_default_organization(user2.id, organization.id)
|
|
end
|
|
end
|
|
|
|
describe "accept_invitation/2 error handling" do
|
|
test "returns error when membership creation fails" do
|
|
owner = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, owner.id)
|
|
|
|
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)
|
|
})
|
|
|
|
# Create a membership first so the accept will fail with unique constraint
|
|
new_user = user_fixture()
|
|
|
|
{:ok, _existing_membership} =
|
|
Organizations.create_membership(%{
|
|
organization_id: organization.id,
|
|
user_id: new_user.id,
|
|
role: :admin
|
|
})
|
|
|
|
# Accepting invitation should fail because membership already exists
|
|
assert {:error, _changeset} = Organizations.accept_invitation(invitation, new_user.id)
|
|
end
|
|
end
|
|
end
|