326 lines
11 KiB
Elixir
326 lines
11 KiB
Elixir
defmodule Towerops.Organizations.SubscriptionLimitsTest do
|
|
use Towerops.DataCase
|
|
|
|
import Towerops.AccountsFixtures
|
|
|
|
alias Towerops.Devices
|
|
alias Towerops.Organizations
|
|
alias Towerops.Organizations.Organization
|
|
alias Towerops.Organizations.SubscriptionLimits
|
|
alias Towerops.Sites
|
|
|
|
doctest SubscriptionLimits
|
|
|
|
describe "device_limit/1" do
|
|
test "returns 10 for free plan" do
|
|
assert SubscriptionLimits.device_limit("free") == 10
|
|
end
|
|
|
|
test "returns unlimited for any non-free plan" do
|
|
# Future: when other plans are added, they will return :unlimited
|
|
assert SubscriptionLimits.device_limit("paid") == :unlimited
|
|
assert SubscriptionLimits.device_limit("premium") == :unlimited
|
|
assert SubscriptionLimits.device_limit("enterprise") == :unlimited
|
|
end
|
|
end
|
|
|
|
describe "effective_device_limit/1" do
|
|
test "returns default limit for free org without override" do
|
|
org = %Organization{subscription_plan: "free", custom_free_device_limit: nil}
|
|
assert SubscriptionLimits.effective_device_limit(org) == 10
|
|
end
|
|
|
|
test "returns custom limit for free org with override" do
|
|
org = %Organization{subscription_plan: "free", custom_free_device_limit: 50}
|
|
assert SubscriptionLimits.effective_device_limit(org) == 50
|
|
end
|
|
|
|
test "returns unlimited for paid org without override" do
|
|
org = %Organization{subscription_plan: "paid", custom_free_device_limit: nil}
|
|
assert SubscriptionLimits.effective_device_limit(org) == :unlimited
|
|
end
|
|
|
|
test "returns unlimited for paid org even with override" do
|
|
org = %Organization{subscription_plan: "paid", custom_free_device_limit: 50}
|
|
assert SubscriptionLimits.effective_device_limit(org) == :unlimited
|
|
end
|
|
end
|
|
|
|
describe "check_device_limit/1" do
|
|
setup do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})
|
|
%{organization: organization, site: site}
|
|
end
|
|
|
|
test "returns ok when organization has no devices", %{organization: organization} do
|
|
assert {:ok, :within_limit} = SubscriptionLimits.check_device_limit(organization)
|
|
end
|
|
|
|
test "returns ok when organization has devices under limit", %{
|
|
organization: organization,
|
|
site: site
|
|
} do
|
|
# Create 5 devices
|
|
for i <- 1..5 do
|
|
create_device(site.id, "Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
organization = Repo.reload!(organization)
|
|
assert {:ok, :within_limit} = SubscriptionLimits.check_device_limit(organization)
|
|
end
|
|
|
|
test "returns error when organization is at device limit", %{
|
|
organization: organization,
|
|
site: site
|
|
} do
|
|
# Create 10 devices (the limit)
|
|
for i <- 1..10 do
|
|
create_device(site.id, "Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
organization = Repo.reload!(organization)
|
|
assert {:error, :at_limit, 10, 10} = SubscriptionLimits.check_device_limit(organization)
|
|
end
|
|
|
|
test "returns error when organization is over device limit", %{
|
|
organization: organization,
|
|
site: site
|
|
} do
|
|
# Create 12 devices (over the limit)
|
|
for i <- 1..12 do
|
|
create_device(site.id, "Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
organization = Repo.reload!(organization)
|
|
assert {:error, :at_limit, 12, 10} = SubscriptionLimits.check_device_limit(organization)
|
|
end
|
|
|
|
test "uses custom limit when set", %{organization: organization, site: site} do
|
|
# Set custom limit of 5
|
|
organization
|
|
|> Organization.billing_override_changeset(%{custom_free_device_limit: 5})
|
|
|> Repo.update!()
|
|
|
|
organization = Repo.get!(Organization, organization.id)
|
|
|
|
# Create 5 devices (at custom limit)
|
|
for i <- 1..5 do
|
|
create_device(site.id, "Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
assert {:error, :at_limit, 5, 5} = SubscriptionLimits.check_device_limit(organization)
|
|
end
|
|
|
|
test "allows more devices with higher custom limit", %{organization: organization, site: site} do
|
|
# Set custom limit of 20
|
|
organization
|
|
|> Organization.billing_override_changeset(%{custom_free_device_limit: 20})
|
|
|> Repo.update!()
|
|
|
|
organization = Repo.get!(Organization, organization.id)
|
|
|
|
# Create 15 devices (under custom limit of 20)
|
|
for i <- 1..15 do
|
|
create_device(site.id, "Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
assert {:ok, :within_limit} = SubscriptionLimits.check_device_limit(organization)
|
|
end
|
|
end
|
|
|
|
describe "device_quota/1" do
|
|
setup do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})
|
|
%{organization: organization, site: site}
|
|
end
|
|
|
|
test "returns {0, 10} for free org with no devices", %{organization: organization} do
|
|
assert {0, 10} = SubscriptionLimits.device_quota(organization)
|
|
end
|
|
|
|
test "returns {5, 10} for free org with 5 devices", %{organization: organization, site: site} do
|
|
for i <- 1..5 do
|
|
create_device(site.id, "Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
organization = Repo.reload!(organization)
|
|
assert {5, 10} = SubscriptionLimits.device_quota(organization)
|
|
end
|
|
|
|
test "uses custom limit in quota", %{organization: organization} do
|
|
organization
|
|
|> Organization.billing_override_changeset(%{custom_free_device_limit: 25})
|
|
|> Repo.update!()
|
|
|
|
organization = Repo.get!(Organization, organization.id)
|
|
assert {0, 25} = SubscriptionLimits.device_quota(organization)
|
|
end
|
|
end
|
|
|
|
describe "can_create_free_organization?/1" do
|
|
test "returns true when user has no free organizations" do
|
|
user = user_fixture()
|
|
assert SubscriptionLimits.can_create_free_organization?(user.id) == true
|
|
end
|
|
|
|
test "returns false when user owns one free organization" do
|
|
user = user_fixture()
|
|
{:ok, _org} = Organizations.create_organization(%{name: "Free Org"}, user.id)
|
|
|
|
assert SubscriptionLimits.can_create_free_organization?(user.id) == false
|
|
end
|
|
|
|
test "returns true when user is member but not owner of free organization" do
|
|
owner = user_fixture()
|
|
member = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Free 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 SubscriptionLimits.can_create_free_organization?(member.id) == true
|
|
end
|
|
end
|
|
|
|
describe "count_user_owned_free_organizations/1" do
|
|
test "returns 0 when user has no organizations" do
|
|
user = user_fixture()
|
|
assert SubscriptionLimits.count_user_owned_free_organizations(user.id) == 0
|
|
end
|
|
|
|
test "returns 1 when user owns one free organization" do
|
|
user = user_fixture()
|
|
{:ok, _org} = Organizations.create_organization(%{name: "Free Org"}, user.id)
|
|
|
|
assert SubscriptionLimits.count_user_owned_free_organizations(user.id) == 1
|
|
end
|
|
|
|
test "returns 0 when user is member but not owner" do
|
|
owner = user_fixture()
|
|
member = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Free Org"}, owner.id)
|
|
|
|
{:ok, _membership} =
|
|
Organizations.create_membership(%{
|
|
user_id: member.id,
|
|
organization_id: organization.id,
|
|
role: :admin
|
|
})
|
|
|
|
assert SubscriptionLimits.count_user_owned_free_organizations(member.id) == 0
|
|
end
|
|
end
|
|
|
|
describe "count_organization_devices/1" do
|
|
setup do
|
|
user = user_fixture()
|
|
{:ok, organization} = Organizations.create_organization(%{name: "Test Org"}, user.id)
|
|
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})
|
|
%{organization: organization, site: site}
|
|
end
|
|
|
|
test "returns 0 for organization with no devices", %{organization: organization} do
|
|
assert SubscriptionLimits.count_organization_devices(organization.id) == 0
|
|
end
|
|
|
|
test "returns correct count for organization with devices", %{
|
|
organization: organization,
|
|
site: site
|
|
} do
|
|
for i <- 1..7 do
|
|
create_device(site.id, "Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
assert SubscriptionLimits.count_organization_devices(organization.id) == 7
|
|
end
|
|
|
|
test "counts devices across multiple sites", %{organization: organization, site: site1} do
|
|
{:ok, site2} = Sites.create_site(%{name: "Site 2", organization_id: organization.id})
|
|
|
|
for i <- 1..3 do
|
|
create_device(site1.id, "Site1 Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
for i <- 4..8 do
|
|
create_device(site2.id, "Site2 Device #{i}", "192.168.2.#{i}")
|
|
end
|
|
|
|
assert SubscriptionLimits.count_organization_devices(organization.id) == 8
|
|
end
|
|
|
|
test "counts devices without site assignment when sites are disabled", %{
|
|
organization: organization
|
|
} do
|
|
# Create devices directly assigned to organization (no site)
|
|
for i <- 1..5 do
|
|
create_device_without_site(organization.id, "Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
assert SubscriptionLimits.count_organization_devices(organization.id) == 5
|
|
end
|
|
|
|
test "counts both site-assigned and unassigned devices", %{
|
|
organization: organization,
|
|
site: site
|
|
} do
|
|
# Create 3 devices assigned to site
|
|
for i <- 1..3 do
|
|
create_device(site.id, "Site Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
# Create 2 devices without site assignment
|
|
for i <- 4..5 do
|
|
create_device_without_site(organization.id, "Org Device #{i}", "192.168.1.#{i}")
|
|
end
|
|
|
|
assert SubscriptionLimits.count_organization_devices(organization.id) == 5
|
|
end
|
|
end
|
|
|
|
# Helper functions
|
|
|
|
defp create_device(site_id, name, ip_address) do
|
|
site = Sites.get_site!(site_id)
|
|
|
|
{:ok, device} =
|
|
Devices.create_device(
|
|
%{
|
|
name: name,
|
|
ip_address: ip_address,
|
|
site_id: site_id,
|
|
organization_id: site.organization_id,
|
|
monitoring_enabled: false,
|
|
snmp_enabled: false
|
|
},
|
|
bypass_limits: true
|
|
)
|
|
|
|
device
|
|
end
|
|
|
|
defp create_device_without_site(organization_id, name, ip_address) do
|
|
{:ok, device} =
|
|
Devices.create_device(
|
|
%{
|
|
name: name,
|
|
ip_address: ip_address,
|
|
organization_id: organization_id,
|
|
monitoring_enabled: false,
|
|
snmp_enabled: false
|
|
},
|
|
bypass_limits: true
|
|
)
|
|
|
|
device
|
|
end
|
|
end
|