521 lines
16 KiB
Elixir
521 lines
16 KiB
Elixir
defmodule SnmpKit.SnmpMgr.BulkTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias SnmpKit.SnmpMgr.Bulk
|
|
|
|
doctest Bulk
|
|
|
|
describe "get_bulk/3 version validation" do
|
|
test "rejects SNMPv1 with error" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], version: :v1)
|
|
|
|
assert {:error, {:unsupported_operation, :get_bulk_requires_v2c}} = result
|
|
end
|
|
|
|
test "rejects SNMPv3 with error" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], version: :v3)
|
|
|
|
assert {:error, {:unsupported_operation, :get_bulk_requires_v2c}} = result
|
|
end
|
|
|
|
test "accepts SNMPv2c (explicit)" do
|
|
# Will timeout or error without proper SNMP infrastructure
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], version: :v2c, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "defaults to SNMPv2c when version not specified" do
|
|
# Should use v2c by default
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "get_bulk/3 OID handling" do
|
|
test "accepts single OID as list" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts single OID as string" do
|
|
result = Bulk.get_bulk("192.168.1.1", "1.3.6.1.2.1.1", timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts multiple OIDs as list" do
|
|
result = Bulk.get_bulk("192.168.1.1", [[1, 3, 6, 1, 2, 1, 1], [1, 3, 6, 1, 2, 1, 2]], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "converts single OID to list internally" do
|
|
# Single OID should be wrapped in a list
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "returns error for invalid OID" do
|
|
result = Bulk.get_bulk("192.168.1.1", "invalid.oid", timeout: 100)
|
|
|
|
assert {:error, _} = result
|
|
end
|
|
end
|
|
|
|
describe "get_bulk/3 options handling" do
|
|
test "uses default max_repetitions when not specified" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
# Default is 30
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "uses specified max_repetitions" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], max_repetitions: 10, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "uses default non_repeaters when not specified" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
# Default is 0
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "uses specified non_repeaters" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], non_repeaters: 1, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "forces version to v2c even if different version passed" do
|
|
# Should override any other version setting
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts community string option" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], community: "private", timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts port option" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], port: 1161, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts timeout option" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 5000)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "get_table_bulk/3" do
|
|
test "retrieves table using GETBULK" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts string table OID" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", "1.3.6.1.2.1.2.2", timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "uses default max_entries when not specified" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], timeout: 100)
|
|
|
|
# Default is 1000
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "respects max_entries option" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], max_entries: 100, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "respects max_repetitions option" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], max_repetitions: 20, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "returns error for invalid table OID" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", "invalid.table", timeout: 100)
|
|
|
|
assert {:error, _} = result
|
|
end
|
|
|
|
test "accepts community string" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], community: "public", timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "walk_bulk/3" do
|
|
test "performs bulk walk of subtree" do
|
|
result = Bulk.walk_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts string root OID" do
|
|
result = Bulk.walk_bulk("192.168.1.1", "1.3.6.1.2.1.1", timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "uses default max_entries when not specified" do
|
|
result = Bulk.walk_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
# Default is 1000
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "respects max_entries option" do
|
|
result = Bulk.walk_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], max_entries: 50, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "respects max_repetitions option" do
|
|
result = Bulk.walk_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], max_repetitions: 15, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "returns error for invalid root OID" do
|
|
result = Bulk.walk_bulk("192.168.1.1", "invalid", timeout: 100)
|
|
|
|
assert {:error, _} = result
|
|
end
|
|
|
|
test "accepts community string" do
|
|
result = Bulk.walk_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], community: "private", timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts port option" do
|
|
result = Bulk.walk_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], port: 1161, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "get_bulk_multi/2" do
|
|
test "performs concurrent GETBULK operations" do
|
|
requests = [
|
|
{"192.168.1.1", [1, 3, 6, 1, 2, 1, 1, 1, 0]},
|
|
{"192.168.1.2", [1, 3, 6, 1, 2, 1, 1, 2, 0]},
|
|
{"192.168.1.3", [1, 3, 6, 1, 2, 1, 1, 3, 0]}
|
|
]
|
|
|
|
results = Bulk.get_bulk_multi(requests, timeout: 100)
|
|
|
|
assert length(results) == 3
|
|
# Each result is either {:ok, _} or {:error, _}
|
|
assert Enum.all?(results, fn result ->
|
|
match?({:ok, _}, result) or match?({:error, _}, result)
|
|
end)
|
|
end
|
|
|
|
test "handles empty request list" do
|
|
results = Bulk.get_bulk_multi([], timeout: 100)
|
|
|
|
assert results == []
|
|
end
|
|
|
|
test "handles single request" do
|
|
requests = [{"192.168.1.1", [1, 3, 6, 1, 2, 1, 1, 1, 0]}]
|
|
|
|
results = Bulk.get_bulk_multi(requests, timeout: 100)
|
|
|
|
assert length(results) == 1
|
|
end
|
|
|
|
test "returns timeout error for tasks that don't complete" do
|
|
requests = [
|
|
{"192.168.1.1", [1, 3, 6, 1, 2, 1, 1, 1, 0]},
|
|
{"192.168.1.2", [1, 3, 6, 1, 2, 1, 1, 2, 0]}
|
|
]
|
|
|
|
# Very short timeout to force timeout
|
|
results = Bulk.get_bulk_multi(requests, timeout: 1)
|
|
|
|
assert length(results) == 2
|
|
# All should error (either timeout or other error like invalid_oid_input)
|
|
assert Enum.all?(results, fn result ->
|
|
match?({:error, _}, result)
|
|
end)
|
|
end
|
|
|
|
test "uses default timeout when not specified" do
|
|
requests = [{"192.168.1.1", [1, 3, 6, 1, 2, 1, 1, 1, 0]}]
|
|
|
|
results = Bulk.get_bulk_multi(requests)
|
|
|
|
assert length(results) == 1
|
|
end
|
|
|
|
test "passes options to individual requests" do
|
|
requests = [
|
|
{"192.168.1.1", [1, 3, 6, 1, 2, 1, 1, 1, 0]},
|
|
{"192.168.1.2", [1, 3, 6, 1, 2, 1, 1, 2, 0]}
|
|
]
|
|
|
|
results = Bulk.get_bulk_multi(requests, community: "public", max_repetitions: 10, timeout: 100)
|
|
|
|
assert length(results) == 2
|
|
end
|
|
|
|
test "handles task failures gracefully" do
|
|
requests = [
|
|
{"192.168.1.1", "invalid.oid"},
|
|
{"192.168.1.2", [1, 3, 6, 1, 2, 1, 1, 2, 0]}
|
|
]
|
|
|
|
results = Bulk.get_bulk_multi(requests, timeout: 100)
|
|
|
|
assert length(results) == 2
|
|
# First should error due to invalid OID
|
|
assert match?({:error, _}, Enum.at(results, 0))
|
|
end
|
|
|
|
test "accepts string OIDs in requests" do
|
|
requests = [
|
|
{"192.168.1.1", "1.3.6.1.2.1.1.1.0"},
|
|
{"192.168.1.2", "1.3.6.1.2.1.1.2.0"}
|
|
]
|
|
|
|
results = Bulk.get_bulk_multi(requests, timeout: 100)
|
|
|
|
assert length(results) == 2
|
|
end
|
|
|
|
test "accepts mixed OID formats in requests" do
|
|
requests = [
|
|
{"192.168.1.1", [1, 3, 6, 1, 2, 1, 1, 1, 0]},
|
|
{"192.168.1.2", "1.3.6.1.2.1.1.2.0"}
|
|
]
|
|
|
|
results = Bulk.get_bulk_multi(requests, timeout: 100)
|
|
|
|
assert length(results) == 2
|
|
end
|
|
end
|
|
|
|
describe "target formats" do
|
|
test "accepts IP address as target" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts hostname as target" do
|
|
result = Bulk.get_bulk("device.local", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts target with port" do
|
|
result = Bulk.get_bulk("192.168.1.1:161", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "error handling" do
|
|
test "handles network timeout" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 1)
|
|
|
|
assert match?({:error, _}, result)
|
|
end
|
|
|
|
test "handles invalid target" do
|
|
result = Bulk.get_bulk("999.999.999.999", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert match?({:error, _}, result)
|
|
end
|
|
|
|
test "handles unreachable target" do
|
|
result = Bulk.get_bulk("192.168.255.254", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert match?({:error, _}, result)
|
|
end
|
|
|
|
test "returns error for malformed options" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], max_repetitions: -1, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "edge cases" do
|
|
test "handles empty table results" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 999], timeout: 100)
|
|
|
|
# Empty table should return {:ok, []}
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "handles max_entries limit reached" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], max_entries: 1, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "handles zero max_entries" do
|
|
result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], max_entries: 0, timeout: 100)
|
|
|
|
# Zero max_entries should return empty results immediately
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "handles very large max_repetitions" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], max_repetitions: 1000, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "handles zero max_repetitions" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], max_repetitions: 0, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "handles single OID result" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1, 1, 0], max_repetitions: 1, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "get_bulk/3 with non_repeaters" do
|
|
test "uses non_repeaters for multiple OIDs" do
|
|
# When retrieving multiple OIDs, non_repeaters controls how many are scalar
|
|
result =
|
|
Bulk.get_bulk("192.168.1.1", [[1, 3, 6, 1, 2, 1, 1], [1, 3, 6, 1, 2, 1, 2]], non_repeaters: 1, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "defaults non_repeaters to 0" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
# Should use non_repeaters: 0 by default
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "accepts large non_repeaters value" do
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], non_repeaters: 100, timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "walk_bulk/3 vs get_table_bulk/3" do
|
|
test "walk_bulk and get_table_bulk produce similar results for tables" do
|
|
# Both should work for walking tables
|
|
walk_result = Bulk.walk_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], timeout: 100)
|
|
table_result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], timeout: 100)
|
|
|
|
# Both should either succeed or fail, but with same error type
|
|
same_outcome =
|
|
(match?({:ok, _}, walk_result) and match?({:ok, _}, table_result)) or
|
|
(match?({:error, _}, walk_result) and match?({:error, _}, table_result))
|
|
|
|
assert same_outcome == true
|
|
end
|
|
|
|
test "walk_bulk works for non-table subtrees" do
|
|
result = Bulk.walk_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], timeout: 100)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "multiple options combinations" do
|
|
test "accepts all options together" do
|
|
opts = [
|
|
version: :v2c,
|
|
community: "public",
|
|
port: 161,
|
|
timeout: 100,
|
|
max_repetitions: 20,
|
|
non_repeaters: 0,
|
|
max_entries: 500
|
|
]
|
|
|
|
result = Bulk.walk_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], opts)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "get_bulk with all options" do
|
|
opts = [
|
|
community: "private",
|
|
port: 1161,
|
|
timeout: 3000,
|
|
max_repetitions: 15,
|
|
non_repeaters: 1
|
|
]
|
|
|
|
result = Bulk.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 1], opts)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "get_table_bulk with all options" do
|
|
opts = [
|
|
community: "public",
|
|
port: 161,
|
|
timeout: 100,
|
|
max_repetitions: 25,
|
|
max_entries: 100
|
|
]
|
|
|
|
result = Bulk.get_table_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], opts)
|
|
|
|
assert elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "get_bulk_multi/2 with various request counts" do
|
|
test "handles large number of concurrent requests" do
|
|
requests =
|
|
for i <- 1..20 do
|
|
{"192.168.1.#{i}", [1, 3, 6, 1, 2, 1, 1, 1, 0]}
|
|
end
|
|
|
|
results = Bulk.get_bulk_multi(requests, timeout: 100)
|
|
|
|
assert length(results) == 20
|
|
end
|
|
|
|
test "all concurrent requests timeout consistently" do
|
|
requests =
|
|
for i <- 1..5 do
|
|
{"192.168.255.#{i}", [1, 3, 6, 1, 2, 1, 1, 1, 0]}
|
|
end
|
|
|
|
results = Bulk.get_bulk_multi(requests, timeout: 10)
|
|
|
|
assert length(results) == 5
|
|
# All should timeout or error
|
|
assert Enum.all?(results, fn result -> match?({:error, _}, result) end)
|
|
end
|
|
end
|
|
end
|