towerops/test/towerops/sites_test.exs

506 lines
20 KiB
Elixir

defmodule Towerops.SitesTest do
use Towerops.DataCase
alias Towerops.Sites
alias Towerops.Sites.Site
describe "sites" do
import Towerops.AccountsFixtures
setup do
user = user_fixture()
{:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
%{organization: organization, user: user}
end
@valid_attrs %{
name: "Main Office",
description: "Primary location",
location: "123 Main St",
snmp_version: "2c",
snmp_community: "public"
}
@invalid_attrs %{name: nil, organization_id: nil}
test "list_organization_sites/1 returns all sites for an organization", %{organization: organization} do
{:ok, site1} = Sites.create_site(Map.put(@valid_attrs, :organization_id, organization.id))
{:ok, site2} = Sites.create_site(%{name: "Branch Office", organization_id: organization.id})
sites = Sites.list_organization_sites(organization.id)
assert length(sites) == 2
assert Enum.any?(sites, &(&1.id == site1.id))
assert Enum.any?(sites, &(&1.id == site2.id))
end
test "list_organization_sites/1 orders sites alphabetically", %{organization: organization} do
{:ok, _zebra} = Sites.create_site(%{name: "Zebra Site", organization_id: organization.id})
{:ok, _alpha} = Sites.create_site(%{name: "Alpha Site", organization_id: organization.id})
sites = Sites.list_organization_sites(organization.id)
assert List.first(sites).name == "Alpha Site"
assert List.last(sites).name == "Zebra Site"
end
test "count_organization_sites/1 returns count of sites", %{organization: organization} do
assert Sites.count_organization_sites(organization.id) == 0
{:ok, _} = Sites.create_site(%{name: "Site 1", organization_id: organization.id})
{:ok, _} = Sites.create_site(%{name: "Site 2", organization_id: organization.id})
assert Sites.count_organization_sites(organization.id) == 2
end
test "list_root_sites/1 returns only sites without parents", %{organization: organization} do
{:ok, root} = Sites.create_site(%{name: "Root Site", organization_id: organization.id})
{:ok, _child} =
Sites.create_site(%{
name: "Child Site",
organization_id: organization.id,
parent_site_id: root.id
})
roots = Sites.list_root_sites(organization.id)
assert length(roots) == 1
assert hd(roots).id == root.id
end
test "list_child_sites/1 returns child sites of a parent", %{organization: organization} do
{:ok, parent} = Sites.create_site(%{name: "Parent", organization_id: organization.id})
{:ok, child1} =
Sites.create_site(%{
name: "Child 1",
organization_id: organization.id,
parent_site_id: parent.id
})
{:ok, child2} =
Sites.create_site(%{
name: "Child 2",
organization_id: organization.id,
parent_site_id: parent.id
})
children = Sites.list_child_sites(parent.id)
assert length(children) == 2
assert Enum.any?(children, &(&1.id == child1.id))
assert Enum.any?(children, &(&1.id == child2.id))
end
test "get_site!/1 returns the site with given id", %{organization: organization} do
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})
fetched = Sites.get_site!(site.id)
assert fetched.id == site.id
assert fetched.name == "Test Site"
end
test "get_site!/1 preloads associations", %{organization: organization} do
{:ok, parent} = Sites.create_site(%{name: "Parent", organization_id: organization.id})
{:ok, site} =
Sites.create_site(%{
name: "Child",
organization_id: organization.id,
parent_site_id: parent.id
})
fetched = Sites.get_site!(site.id)
assert Ecto.assoc_loaded?(fetched.parent_site)
assert Ecto.assoc_loaded?(fetched.child_sites)
assert Ecto.assoc_loaded?(fetched.device)
end
test "get_site!/1 raises when site doesn't exist" do
assert_raise Ecto.NoResultsError, fn ->
Sites.get_site!(Ecto.UUID.generate())
end
end
test "get_organization_site!/2 returns site for specific organization", %{organization: organization} do
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})
fetched = Sites.get_organization_site!(organization.id, site.id)
assert fetched.id == site.id
end
test "get_organization_site!/2 preloads associations", %{organization: organization} do
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})
fetched = Sites.get_organization_site!(organization.id, site.id)
assert Ecto.assoc_loaded?(fetched.parent_site)
assert Ecto.assoc_loaded?(fetched.child_sites)
assert Ecto.assoc_loaded?(fetched.device)
end
test "get_organization_site!/2 raises when site doesn't exist" do
user = user_fixture()
{:ok, org} = Towerops.Organizations.create_organization(%{name: "Org"}, user.id)
assert_raise Ecto.NoResultsError, fn ->
Sites.get_organization_site!(org.id, Ecto.UUID.generate())
end
end
test "create_site/1 with valid data creates a site", %{organization: organization} do
attrs = Map.put(@valid_attrs, :organization_id, organization.id)
assert {:ok, %Site{} = site} = Sites.create_site(attrs)
assert site.name == "Main Office"
assert site.description == "Primary location"
assert site.location == "123 Main St"
assert site.snmp_version == "2c"
assert site.snmp_community == "public"
end
test "create_site/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Sites.create_site(@invalid_attrs)
end
test "create_site/1 validates name length", %{organization: organization} do
# Too short
attrs = %{name: "A", organization_id: organization.id}
assert {:error, changeset} = Sites.create_site(attrs)
assert "should be at least 2 character(s)" in errors_on(changeset).name
# Too long
attrs = %{name: String.duplicate("a", 201), organization_id: organization.id}
assert {:error, changeset} = Sites.create_site(attrs)
assert "should be at most 200 character(s)" in errors_on(changeset).name
end
test "create_site/1 validates description length", %{organization: organization} do
attrs = %{name: "Test Site", description: String.duplicate("a", 1001), organization_id: organization.id}
assert {:error, changeset} = Sites.create_site(attrs)
assert "should be at most 1000 character(s)" in errors_on(changeset).description
end
test "create_site/1 validates location length", %{organization: organization} do
attrs = %{name: "Test Site", location: String.duplicate("a", 201), organization_id: organization.id}
assert {:error, changeset} = Sites.create_site(attrs)
assert "should be at most 200 character(s)" in errors_on(changeset).location
end
test "create_site/1 validates snmp_version inclusion", %{organization: organization} do
attrs = %{name: "Test Site", snmp_version: "invalid", organization_id: organization.id}
assert {:error, changeset} = Sites.create_site(attrs)
assert "must be 1, 2c, or 3" in errors_on(changeset).snmp_version
end
test "create_site/1 prevents duplicate site names within same organization", %{
organization: organization
} do
# Create first site
{:ok, _site} = Sites.create_site(%{name: "Duplicate Site", organization_id: organization.id})
# Try to create second site with same name in same organization
assert {:error, changeset} =
Sites.create_site(%{name: "Duplicate Site", organization_id: organization.id})
assert "has already been taken for this organization" in errors_on(changeset).name
end
test "create_site/1 allows duplicate site names across different organizations" do
user1 = user_fixture()
user2 = user_fixture()
{:ok, org1} = Towerops.Organizations.create_organization(%{name: "Org 1"}, user1.id)
{:ok, org2} = Towerops.Organizations.create_organization(%{name: "Org 2"}, user2.id)
# Create site in first org
assert {:ok, site1} = Sites.create_site(%{name: "Main Office", organization_id: org1.id})
# Should be able to create site with same name in different org
assert {:ok, site2} = Sites.create_site(%{name: "Main Office", organization_id: org2.id})
assert site1.name == site2.name
assert site1.organization_id != site2.organization_id
end
test "create_site/1 prevents circular parent reference", %{organization: organization} do
{:ok, site} = Sites.create_site(%{name: "Test Site", organization_id: organization.id})
# Try to make site its own parent
assert {:error, changeset} = Sites.update_site(site, %{parent_site_id: site.id, organization_id: organization.id})
assert "cannot be the same as the site itself" in errors_on(changeset).parent_site_id
end
test "update_site/2 with valid data updates the site", %{organization: organization} do
{:ok, site} = Sites.create_site(%{name: "Original", organization_id: organization.id})
assert {:ok, %Site{} = updated} = Sites.update_site(site, %{name: "Updated", description: "New description"})
assert updated.name == "Updated"
assert updated.description == "New description"
end
test "update_site/2 with invalid data returns error changeset", %{organization: organization} do
{:ok, site} = Sites.create_site(%{name: "Test", organization_id: organization.id})
assert {:error, %Ecto.Changeset{}} = Sites.update_site(site, %{name: nil})
end
test "delete_site/1 deletes the site", %{organization: organization} do
{:ok, site} = Sites.create_site(%{name: "Test", organization_id: organization.id})
assert {:ok, %Site{}} = Sites.delete_site(site)
assert_raise Ecto.NoResultsError, fn -> Sites.get_site!(site.id) end
end
test "change_site/1 returns a site changeset", %{organization: organization} do
{:ok, site} = Sites.create_site(%{name: "Test", organization_id: organization.id})
assert %Ecto.Changeset{} = Sites.change_site(site)
end
test "change_site/2 returns a site changeset with changes", %{organization: organization} do
{:ok, site} = Sites.create_site(%{name: "Test", organization_id: organization.id})
changeset = Sites.change_site(site, %{name: "New Name"})
assert %Ecto.Changeset{} = changeset
assert changeset.changes.name == "New Name"
end
test "build_site_tree/1 builds hierarchical structure", %{organization: organization} do
{:ok, root1} = Sites.create_site(%{name: "Root 1", organization_id: organization.id})
{:ok, root2} = Sites.create_site(%{name: "Root 2", organization_id: organization.id})
{:ok, child1} =
Sites.create_site(%{
name: "Child 1",
organization_id: organization.id,
parent_site_id: root1.id
})
{:ok, grandchild} =
Sites.create_site(%{
name: "Grandchild",
organization_id: organization.id,
parent_site_id: child1.id
})
tree = Sites.build_site_tree(organization.id)
assert length(tree) == 2
root1_node = Enum.find(tree, &(&1.site.id == root1.id))
assert length(root1_node.children) == 1
assert hd(root1_node.children).site.id == child1.id
assert length(hd(root1_node.children).children) == 1
assert hd(hd(root1_node.children).children).site.id == grandchild.id
root2_node = Enum.find(tree, &(&1.site.id == root2.id))
assert root2_node.children == []
end
test "build_site_tree/1 returns empty list for organization with no sites", %{organization: organization} do
tree = Sites.build_site_tree(organization.id)
assert tree == []
end
test "apply_snmp_config_to_all_equipment/1 updates all devices at site", %{organization: organization} do
{:ok, site} =
Sites.create_site(%{
name: "Test Site",
organization_id: organization.id,
snmp_version: "2c",
snmp_community: "test-community"
})
{:ok, device1} =
Towerops.Devices.create_device(%{
name: "Device 1",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id,
snmp_version: "1",
snmp_community: "old-community"
})
{:ok, device2} =
Towerops.Devices.create_device(%{
name: "Device 2",
ip_address: "192.168.1.2",
site_id: site.id,
organization_id: organization.id,
snmp_version: "1",
snmp_community: "old-community"
})
{count, _} = Sites.apply_snmp_config_to_all_equipment(site.id)
assert count == 2
updated1 = Towerops.Devices.get_device!(device1.id)
assert updated1.snmp_version == "2c"
assert updated1.snmp_community == "test-community"
updated2 = Towerops.Devices.get_device!(device2.id)
assert updated2.snmp_version == "2c"
assert updated2.snmp_community == "test-community"
end
test "apply_agent_to_all_equipment/1 creates agent assignments", %{organization: organization, user: _user} do
{:ok, agent_token, _token} = Towerops.Agents.create_agent_token(organization.id, "Test Agent")
{:ok, site} =
Sites.create_site(%{
name: "Test Site",
organization_id: organization.id,
agent_token_id: agent_token.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, _} = Sites.apply_agent_to_all_equipment(site.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 "apply_agent_to_all_equipment/1 deletes assignments when no agent set", %{
organization: organization,
user: _user
} do
{:ok, agent_token, _token} = Towerops.Agents.create_agent_token(organization.id, "Test Agent")
{:ok, site} =
Sites.create_site(%{
name: "Test Site",
organization_id: organization.id,
agent_token_id: agent_token.id
})
{:ok, device} =
Towerops.Devices.create_device(%{
name: "Device 1",
ip_address: "192.168.1.1",
site_id: site.id,
organization_id: organization.id
})
# Create initial assignment
Sites.apply_agent_to_all_equipment(site.id)
assert Towerops.Agents.get_device_assignment(device.id)
# Remove agent from site
{:ok, site} = Sites.update_site(site, %{agent_token_id: nil})
# Apply again - should delete assignments
{count, _} = Sites.apply_agent_to_all_equipment(site.id)
assert count == 1
# Verify assignment was deleted
assert is_nil(Towerops.Agents.get_device_assignment(device.id))
end
test "reorder_site/2 reorders site to first position", %{organization: organization} do
{:ok, site1} = Sites.create_site(%{name: "Site 1", organization_id: organization.id})
{:ok, site2} = Sites.create_site(%{name: "Site 2", organization_id: organization.id})
{:ok, site3} = Sites.create_site(%{name: "Site 3", organization_id: organization.id})
# Move site3 to first position
{:ok, _updated} = Sites.reorder_site(site3.id, 1)
# Verify order
sites = Sites.list_organization_sites(organization.id)
assert Enum.at(sites, 0).id == site3.id
assert Enum.at(sites, 0).display_order == 1
assert Enum.at(sites, 1).id == site1.id
assert Enum.at(sites, 1).display_order == 2
assert Enum.at(sites, 2).id == site2.id
assert Enum.at(sites, 2).display_order == 3
end
test "reorder_site/2 reorders site to last position", %{organization: organization} do
{:ok, site1} = Sites.create_site(%{name: "Site 1", organization_id: organization.id})
{:ok, site2} = Sites.create_site(%{name: "Site 2", organization_id: organization.id})
{:ok, site3} = Sites.create_site(%{name: "Site 3", organization_id: organization.id})
# Move site1 to last position
{:ok, _updated} = Sites.reorder_site(site1.id, 3)
# Verify order
sites = Sites.list_organization_sites(organization.id)
assert Enum.at(sites, 0).id == site2.id
assert Enum.at(sites, 0).display_order == 1
assert Enum.at(sites, 1).id == site3.id
assert Enum.at(sites, 1).display_order == 2
assert Enum.at(sites, 2).id == site1.id
assert Enum.at(sites, 2).display_order == 3
end
test "reorder_site/2 reorders site to middle position", %{organization: organization} do
{:ok, site1} = Sites.create_site(%{name: "Site 1", organization_id: organization.id})
{:ok, site2} = Sites.create_site(%{name: "Site 2", organization_id: organization.id})
{:ok, site3} = Sites.create_site(%{name: "Site 3", organization_id: organization.id})
# Move site3 to middle position
{:ok, _updated} = Sites.reorder_site(site3.id, 2)
# Verify order
sites = Sites.list_organization_sites(organization.id)
assert Enum.at(sites, 0).id == site1.id
assert Enum.at(sites, 0).display_order == 1
assert Enum.at(sites, 1).id == site3.id
assert Enum.at(sites, 1).display_order == 2
assert Enum.at(sites, 2).id == site2.id
assert Enum.at(sites, 2).display_order == 3
end
test "reorder_site/2 maintains continuous numbering", %{organization: organization} do
{:ok, _site1} = Sites.create_site(%{name: "Site 1", organization_id: organization.id})
{:ok, site2} = Sites.create_site(%{name: "Site 2", organization_id: organization.id})
{:ok, _site3} = Sites.create_site(%{name: "Site 3", organization_id: organization.id})
{:ok, site4} = Sites.create_site(%{name: "Site 4", organization_id: organization.id})
# Reorder multiple times
{:ok, _} = Sites.reorder_site(site2.id, 1)
{:ok, _} = Sites.reorder_site(site4.id, 2)
# Verify all have continuous display_order values
sites = Sites.list_organization_sites(organization.id)
display_orders = Enum.map(sites, & &1.display_order)
assert display_orders == [1, 2, 3, 4]
end
test "reset_site_order/1 clears all display_order values", %{organization: organization} do
{:ok, site1} = Sites.create_site(%{name: "Zebra Site", organization_id: organization.id})
{:ok, site2} = Sites.create_site(%{name: "Alpha Site", organization_id: organization.id})
# Set custom order
{:ok, _} = Sites.reorder_site(site1.id, 1)
{:ok, _} = Sites.reorder_site(site2.id, 2)
# Verify custom order is set
sites_before = Sites.list_organization_sites(organization.id)
assert List.first(sites_before).id == site1.id
assert List.first(sites_before).display_order == 1
# Reset order
{count, _} = Sites.reset_site_order(organization.id)
assert count == 2
# Verify display_order is cleared and falls back to alphabetical
sites_after = Sites.list_organization_sites(organization.id)
assert List.first(sites_after).name == "Alpha Site"
assert List.first(sites_after).display_order == nil
assert List.last(sites_after).name == "Zebra Site"
assert List.last(sites_after).display_order == nil
end
end
end