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

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