defmodule Towerops.SnmpFixtures do @moduledoc """ This module defines test helpers for creating SNMP entities. """ alias Towerops.DevicesFixtures alias Towerops.Organizations alias Towerops.Snmp @doc """ Generate an SNMP device with all required fields. ## Options - `:device` - Existing device to attach SNMP device to - `:device_id` - ID of device to attach SNMP device to - All other SNMP Device fields (sys_name, sys_descr, etc.) ## Examples iex> snmp_device_fixture() %Snmp.Device{sys_name: "device", ...} iex> snmp_device_fixture(%{sys_name: "router1"}) %Snmp.Device{sys_name: "router1", ...} """ def snmp_device_fixture(attrs \\ %{}) do # Convert keyword list to map if needed attrs = if is_list(attrs), do: Map.new(attrs), else: attrs # Handle device creation or lookup device = cond do attrs[:device] -> attrs[:device] attrs[:device_id] -> Towerops.Devices.get_device!(attrs[:device_id]) true -> DevicesFixtures.device_fixture() end # Default SNMP device attributes default_attrs = %{ device_id: device.id, sys_name: "device-#{System.unique_integer([:positive])}", sys_descr: "Test Device", sys_object_id: "1.3.6.1.4.1.14988.1", manufacturer: "Test Manufacturer", model: "Test Model" } # Merge with provided attrs merged_attrs = Map.merge( default_attrs, attrs |> Map.drop([:device, "device"]) |> Map.new(fn {k, v} -> {to_atom_key(k), v} end) ) {:ok, snmp_device} = %Snmp.Device{} |> Snmp.Device.changeset(merged_attrs) |> Towerops.Repo.insert() snmp_device end @doc """ Generate a wireless client with all required fields. ## Options - `:device` - Existing device to attach client to - `:device_id` - ID of device to attach client to - `:organization` - Organization for the client - `:organization_id` - ID of organization for the client - All other WirelessClient fields (mac_address, signal_strength, etc.) ## Examples iex> wireless_client_fixture() %WirelessClient{mac_address: "AA:BB:CC:DD:EE:FF", ...} iex> wireless_client_fixture(%{signal_strength: -85}) %WirelessClient{signal_strength: -85, ...} """ def wireless_client_fixture(attrs \\ %{}) do # Handle device creation or lookup device = attrs[:device] || create_device_with_organization(attrs) organization = device.organization || Organizations.get_organization!(device.organization_id) # Generate unique MAC address mac_suffix = 16_777_215 |> :rand.uniform() |> Integer.to_string(16) |> String.pad_leading(6, "0") mac_parts = mac_suffix |> String.graphemes() |> Enum.chunk_every(2) |> Enum.map(&Enum.join/1) default_mac = "AA:BB:CC:#{Enum.join(mac_parts, ":")}" # Default wireless client attributes default_attrs = %{ device_id: device.id, organization_id: organization.id, mac_address: default_mac, ip_address: "10.0.#{:rand.uniform(254)}.#{:rand.uniform(254)}", hostname: "client-#{System.unique_integer([:positive])}", signal_strength: -65, snr: 25, distance: 1000, tx_rate: 100_000, rx_rate: 50_000, uptime_seconds: 3600, last_seen_at: Towerops.Time.now(), metadata: %{} } # Merge with provided attrs, converting string keys if needed merged_attrs = Map.merge( default_attrs, attrs |> Map.drop([:device, :organization, "device", "organization"]) |> Map.new(fn {k, v} -> {to_atom_key(k), v} end) ) {:ok, wireless_client} = %Snmp.WirelessClient{} |> Snmp.WirelessClient.changeset(merged_attrs) |> Towerops.Repo.insert() wireless_client end defp to_atom_key(key) when is_atom(key), do: key defp to_atom_key(key) when is_binary(key), do: String.to_existing_atom(key) @doc """ Generate an ARP entry with all required fields. ## Options - `:device_id` - ID of device for ARP entry - All other ARPEntry fields (mac_address, ip_address, etc.) """ def arp_entry_fixture(device_id, attrs \\ %{}) when is_binary(device_id) do # Generate unique MAC and IP if not provided mac_suffix = 16_777_215 |> :rand.uniform() |> Integer.to_string(16) |> String.pad_leading(6, "0") mac_parts = mac_suffix |> String.graphemes() |> Enum.chunk_every(2) |> Enum.map(&Enum.join/1) default_mac = "AA:BB:CC:#{Enum.join(mac_parts, ":")}" default_attrs = %{ device_id: device_id, mac_address: default_mac, ip_address: "10.0.#{:rand.uniform(254)}.#{:rand.uniform(254)}", interface: "ether1", last_seen_at: Towerops.Time.now() } merged_attrs = Map.merge( default_attrs, Map.new(attrs, fn {k, v} -> {to_atom_key(k), v} end) ) {:ok, arp_entry} = %Snmp.ArpEntry{} |> Snmp.ArpEntry.changeset(merged_attrs) |> Towerops.Repo.insert() arp_entry end @doc """ Wrapper for wireless_client_fixture with positional arguments for device_id and org_id. """ def wireless_client_fixture(device_id, organization_id, attrs) when is_binary(device_id) and is_binary(organization_id) do wireless_client_fixture(Map.merge(attrs, %{device_id: device_id, organization_id: organization_id})) end @doc """ Generate an entity physical with all required fields. ## Options - `:snmp_device` - Existing SNMP device to attach entity to - `:snmp_device_id` - ID of SNMP device to attach entity to - All other EntityPhysical fields (entity_index, entity_class, etc.) ## Examples iex> entity_physical_fixture() %EntityPhysical{entity_index: "1", ...} iex> entity_physical_fixture(%{entity_class: "chassis"}) %EntityPhysical{entity_class: "chassis", ...} """ def entity_physical_fixture(attrs \\ %{}) do # Convert keyword list to map if needed attrs = if is_list(attrs), do: Map.new(attrs), else: attrs # Handle SNMP device creation or lookup snmp_device = cond do attrs[:snmp_device] -> attrs[:snmp_device] attrs[:snmp_device_id] -> Towerops.Repo.get!(Snmp.Device, attrs[:snmp_device_id]) true -> snmp_device_fixture() end # Default entity physical attributes default_attrs = %{ snmp_device_id: snmp_device.id, entity_index: "#{System.unique_integer([:positive])}", entity_class: "other", name: "Entity #{System.unique_integer([:positive])}" } # Merge with provided attrs merged_attrs = Map.merge( default_attrs, attrs |> Map.drop([:snmp_device, "snmp_device"]) |> Map.new(fn {k, v} -> {to_atom_key(k), v} end) ) {:ok, entity_physical} = %Snmp.EntityPhysical{} |> Snmp.EntityPhysical.changeset(merged_attrs) |> Towerops.Repo.insert() entity_physical end @doc """ Generate a transceiver with all required fields. ## Options - `:snmp_device` - Existing SNMP device to attach transceiver to - `:snmp_device_id` - ID of SNMP device to attach transceiver to - `:entity_physical` - Existing entity physical to link transceiver to - `:entity_physical_id` - ID of entity physical to link transceiver to - All other Transceiver fields (port_index, transceiver_type, vendor_name, etc.) ## Examples iex> transceiver_fixture() %Transceiver{port_index: "1", ...} iex> transceiver_fixture(%{transceiver_type: "SFP+"}) %Transceiver{transceiver_type: "SFP+", ...} """ def transceiver_fixture(attrs \\ %{}) do # Convert keyword list to map if needed attrs = if is_list(attrs), do: Map.new(attrs), else: attrs # Handle SNMP device creation or lookup snmp_device = cond do attrs[:snmp_device] -> attrs[:snmp_device] attrs[:snmp_device_id] -> Towerops.Repo.get!(Snmp.Device, attrs[:snmp_device_id]) true -> snmp_device_fixture() end # Handle entity physical lookup if provided entity_physical_id = cond do attrs[:entity_physical] -> attrs[:entity_physical].id attrs[:entity_physical_id] -> attrs[:entity_physical_id] true -> nil end # Default transceiver attributes default_attrs = %{ snmp_device_id: snmp_device.id, entity_physical_id: entity_physical_id, port_index: "#{System.unique_integer([:positive])}", transceiver_type: "SFP", vendor_name: "Test Vendor", vendor_part_number: "TEST-SFP-1G", vendor_serial_number: "SN#{System.unique_integer([:positive])}", supports_dom: true } # Merge with provided attrs merged_attrs = Map.merge( default_attrs, attrs |> Map.drop([:snmp_device, :entity_physical, "snmp_device", "entity_physical"]) |> Map.new(fn {k, v} -> {to_atom_key(k), v} end) ) {:ok, transceiver} = %Snmp.Transceiver{} |> Snmp.Transceiver.changeset(merged_attrs) |> Towerops.Repo.insert() transceiver end @doc """ Generate a transceiver reading with all required fields. ## Options - `:transceiver` - Existing transceiver to attach reading to - `:transceiver_id` - ID of transceiver to attach reading to - All other TransceiverReading fields (rx_power_dbm, tx_power_dbm, etc.) ## Examples iex> transceiver_reading_fixture() %TransceiverReading{rx_power_dbm: -5.0, ...} iex> transceiver_reading_fixture(%{tx_power_dbm: -3.2}) %TransceiverReading{tx_power_dbm: -3.2, ...} """ def transceiver_reading_fixture(attrs \\ %{}) do # Convert keyword list to map if needed attrs = if is_list(attrs), do: Map.new(attrs), else: attrs # Handle transceiver creation or lookup transceiver = cond do attrs[:transceiver] -> attrs[:transceiver] attrs[:transceiver_id] -> Towerops.Repo.get!(Snmp.Transceiver, attrs[:transceiver_id]) true -> transceiver_fixture() end # Default reading attributes default_attrs = %{ transceiver_id: transceiver.id, measured_at: Towerops.Time.now() } # Merge with provided attrs merged_attrs = Map.merge( default_attrs, attrs |> Map.drop([:transceiver, "transceiver"]) |> Map.new(fn {k, v} -> {to_atom_key(k), v} end) ) {:ok, reading} = %Snmp.TransceiverReading{} |> Snmp.TransceiverReading.changeset(merged_attrs) |> Towerops.Repo.insert() reading end defp create_device_with_organization(attrs) do case {attrs[:device_id], attrs[:organization_id]} do {device_id, _} when not is_nil(device_id) -> Towerops.Devices.get_device!(device_id) {_, organization_id} when not is_nil(organization_id) -> DevicesFixtures.device_fixture(%{organization_id: organization_id}) _ -> DevicesFixtures.device_fixture() end end end