towerops/test/towerops/snmp/ip_address_test.exs

245 lines
7 KiB
Elixir

defmodule Towerops.Snmp.IpAddressTest do
use Towerops.DataCase, async: true
import Towerops.AccountsFixtures
alias Towerops.Snmp.Device
alias Towerops.Snmp.Interface
alias Towerops.Snmp.IpAddress
setup do
user = user_fixture()
{:ok, organization} =
Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)
{:ok, site} =
Towerops.Sites.create_site(%{
name: "Test Site",
organization_id: organization.id
})
{:ok, device_schema} =
Towerops.Devices.create_device(%{
name: "Test Router",
ip_address: "192.168.1.1",
snmp_enabled: true,
snmp_version: "2c",
snmp_community: "public",
snmp_port: 161,
site_id: site.id,
organization_id: organization.id
})
snmp_device =
%Device{}
|> Device.changeset(%{
device_id: device_schema.id,
sys_name: "test-router",
sys_descr: "Test Router"
})
|> Repo.insert!()
interface =
%Interface{}
|> Interface.changeset(%{
snmp_device_id: snmp_device.id,
if_index: 1,
if_descr: "eth0",
if_type: 6,
if_admin_status: "up",
if_oper_status: "up"
})
|> Repo.insert!()
%{device: device_schema, snmp_device: snmp_device, interface: interface}
end
describe "changeset/2" do
test "valid changeset with all required fields", %{interface: interface} do
attrs = %{
snmp_interface_id: interface.id,
ip_address: "192.168.1.100",
ip_type: "ipv4"
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
assert changeset.valid?
end
test "valid changeset with all fields", %{interface: interface} do
attrs = %{
snmp_interface_id: interface.id,
ip_address: "192.168.1.100",
subnet_mask: "255.255.255.0",
prefix_length: 24,
ip_type: "ipv4",
is_primary: true,
last_checked_at: DateTime.utc_now(),
metadata: %{"source" => "ip-mib"}
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
assert changeset.valid?
assert get_field(changeset, :ip_address) == "192.168.1.100"
assert get_field(changeset, :subnet_mask) == "255.255.255.0"
assert get_field(changeset, :prefix_length) == 24
assert get_field(changeset, :is_primary) == true
end
test "valid changeset with IPv6 address", %{interface: interface} do
attrs = %{
snmp_interface_id: interface.id,
ip_address: "2001:db8::1",
prefix_length: 64,
ip_type: "ipv6"
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
assert changeset.valid?
assert get_field(changeset, :ip_type) == "ipv6"
end
test "invalid changeset without snmp_interface_id" do
attrs = %{
ip_address: "192.168.1.100",
ip_type: "ipv4"
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
refute changeset.valid?
assert "can't be blank" in errors_on(changeset).snmp_interface_id
end
test "invalid changeset without ip_address", %{interface: interface} do
attrs = %{
snmp_interface_id: interface.id,
ip_type: "ipv4"
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
refute changeset.valid?
assert "can't be blank" in errors_on(changeset).ip_address
end
test "invalid changeset without ip_type", %{interface: interface} do
attrs = %{
snmp_interface_id: interface.id,
ip_address: "192.168.1.100"
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
refute changeset.valid?
assert "can't be blank" in errors_on(changeset).ip_type
end
test "invalid ip_type value", %{interface: interface} do
attrs = %{
snmp_interface_id: interface.id,
ip_address: "192.168.1.100",
ip_type: "invalid"
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
refute changeset.valid?
assert "is invalid" in errors_on(changeset).ip_type
end
test "valid ip_type values", %{interface: interface} do
for ip_type <- ~w(ipv4 ipv6) do
attrs = %{
snmp_interface_id: interface.id,
ip_address:
if(ip_type == "ipv4", do: "192.168.1.#{:rand.uniform(254)}", else: "2001:db8::#{:rand.uniform(100)}"),
ip_type: ip_type
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
assert changeset.valid?, "ip_type '#{ip_type}' should be valid"
end
end
test "validates prefix_length range for IPv4", %{interface: interface} do
for invalid_prefix <- [-1, 33, 128] do
attrs = %{
snmp_interface_id: interface.id,
ip_address: "192.168.1.100",
ip_type: "ipv4",
prefix_length: invalid_prefix
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
refute changeset.valid?, "prefix_length #{invalid_prefix} should be invalid for IPv4"
end
end
test "accepts valid prefix_length for IPv4", %{interface: interface} do
for valid_prefix <- [0, 8, 16, 24, 32] do
attrs = %{
snmp_interface_id: interface.id,
ip_address: "192.168.1.100",
ip_type: "ipv4",
prefix_length: valid_prefix
}
changeset = IpAddress.changeset(%IpAddress{}, attrs)
assert changeset.valid?, "prefix_length #{valid_prefix} should be valid for IPv4"
end
end
end
describe "unique constraint" do
test "prevents duplicate IP addresses on same interface", %{interface: interface} do
attrs = %{
snmp_interface_id: interface.id,
ip_address: "192.168.1.100",
ip_type: "ipv4"
}
# Insert first IP
changeset = IpAddress.changeset(%IpAddress{}, attrs)
assert {:ok, _ip} = Repo.insert(changeset)
# Attempt to insert duplicate
changeset2 = IpAddress.changeset(%IpAddress{}, attrs)
assert {:error, changeset} = Repo.insert(changeset2)
errors = errors_on(changeset)
assert Enum.any?([:ip_address, :snmp_interface_id], &("has already been taken" in Map.get(errors, &1, [])))
end
test "allows same IP address on different interfaces", %{snmp_device: snmp_device, interface: interface} do
# Create second interface
interface2 =
%Interface{}
|> Interface.changeset(%{
snmp_device_id: snmp_device.id,
if_index: 2,
if_descr: "eth1",
if_type: 6,
if_admin_status: "up",
if_oper_status: "up"
})
|> Repo.insert!()
attrs1 = %{
snmp_interface_id: interface.id,
ip_address: "192.168.1.100",
ip_type: "ipv4"
}
attrs2 = %{
snmp_interface_id: interface2.id,
ip_address: "192.168.1.100",
ip_type: "ipv4"
}
changeset1 = IpAddress.changeset(%IpAddress{}, attrs1)
assert {:ok, _ip1} = Repo.insert(changeset1)
changeset2 = IpAddress.changeset(%IpAddress{}, attrs2)
assert {:ok, _ip2} = Repo.insert(changeset2)
end
end
end