333 lines
11 KiB
Elixir
333 lines
11 KiB
Elixir
defmodule SnmpKit.SnmpLib.WalkerTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias SnmpKit.SnmpLib.Walker
|
|
|
|
doctest Walker
|
|
|
|
@moduletag :walker_test
|
|
|
|
describe "Walker.walk_table/3" do
|
|
test "selects appropriate walking strategy based on SNMP version" do
|
|
# v2c should use bulk walking
|
|
assert {:error, _} =
|
|
Walker.walk_table("invalid.host.test", [1, 3, 6, 1, 2, 1, 2, 2],
|
|
version: :v2c,
|
|
timeout: 100
|
|
)
|
|
|
|
# v1 should use sequential walking
|
|
assert {:error, _} =
|
|
Walker.walk_table("invalid.host.test", [1, 3, 6, 1, 2, 1, 2, 2],
|
|
version: :v1,
|
|
timeout: 100
|
|
)
|
|
end
|
|
|
|
test "handles table OID normalization" do
|
|
# List OID
|
|
list_oid = [1, 3, 6, 1, 2, 1, 2, 2]
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", list_oid, timeout: 100)
|
|
|
|
# String OID
|
|
string_oid = "1.3.6.1.2.1.2.2"
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", string_oid, timeout: 100)
|
|
end
|
|
|
|
test "validates walking options" do
|
|
opts = [
|
|
community: "public",
|
|
version: :v2c,
|
|
timeout: 5000,
|
|
max_repetitions: 25,
|
|
max_retries: 3,
|
|
retry_delay: 1000
|
|
]
|
|
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1, 2, 1, 2, 2], opts)
|
|
end
|
|
|
|
test "handles bulk size configuration" do
|
|
# Small bulk size
|
|
small_opts = [max_repetitions: 5, timeout: 100]
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], small_opts)
|
|
|
|
# Large bulk size
|
|
large_opts = [max_repetitions: 100, timeout: 100]
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], large_opts)
|
|
end
|
|
end
|
|
|
|
describe "Walker.walk_subtree/3" do
|
|
test "walks entire subtree under base OID" do
|
|
# system subtree
|
|
base_oid = [1, 3, 6, 1, 2, 1, 1]
|
|
|
|
assert {:error, _} = Walker.walk_subtree("192.168.255.254", base_oid, timeout: 50)
|
|
end
|
|
|
|
test "handles subtree boundary detection" do
|
|
# Should stop when OIDs no longer match prefix
|
|
assert {:error, _} = Walker.walk_subtree("192.168.255.254", [1, 3, 6, 1], timeout: 50)
|
|
end
|
|
|
|
test "uses appropriate strategy for subtree walking" do
|
|
opts_v1 = [version: :v1, timeout: 100]
|
|
opts_v2c = [version: :v2c, timeout: 100]
|
|
|
|
# Both should attempt but fail due to invalid host
|
|
assert {:error, _} = Walker.walk_subtree("invalid.host.test", [1, 3, 6, 1], opts_v1)
|
|
assert {:error, _} = Walker.walk_subtree("invalid.host.test", [1, 3, 6, 1], opts_v2c)
|
|
end
|
|
end
|
|
|
|
describe "Walker.stream_table/3" do
|
|
test "returns a stream for table walking" do
|
|
stream = Walker.stream_table("invalid.host.test", [1, 3, 6, 1, 2, 1, 2, 2], timeout: 100)
|
|
|
|
# Should be enumerable
|
|
assert Enumerable.impl_for(stream)
|
|
|
|
# Should handle empty results gracefully when enumerated
|
|
# (will be empty due to invalid host)
|
|
results = Enum.take(stream, 5)
|
|
assert results == []
|
|
end
|
|
|
|
test "supports streaming configuration options" do
|
|
opts = [
|
|
chunk_size: 10,
|
|
max_repetitions: 20,
|
|
timeout: 100
|
|
]
|
|
|
|
stream = Walker.stream_table("invalid.host.test", [1, 3, 6, 1, 2, 1, 2, 2], opts)
|
|
|
|
# Should create stream with custom options
|
|
assert Enumerable.impl_for(stream)
|
|
end
|
|
|
|
test "integrates with Stream operations" do
|
|
stream = Walker.stream_table("invalid.host.test", [1, 3, 6, 1, 2, 1, 2, 2], timeout: 100)
|
|
|
|
# Should work with standard Stream operations
|
|
filtered =
|
|
stream
|
|
# Flatten chunks
|
|
|> Stream.flat_map(& &1)
|
|
|> Stream.filter(fn {_oid, value} -> value != nil end)
|
|
|> Stream.take(10)
|
|
|
|
# Should enumerate without error (though empty due to invalid host)
|
|
results = Enum.to_list(filtered)
|
|
assert results == []
|
|
end
|
|
end
|
|
|
|
describe "Walker.walk_column/3" do
|
|
test "extracts single column from table" do
|
|
# Interface description column
|
|
column_oid = [1, 3, 6, 1, 2, 1, 2, 2, 1, 2]
|
|
|
|
assert {:error, _} = Walker.walk_column("invalid.host.test", column_oid, timeout: 100)
|
|
end
|
|
|
|
test "returns index-value pairs for column data" do
|
|
# Test that the function signature is correct
|
|
assert is_function(&Walker.walk_column/3)
|
|
|
|
# Would test actual column extraction with mock data
|
|
column_oid = [1, 3, 6, 1, 2, 1, 2, 2, 1, 1]
|
|
assert {:error, _} = Walker.walk_column("invalid.host.test", column_oid, timeout: 100)
|
|
end
|
|
end
|
|
|
|
describe "Walker.estimate_table_size/3" do
|
|
test "estimates table size by walking first column" do
|
|
table_oid = [1, 3, 6, 1, 2, 1, 2, 2]
|
|
|
|
assert {:error, _} =
|
|
Walker.estimate_table_size("invalid.host.test", table_oid, timeout: 100)
|
|
end
|
|
|
|
test "provides count estimate for planning" do
|
|
# Test interface for size estimation
|
|
assert is_function(&Walker.estimate_table_size/3)
|
|
|
|
# Should return count when successful
|
|
# routing table
|
|
table_oid = [1, 3, 6, 1, 2, 1, 4, 21]
|
|
|
|
assert {:error, _} =
|
|
Walker.estimate_table_size("invalid.host.test", table_oid, timeout: 100)
|
|
end
|
|
end
|
|
|
|
describe "Walker strategy selection" do
|
|
test "chooses bulk walking for SNMPv2c" do
|
|
opts = [version: :v2c, timeout: 100]
|
|
|
|
# Should attempt bulk operations
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], opts)
|
|
end
|
|
|
|
test "falls back to sequential for SNMPv1" do
|
|
opts = [version: :v1, timeout: 100]
|
|
|
|
# Should use sequential walking
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], opts)
|
|
end
|
|
|
|
test "handles strategy-specific parameters" do
|
|
bulk_opts = [version: :v2c, max_repetitions: 50, timeout: 100]
|
|
seq_opts = [version: :v1, timeout: 100]
|
|
|
|
# Both should handle appropriately
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], bulk_opts)
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], seq_opts)
|
|
end
|
|
end
|
|
|
|
describe "Walker error handling" do
|
|
test "handles network timeouts gracefully" do
|
|
opts = [timeout: 20]
|
|
|
|
# Should timeout quickly
|
|
assert {:error, _} = Walker.walk_table("192.168.255.255", [1, 3, 6, 1], opts)
|
|
end
|
|
|
|
@tag :skip
|
|
test "implements retry logic" do
|
|
# Test with unreachable host
|
|
opts = [max_retries: 1, retry_delay: 50, timeout: 20]
|
|
|
|
# Should retry on failures
|
|
start_time = System.monotonic_time(:millisecond)
|
|
{:error, _} = Walker.walk_table("192.168.255.255", [1, 3, 6, 1], opts)
|
|
end_time = System.monotonic_time(:millisecond)
|
|
|
|
# Should take longer due to retries
|
|
# At least one retry delay
|
|
assert end_time - start_time >= 50
|
|
end
|
|
|
|
test "handles table boundary conditions" do
|
|
# Test with various table scenarios
|
|
assert {:error, _} = Walker.walk_table("192.168.255.254", [1, 3, 6, 1], timeout: 20)
|
|
assert {:error, _} = Walker.walk_subtree("192.168.255.254", [1, 3, 6, 1], timeout: 20)
|
|
end
|
|
|
|
test "gracefully handles empty tables" do
|
|
# Should handle tables with no entries
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1, 99], timeout: 100)
|
|
end
|
|
end
|
|
|
|
describe "Walker performance features" do
|
|
test "supports adaptive bulk sizing" do
|
|
opts = [adaptive_bulk: true, max_repetitions: 25, timeout: 100]
|
|
|
|
# Should attempt adaptive sizing
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], opts)
|
|
end
|
|
|
|
test "handles concurrent walking operations" do
|
|
# Test multiple concurrent table walks
|
|
tasks =
|
|
Enum.map(1..3, fn i ->
|
|
Task.async(fn ->
|
|
Walker.walk_table("invalid.host.test", [1, 3, 6, 1, i], timeout: 100)
|
|
end)
|
|
end)
|
|
|
|
results = Task.await_many(tasks, 10_000)
|
|
|
|
# All should complete (with errors due to invalid host)
|
|
assert length(results) == 3
|
|
assert Enum.all?(results, fn result -> match?({:error, _}, result) end)
|
|
end
|
|
|
|
@tag :performance
|
|
test "streaming is memory efficient" do
|
|
# Create stream but don't fully enumerate
|
|
stream = Walker.stream_table("invalid.host.test", [1, 3, 6, 1, 2, 1, 2, 2], timeout: 100)
|
|
|
|
# Take just a few items (should not load entire table)
|
|
partial_results = Enum.take(stream, 2)
|
|
|
|
# Should handle partial enumeration gracefully
|
|
assert partial_results == []
|
|
end
|
|
|
|
@tag :performance
|
|
test "bulk operations are more efficient than sequential" do
|
|
# This would test actual performance differences
|
|
# For now, verify that both strategies are available
|
|
|
|
bulk_opts = [version: :v2c, max_repetitions: 50, timeout: 100]
|
|
seq_opts = [version: :v1, timeout: 100]
|
|
|
|
# Both should be callable
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], bulk_opts)
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], seq_opts)
|
|
end
|
|
end
|
|
|
|
describe "Walker integration" do
|
|
test "integrates with Manager for individual operations" do
|
|
# Walker should use Manager for SNMP operations
|
|
assert is_function(&Walker.walk_table/3)
|
|
assert is_function(&Walker.estimate_table_size/3)
|
|
|
|
# Should handle Manager responses appropriately
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], timeout: 100)
|
|
end
|
|
|
|
test "uses SnmpKit.SnmpLib.OID for OID manipulation" do
|
|
# Should handle both string and list OIDs
|
|
list_oid = [1, 3, 6, 1, 2, 1, 2, 2]
|
|
string_oid = "1.3.6.1.2.1.2.2"
|
|
|
|
# Both should work
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", list_oid, timeout: 100)
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", string_oid, timeout: 100)
|
|
end
|
|
|
|
test "handles SnmpLib error responses correctly" do
|
|
# Should properly handle various SNMP error conditions
|
|
# These would be tested with actual SNMP responses
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], timeout: 100)
|
|
end
|
|
end
|
|
|
|
describe "Walker option validation" do
|
|
test "validates and applies default options" do
|
|
# Test with minimal options
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1])
|
|
|
|
# Test with custom options
|
|
custom_opts = [
|
|
community: "private",
|
|
timeout: 10_000,
|
|
max_repetitions: 100,
|
|
chunk_size: 50
|
|
]
|
|
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], custom_opts)
|
|
end
|
|
|
|
test "handles invalid option values gracefully" do
|
|
# Test with various option edge cases
|
|
opts = [
|
|
# Very short timeout
|
|
timeout: 1,
|
|
# Very large bulk size
|
|
max_repetitions: 1000,
|
|
# No retries
|
|
max_retries: 0
|
|
]
|
|
|
|
assert {:error, _} = Walker.walk_table("invalid.host.test", [1, 3, 6, 1], opts)
|
|
end
|
|
end
|
|
end
|