towerops/test/snmpkit/snmp_mgr_integration_test.exs
2026-06-15 11:15:46 -05:00

502 lines
13 KiB
Elixir

defmodule SnmpKit.SnmpMgrIntegrationTest do
use ExUnit.Case, async: false
alias SnmpKit.SnmpMgr
@moduletag :integration
@moduletag :snmpkit
describe "SnmpMgr get/3" do
test "successfully retrieves sysUpTime from localhost simulator" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1.3.0"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.get(target, oid, opts) do
{:ok, value} ->
assert tuple_size(value) == 2
{type, _data} = value
assert type == :timeticks
{:error, :timeout} ->
# Simulator not running, skip test
:ok
{:error, reason} ->
flunk("Unexpected error: #{inspect(reason)}")
end
end
test "handles string OID format" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1.1.0"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
result = SnmpMgr.get(target, oid, opts)
assert elem(result, 0) in [:ok, :error]
end
test "handles list OID format" do
target = "127.0.0.1"
oid = [1, 3, 6, 1, 2, 1, 1, 1, 0]
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
result = SnmpMgr.get(target, oid, opts)
assert elem(result, 0) in [:ok, :error]
end
test "returns error for invalid OID" do
target = "127.0.0.1"
oid = "1.3.6.1.99.99.99.99"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.get(target, oid, opts) do
{:error, _reason} -> :ok
{:ok, _} -> flunk("Expected error for invalid OID")
end
end
test "handles timeout gracefully" do
target = "192.0.2.1"
# RFC 5737 TEST-NET-1 address that should not route
oid = "1.3.6.1.2.1.1.1.0"
opts = [community: "public", version: :v2c, port: 161, timeout: 1000]
assert {:error, :timeout} = SnmpMgr.get(target, oid, opts)
end
test "returns error for wrong community string" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1.1.0"
opts = [community: "wrong_community", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.get(target, oid, opts) do
{:error, _reason} ->
:ok
{:ok, _} ->
# Simulator may allow any community
:ok
end
end
end
describe "SnmpMgr walk/3" do
test "successfully walks interface table" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.2.2.1.2"
opts = [community: "public", version: :v2c, port: 1161, timeout: 10_000]
case SnmpMgr.walk(target, oid, opts) do
{:ok, results} ->
case results do
[first | _rest] ->
# Verify structure of results
assert Map.has_key?(first, :oid)
assert Map.has_key?(first, :value)
[] ->
:ok
end
{:error, :timeout} ->
# Simulator not running
:ok
{:error, reason} ->
# Other errors are acceptable (no such object, etc.)
assert reason in [:noSuchName, :noSuchObject, :genErr, :tooBig, :badValue, :readOnly]
end
end
test "returns empty list for non-existent subtree" do
target = "127.0.0.1"
oid = "1.3.6.1.99.99.99"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.walk(target, oid, opts) do
{:ok, results} ->
assert results == []
{:error, _reason} ->
# Error is also acceptable
:ok
end
end
test "handles walk timeout" do
target = "192.0.2.1"
oid = "1.3.6.1.2.1.2.2.1"
opts = [community: "public", version: :v2c, port: 161, timeout: 1000]
assert {:error, :timeout} = SnmpMgr.walk(target, oid, opts)
end
test "walk result format is consistent" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.walk(target, oid, opts) do
{:ok, results} when is_list(results) ->
# All results should be maps with :oid and :value keys
for result <- results do
assert Map.has_key?(result, :oid)
assert Map.has_key?(result, :value)
assert byte_size(result.oid) > 0
end
{:error, :timeout} ->
:ok
{:error, _} ->
:ok
end
end
end
describe "SnmpMgr get_bulk/3" do
test "successfully performs get_bulk operation" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.2.2.1.10"
opts = [
community: "public",
version: :v2c,
port: 1161,
timeout: 5000,
max_repetitions: 10
]
case SnmpMgr.get_bulk(target, oid, opts) do
{:ok, results} ->
case results do
[first | _rest] ->
assert Map.has_key?(first, :oid)
assert Map.has_key?(first, :value)
[] ->
:ok
end
{:error, :timeout} ->
:ok
{:error, reason} ->
assert match?(r when is_atom(r), reason)
end
end
test "respects max_repetitions parameter" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.2.2.1"
opts = [
community: "public",
version: :v2c,
port: 1161,
timeout: 5000,
max_repetitions: 5
]
case SnmpMgr.get_bulk(target, oid, opts) do
{:ok, results} ->
# Should get at most max_repetitions results per column
assert match?(l when is_list(l), results)
{:error, :timeout} ->
:ok
{:error, _} ->
:ok
end
end
test "get_bulk only works with SNMPv2c" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.2.2.1.10"
# Try with v1 - should not support get_bulk
opts_v1 = [community: "public", version: :v1, port: 1161, timeout: 5000]
case SnmpMgr.get_bulk(target, oid, opts_v1) do
{:error, _reason} ->
# Expected - v1 doesn't support get_bulk
:ok
{:ok, _} ->
# Some implementations may allow it or fall back
:ok
end
# Try with v2c - should work
opts_v2c = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.get_bulk(target, oid, opts_v2c) do
{:ok, results} ->
assert match?(l when is_list(l), results)
{:error, :timeout} ->
:ok
{:error, _} ->
:ok
end
end
end
describe "SnmpMgr with different versions" do
test "SNMPv1 get operation" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1.3.0"
opts = [community: "public", version: :v1, port: 1161, timeout: 5000]
result = SnmpMgr.get(target, oid, opts)
assert elem(result, 0) in [:ok, :error]
end
test "SNMPv2c get operation" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1.3.0"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
result = SnmpMgr.get(target, oid, opts)
assert elem(result, 0) in [:ok, :error]
end
end
describe "SnmpMgr error handling" do
test "handles network errors gracefully" do
target = "192.0.2.1"
oid = "1.3.6.1.2.1.1.1.0"
opts = [community: "public", version: :v2c, port: 161, timeout: 1000]
assert {:error, :timeout} = SnmpMgr.get(target, oid, opts)
end
test "handles invalid target format" do
target = "not-a-valid-ip"
oid = "1.3.6.1.2.1.1.1.0"
opts = [community: "public", version: :v2c, port: 161, timeout: 1000]
case SnmpMgr.get(target, oid, opts) do
{:error, _reason} ->
:ok
{:ok, _} ->
:ok
# Some systems may resolve hostnames
end
end
test "handles malformed OID" do
target = "127.0.0.1"
# Invalid OID format
oid = "not.an.oid"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.get(target, oid, opts) do
{:error, _reason} ->
# Expected
:ok
{:ok, _} ->
# Some parsers may handle this
:ok
end
end
end
describe "SnmpMgr real-world scenarios" do
test "retrieve system information bundle" do
target = "127.0.0.1"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
system_oids = [
"1.3.6.1.2.1.1.1.0",
# sysDescr
"1.3.6.1.2.1.1.2.0",
# sysObjectID
"1.3.6.1.2.1.1.3.0",
# sysUpTime
"1.3.6.1.2.1.1.4.0",
# sysContact
"1.3.6.1.2.1.1.5.0",
# sysName
"1.3.6.1.2.1.1.6.0"
# sysLocation
]
# Try to get all system info
results =
Enum.map(system_oids, fn oid ->
SnmpMgr.get(target, oid, opts)
end)
# At least some should succeed (if simulator running) or all timeout
assert Enum.all?(results, fn result ->
match?({:ok, _}, result) or match?({:error, _}, result)
end)
end
test "walk and count interfaces" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.2.2.1.1"
# ifIndex
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.walk(target, oid, opts) do
{:ok, results} ->
interface_count = length(results)
assert interface_count >= 0
{:error, :timeout} ->
:ok
{:error, _} ->
:ok
end
end
test "retrieve interface statistics" do
target = "127.0.0.1"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
# Common interface stats OIDs
stats_oids = [
"1.3.6.1.2.1.2.2.1.10.1",
# ifInOctets for interface 1
"1.3.6.1.2.1.2.2.1.16.1",
# ifOutOctets for interface 1
"1.3.6.1.2.1.2.2.1.8.1"
# ifOperStatus for interface 1
]
results =
Enum.map(stats_oids, fn oid ->
SnmpMgr.get(target, oid, opts)
end)
assert Enum.all?(results, fn result ->
match?({:ok, _}, result) or match?({:error, _}, result)
end)
end
end
describe "SnmpMgr performance and reliability" do
test "handles multiple concurrent requests" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1.3.0"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
tasks =
for _ <- 1..5 do
Task.async(fn -> SnmpMgr.get(target, oid, opts) end)
end
results = Task.await_many(tasks, 10_000)
# All requests should complete
assert length(results) == 5
# All should either succeed or have consistent errors
assert Enum.all?(results, fn result ->
match?({:ok, _}, result) or match?({:error, _}, result)
end)
end
test "respects timeout parameter" do
target = "192.0.2.1"
oid = "1.3.6.1.2.1.1.1.0"
opts = [community: "public", version: :v2c, port: 161, timeout: 500]
start_time = System.monotonic_time(:millisecond)
result = SnmpMgr.get(target, oid, opts)
end_time = System.monotonic_time(:millisecond)
assert {:error, :timeout} = result
# Should timeout around the specified timeout value
duration = end_time - start_time
# Allow some overhead
assert duration < 2000
end
test "handles rapid sequential requests" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1.3.0"
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
results =
for _ <- 1..10 do
SnmpMgr.get(target, oid, opts)
end
# All should complete
assert length(results) == 10
# All should have consistent results
assert Enum.all?(results, fn result ->
match?({:ok, _}, result) or match?({:error, _}, result)
end)
end
end
describe "SnmpMgr data type handling" do
test "correctly handles timeticks values" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1.3.0"
# sysUpTime
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.get(target, oid, opts) do
{:ok, value} ->
assert elem(value, 0) == :timeticks
{:error, :timeout} ->
:ok
{:error, _} ->
:ok
end
end
test "correctly handles octet string values" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.1.1.0"
# sysDescr
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.get(target, oid, opts) do
{:ok, value} ->
# Could be :octet_string
type = elem(value, 0)
assert type == :octet_string
{:error, :timeout} ->
:ok
{:error, _} ->
:ok
end
end
test "correctly handles integer values" do
target = "127.0.0.1"
oid = "1.3.6.1.2.1.2.2.1.8.1"
# ifOperStatus
opts = [community: "public", version: :v2c, port: 1161, timeout: 5000]
case SnmpMgr.get(target, oid, opts) do
{:ok, value} ->
assert is_integer(value)
{:error, :timeout} ->
:ok
{:error, _} ->
:ok
end
end
end
end