towerops/test/snmpkit/snmp_lib/utils_test.exs

498 lines
15 KiB
Elixir

defmodule SnmpKit.SnmpLib.UtilsTest do
use ExUnit.Case, async: true
alias SnmpKit.SnmpLib.Utils
doctest Utils
describe "Utils.pretty_print_pdu/1" do
test "formats basic GET request PDU" do
pdu = %{
type: :get_request,
request_id: 123,
varbinds: [
{[1, 3, 6, 1, 2, 1, 1, 1, 0], :null}
]
}
result = Utils.pretty_print_pdu(pdu)
assert String.contains?(result, "GET Request")
assert String.contains?(result, "ID: 123")
assert String.contains?(result, "1.3.6.1.2.1.1.1.0")
end
test "formats error response PDU" do
pdu = %{
type: :get_response,
request_id: 456,
error_status: 2,
error_index: 1,
varbinds: []
}
result = Utils.pretty_print_pdu(pdu)
assert String.contains?(result, "Response")
assert String.contains?(result, "ID: 456")
assert String.contains?(result, "Error:")
assert String.contains?(result, "no varbinds")
end
test "formats GET-BULK request PDU" do
pdu = %{
type: :get_bulk_request,
request_id: 789,
non_repeaters: 1,
max_repetitions: 10,
varbinds: [
{[1, 3, 6, 1, 2, 1, 2, 2, 1, 1], :null}
]
}
result = Utils.pretty_print_pdu(pdu)
assert String.contains?(result, "GET-BULK Request")
assert String.contains?(result, "Non-repeaters: 1")
assert String.contains?(result, "Max-repetitions: 10")
end
test "handles invalid PDU gracefully" do
result = Utils.pretty_print_pdu("not a map")
assert result == "Invalid PDU"
end
end
describe "Utils.pretty_print_varbinds/1" do
test "formats varbinds list" do
varbinds = [
{[1, 3, 6, 1, 2, 1, 1, 1, 0], "Linux server"},
{[1, 3, 6, 1, 2, 1, 1, 3, 0], {:timeticks, 12_345}}
]
result = Utils.pretty_print_varbinds(varbinds)
assert String.contains?(result, "1. 1.3.6.1.2.1.1.1.0")
assert String.contains?(result, "2. 1.3.6.1.2.1.1.3.0")
assert String.contains?(result, "Linux server")
assert String.contains?(result, "TimeTicks")
end
test "handles empty varbinds list" do
result = Utils.pretty_print_varbinds([])
assert result == " (no varbinds)"
end
test "handles invalid varbinds" do
result = Utils.pretty_print_varbinds("not a list")
assert result == "Invalid varbinds"
end
end
describe "Utils.pretty_print_oid/1" do
test "formats OID list" do
oid = [1, 3, 6, 1, 2, 1, 1, 1, 0]
result = Utils.pretty_print_oid(oid)
assert result == "1.3.6.1.2.1.1.1.0"
end
test "handles OID string" do
oid = "1.3.6.1.2.1.1.1.0"
result = Utils.pretty_print_oid(oid)
assert result == "1.3.6.1.2.1.1.1.0"
end
test "handles invalid OID" do
result = Utils.pretty_print_oid(123)
assert result == "invalid-oid"
end
end
describe "Utils.pretty_print_value/1" do
test "formats null value" do
result = Utils.pretty_print_value(:null)
assert result == "NULL"
end
test "formats integer values" do
result = Utils.pretty_print_value({:integer, 42})
assert result == "INTEGER: 42"
result = Utils.pretty_print_value(42)
assert result == "INTEGER: 42"
end
test "formats string values" do
result = Utils.pretty_print_value({:octet_string, "Hello"})
assert result == ~s(OCTET STRING: "Hello")
result = Utils.pretty_print_value("Hello")
assert result == ~s("Hello")
end
test "formats binary data as hex" do
binary_data = <<0x00, 0x1B, 0x21, 0x3C>>
result = Utils.pretty_print_value({:octet_string, binary_data})
assert String.contains?(result, "00 1B 21 3C")
result = Utils.pretty_print_value(binary_data)
assert String.contains?(result, "00 1B 21 3C")
end
test "formats SNMP-specific types" do
result = Utils.pretty_print_value({:counter32, 12_345})
assert result == "Counter32: 12,345"
result = Utils.pretty_print_value({:gauge32, 67_890})
assert result == "Gauge32: 67,890"
result = Utils.pretty_print_value({:timeticks, 123_456})
assert String.contains?(result, "TimeTicks: 123,456")
result = Utils.pretty_print_value({:counter64, 9_876_543_210})
assert result == "Counter64: 9,876,543,210"
end
test "formats IP address" do
result = Utils.pretty_print_value({:ip_address, <<192, 168, 1, 1>>})
assert result == "IpAddress: 192.168.1.1"
end
test "formats OID value" do
result = Utils.pretty_print_value({:object_identifier, [1, 3, 6, 1, 2, 1, 1, 1, 0]})
assert result == "OID: 1.3.6.1.2.1.1.1.0"
end
test "formats SNMPv2c exception values" do
result = Utils.pretty_print_value({:no_such_object, nil})
assert result == "noSuchObject"
result = Utils.pretty_print_value({:no_such_instance, nil})
assert result == "noSuchInstance"
result = Utils.pretty_print_value({:end_of_mib_view, nil})
assert result == "endOfMibView"
end
test "formats unknown values" do
result = Utils.pretty_print_value({:unknown_type, "data"})
assert String.contains?(result, "unknown_type")
end
end
describe "Utils.format_bytes/1" do
test "formats bytes" do
assert Utils.format_bytes(512) == "512 B"
assert Utils.format_bytes(0) == "0 B"
end
test "formats kilobytes" do
assert Utils.format_bytes(1024) == "1.0 KB"
assert Utils.format_bytes(1536) == "1.5 KB"
end
test "formats megabytes" do
assert Utils.format_bytes(1_048_576) == "1.0 MB"
assert Utils.format_bytes(2_621_440) == "2.5 MB"
end
test "formats gigabytes" do
assert Utils.format_bytes(1_073_741_824) == "1.0 GB"
assert Utils.format_bytes(2_147_483_648) == "2.0 GB"
end
test "handles invalid input" do
assert Utils.format_bytes(-1) == "Invalid byte count"
assert Utils.format_bytes("not a number") == "Invalid byte count"
end
end
describe "Utils.format_rate/2" do
test "formats basic rates" do
assert Utils.format_rate(100, "bps") == "100 bps"
assert Utils.format_rate(45, "pps") == "45 pps"
end
test "formats kilo rates" do
assert Utils.format_rate(1500, "bps") == "1.5 Kbps"
assert Utils.format_rate(2000, "pps") == "2.0 Kpps"
end
test "formats mega rates" do
assert Utils.format_rate(1_500_000, "bps") == "1.5 Mbps"
assert Utils.format_rate(10_000_000, "Hz") == "10.0 MHz"
end
test "formats giga rates" do
assert Utils.format_rate(1_000_000_000, "bps") == "1.0 Gbps"
assert Utils.format_rate(2_500_000_000, "Hz") == "2.5 GHz"
end
test "handles invalid input" do
assert Utils.format_rate("not a number", "bps") == "Invalid rate"
assert Utils.format_rate(100, 123) == "Invalid rate"
end
end
describe "Utils.truncate_string/2" do
test "truncates long strings" do
result = Utils.truncate_string("Hello, World!", 10)
assert result == "Hello, ..."
end
test "leaves short strings unchanged" do
result = Utils.truncate_string("Short", 10)
assert result == "Short"
end
test "handles exact length" do
result = Utils.truncate_string("1234567890", 10)
assert result == "1234567890"
end
test "handles edge cases" do
result = Utils.truncate_string("Test", 3)
# max_length too small for ellipsis
assert result == "Test"
result = Utils.truncate_string("", 10)
assert result == ""
result = Utils.truncate_string(123, 10)
assert result == ""
end
end
describe "Utils.format_hex/2" do
test "formats hex with default separator" do
data = <<0x00, 0x1B, 0x21, 0x3C, 0x92, 0xEB>>
result = Utils.format_hex(data)
assert result == "00:1B:21:3C:92:EB"
end
test "formats hex with custom separator" do
data = <<0xDE, 0xAD, 0xBE, 0xEF>>
result = Utils.format_hex(data, " ")
assert result == "DE AD BE EF"
result = Utils.format_hex(data, "-")
assert result == "DE-AD-BE-EF"
end
test "handles empty binary" do
result = Utils.format_hex(<<>>)
assert result == ""
end
test "handles single byte" do
result = Utils.format_hex(<<0xFF>>)
assert result == "FF"
end
test "handles invalid input" do
result = Utils.format_hex(12_345, ":")
assert result == "Invalid binary data"
result = Utils.format_hex(<<0x00>>, 123)
assert result == "Invalid binary data"
end
end
describe "Utils.format_number/1" do
test "formats numbers with commas" do
assert Utils.format_number(1_234_567) == "1,234,567"
assert Utils.format_number(12_345) == "12,345"
assert Utils.format_number(1234) == "1,234"
end
test "handles small numbers" do
assert Utils.format_number(123) == "123"
assert Utils.format_number(42) == "42"
assert Utils.format_number(0) == "0"
end
test "handles negative numbers" do
assert Utils.format_number(-12_345) == "-12,345"
assert Utils.format_number(-123) == "-123"
end
test "handles invalid input" do
assert Utils.format_number("not a number") == "Invalid number"
assert Utils.format_number(12.34) == "Invalid number"
end
end
describe "Utils.measure_request_time/1" do
test "measures function execution time" do
{result, time} =
Utils.measure_request_time(fn ->
:timer.sleep(10)
:test_result
end)
assert result == :test_result
# At least 10ms in microseconds
assert time >= 10_000
assert is_integer(time)
end
test "handles functions that return various types" do
{result, time} = Utils.measure_request_time(fn -> {:ok, "success"} end)
assert result == {:ok, "success"}
assert is_integer(time)
assert_raise RuntimeError, fn ->
Utils.measure_request_time(fn -> raise "error" end)
end
end
end
describe "Utils.format_response_time/1" do
test "formats microseconds" do
assert Utils.format_response_time(500) == "500μs"
assert Utils.format_response_time(999) == "999μs"
end
test "formats milliseconds" do
assert Utils.format_response_time(1500) == "1.50ms"
assert Utils.format_response_time(10_000) == "10.00ms"
assert Utils.format_response_time(999_999) == "1000.00ms"
end
test "formats seconds" do
assert Utils.format_response_time(1_500_000) == "1.50s"
assert Utils.format_response_time(2_500_000) == "2.50s"
end
test "handles edge cases" do
assert Utils.format_response_time(0) == "0μs"
assert Utils.format_response_time(1000) == "1.00ms"
assert Utils.format_response_time(1_000_000) == "1.00s"
end
test "handles invalid input" do
assert Utils.format_response_time(-1) == "Invalid time"
assert Utils.format_response_time("not a number") == "Invalid time"
end
end
describe "Utils.valid_snmp_version?/1" do
test "validates numeric versions" do
assert Utils.valid_snmp_version?(0) == true
assert Utils.valid_snmp_version?(1) == true
assert Utils.valid_snmp_version?(2) == true
assert Utils.valid_snmp_version?(3) == true
end
test "validates atom versions" do
assert Utils.valid_snmp_version?(:v1) == true
assert Utils.valid_snmp_version?(:v2c) == true
assert Utils.valid_snmp_version?(:v3) == true
end
test "rejects invalid versions" do
assert Utils.valid_snmp_version?(4) == false
assert Utils.valid_snmp_version?(-1) == false
assert Utils.valid_snmp_version?(:v4) == false
assert Utils.valid_snmp_version?("1") == false
end
end
describe "Utils.valid_community_string?/1" do
test "validates good community strings" do
assert Utils.valid_community_string?("public") == true
assert Utils.valid_community_string?("private") == true
assert Utils.valid_community_string?("community123") == true
assert Utils.valid_community_string?("My-Community_String") == true
end
test "rejects invalid community strings" do
assert Utils.valid_community_string?("") == false
assert Utils.valid_community_string?(123) == false
assert Utils.valid_community_string?(nil) == false
end
test "rejects non-printable characters" do
# This test assumes non-printable characters would be rejected
# The exact behavior depends on String.printable?/1
binary_with_null = "test\0string"
assert Utils.valid_community_string?(binary_with_null) == false
end
end
describe "Utils.sanitize_community/1" do
test "sanitizes community strings" do
assert Utils.sanitize_community("public") == "******"
assert Utils.sanitize_community("private") == "*******"
assert Utils.sanitize_community("a") == "*"
end
test "handles empty community" do
assert Utils.sanitize_community("") == "<empty>"
end
test "handles invalid input" do
assert Utils.sanitize_community(123) == "<invalid>"
assert Utils.sanitize_community(nil) == "<invalid>"
end
end
describe "integration tests" do
test "pretty printing works with complex PDU" do
pdu = %{
type: :get_response,
request_id: 42,
error_status: 0,
error_index: 0,
varbinds: [
{[1, 3, 6, 1, 2, 1, 1, 1, 0], "Linux server 4.19.0"},
{[1, 3, 6, 1, 2, 1, 1, 3, 0], {:timeticks, 123_456}},
{[1, 3, 6, 1, 2, 1, 2, 1, 0], {:integer, 2}},
{[1, 3, 6, 1, 2, 1, 1, 6, 0], {:object_identifier, [1, 3, 6, 1, 4, 1, 8072, 3, 2, 10]}}
]
}
result = Utils.pretty_print_pdu(pdu)
# Should contain all the expected elements
assert String.contains?(result, "Response")
assert String.contains?(result, "Linux server")
assert String.contains?(result, "TimeTicks")
assert String.contains?(result, "INTEGER: 2")
assert String.contains?(result, "OID:")
end
test "timing utilities work with SNMP operations" do
# Simulate an SNMP operation
mock_snmp_operation = fn ->
# Simulate 5ms operation
:timer.sleep(5)
{:ok, "SNMP response"}
end
{result, time_us} = Utils.measure_request_time(mock_snmp_operation)
formatted_time = Utils.format_response_time(time_us)
assert result == {:ok, "SNMP response"}
# At least 5ms in microseconds
assert time_us >= 5000
assert formatted_time =~ ~r/ms|μs/
end
test "data formatting works with SNMP values" do
# Test formatting of various SNMP counter values
interface_octets = 1_234_567_890
# About 1.4 days
uptime_ticks = 12_345_600
formatted_bytes = Utils.format_bytes(interface_octets)
formatted_number = Utils.format_number(interface_octets)
formatted_timeticks = Utils.pretty_print_value({:timeticks, uptime_ticks})
assert formatted_bytes =~ ~r/GB|MB/
assert String.contains?(formatted_number, ",")
assert formatted_timeticks =~ ~r/d|h/
end
end
end