Removes unnecessary two-layer architecture (Oban Coordinator → GenServer) in favor of direct Oban workers that perform the actual work. Changes: - Replace DevicePollerCoordinator + PollerWorker with DevicePollerWorker - Replace DeviceMonitorCoordinator + DeviceMonitor with DeviceMonitorWorker - Simplify Monitoring.Supervisor (removed Registries, DynamicSupervisors) - Remove all Exq dependencies (workers, supervisor, mix deps) - Convert DiscoveryWorker from Exq to Oban pattern - Update Devices context to auto-start/stop monitoring and polling - Update all LiveView callers to use new Oban enqueue pattern - Fix all tests to use Oban.Job struct instead of raw IDs Benefits: - Simpler codebase (~850 lines removed) - Better reliability (Oban handles retries, failures) - Lower memory (no persistent GenServers) - Better observability (work visible in Oban dashboard) - Cluster-wide coordination via PostgreSQL - Extensive PubSub usage for real-time events Files deleted: - lib/towerops/workers/device_monitor_coordinator.ex - lib/towerops/workers/device_poller_coordinator.ex - lib/towerops/snmp/poller_worker.ex - lib/towerops/monitoring/device_monitor.ex - lib/towerops/workers/monitor_worker.ex - lib/towerops/workers/poll_worker.ex - lib/towerops/exq_supervisor.ex - All related test files Files created: - lib/towerops/workers/device_poller_worker.ex - lib/towerops/workers/device_monitor_worker.ex All 3,686 tests passing.
140 lines
4.1 KiB
Elixir
140 lines
4.1 KiB
Elixir
defmodule Towerops.Integration.SnmpIntegrationTest do
|
|
@moduledoc """
|
|
Integration tests for SNMP functionality against real devices.
|
|
|
|
These tests are excluded from the main test suite by default.
|
|
To run them: mix test --only integration
|
|
|
|
Requirements:
|
|
- Network access to test devices
|
|
- SNMP-enabled devices (configure IP/community below)
|
|
"""
|
|
use ExUnit.Case, async: false
|
|
|
|
alias Towerops.Snmp.Client
|
|
alias Towerops.Snmp.Poller
|
|
alias Towerops.Workers.DeviceMonitorWorker
|
|
|
|
@moduletag :integration
|
|
|
|
# Configure your test device here
|
|
@test_device_opts [
|
|
ip: "10.0.19.254",
|
|
community: "kdyyJrT0Mm",
|
|
version: "2c",
|
|
port: 161,
|
|
timeout: 5000
|
|
]
|
|
|
|
describe "SNMP Client against real device" do
|
|
test "can perform GET operation" do
|
|
case Client.get(@test_device_opts, "1.3.6.1.2.1.1.3.0") do
|
|
{:ok, uptime} ->
|
|
assert is_integer(uptime)
|
|
|
|
{:error, reason} ->
|
|
flunk("SNMP GET failed: #{inspect(reason)}")
|
|
end
|
|
end
|
|
|
|
test "can perform WALK operation" do
|
|
case Client.walk(@test_device_opts, "1.3.6.1.2.1.2.2.1.1") do
|
|
{:ok, results} ->
|
|
assert is_map(results)
|
|
assert map_size(results) > 0
|
|
|
|
{:error, reason} ->
|
|
flunk("SNMP WALK failed: #{inspect(reason)}")
|
|
end
|
|
end
|
|
|
|
test "can test connection" do
|
|
case Client.test_connection(@test_device_opts) do
|
|
{:ok, _message} ->
|
|
assert true
|
|
|
|
{:error, reason} ->
|
|
flunk("Connection test failed: #{inspect(reason)}")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "SNMP Poller against real device" do
|
|
test "can check device reachability" do
|
|
case Poller.check_device(@test_device_opts) do
|
|
{:ok, response_time} ->
|
|
assert is_float(response_time)
|
|
assert response_time > 0
|
|
|
|
{:error, reason} ->
|
|
flunk("Poller check failed: #{inspect(reason)}")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "Discovery against real device" do
|
|
@tag :full_discovery
|
|
test "can discover device using Base profile" do
|
|
alias Towerops.Snmp.Profiles.Base
|
|
|
|
# Discover system info using Base profile
|
|
{:ok, system_info} = Base.discover_system_info(@test_device_opts)
|
|
assert is_binary(system_info.sys_descr)
|
|
assert is_binary(system_info.sys_name)
|
|
|
|
# Discover interfaces
|
|
{:ok, interfaces} = Base.discover_interfaces(@test_device_opts)
|
|
refute Enum.empty?(interfaces)
|
|
|
|
# Discover sensors (may be empty if device doesn't support ENTITY-SENSOR-MIB)
|
|
{:ok, sensors} = Base.discover_sensors(@test_device_opts)
|
|
assert is_list(sensors)
|
|
|
|
# Identify device
|
|
device_info = Base.identify_device(system_info)
|
|
assert is_binary(device_info.manufacturer)
|
|
assert is_binary(device_info.model)
|
|
end
|
|
end
|
|
|
|
describe "Device Monitor with SNMP" do
|
|
test "creates SNMP-specific alert messages when device goes down" do
|
|
# This test makes real SNMP calls to an unreachable IP to verify timeout handling
|
|
import Towerops.AccountsFixtures
|
|
|
|
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} =
|
|
Towerops.Devices.create_device(%{
|
|
name: "SNMP Router",
|
|
ip_address: "192.0.2.1",
|
|
site_id: site.id,
|
|
snmp_enabled: true,
|
|
snmp_version: "2c",
|
|
snmp_community: "public",
|
|
snmp_port: 161,
|
|
monitoring_enabled: true
|
|
})
|
|
|
|
# Set to up first so status change will trigger
|
|
{:ok, device} = Towerops.Devices.update_device_status(device, :up)
|
|
|
|
# Trigger check - will timeout trying to reach 192.0.2.1
|
|
DeviceMonitorWorker.perform(%Oban.Job{args: %{"device_id" => device.id}})
|
|
|
|
alerts = Towerops.Alerts.list_devices_alerts(device.id)
|
|
refute Enum.empty?(alerts)
|
|
|
|
alert = hd(alerts)
|
|
assert alert.alert_type == :device_down
|
|
assert String.contains?(alert.message, "SNMP")
|
|
end
|
|
end
|
|
end
|