606 lines
20 KiB
Elixir
606 lines
20 KiB
Elixir
defmodule SnmpKit.SnmpMgr.MIBTest do
|
|
use ExUnit.Case, async: false
|
|
|
|
alias SnmpKit.SnmpLib.MIB.Parser
|
|
alias SnmpKit.SnmpMgr.MIB
|
|
|
|
doctest MIB
|
|
|
|
setup do
|
|
# Start the MIB GenServer if not already running
|
|
case GenServer.whereis(MIB) do
|
|
nil ->
|
|
{:ok, pid} = MIB.start_link()
|
|
on_exit(fn -> if Process.alive?(pid), do: GenServer.stop(pid) end)
|
|
{:ok, mib_pid: pid}
|
|
|
|
pid ->
|
|
{:ok, mib_pid: pid}
|
|
end
|
|
end
|
|
|
|
# Test helper to parse a MIB and extract name->OID map
|
|
defp parse_and_extract(mib_path) do
|
|
{:ok, content} = File.read(mib_path)
|
|
{:ok, parsed} = Parser.parse(content)
|
|
definitions = Map.get(parsed, :definitions, [])
|
|
|
|
# Extract OID definitions (simulating build_oid_definition_map)
|
|
oid_defs =
|
|
definitions
|
|
|> Enum.filter(fn def ->
|
|
type = Map.get(def, :__type__)
|
|
type in [:object_type, :object_identifier, :module_identity]
|
|
end)
|
|
|> Enum.reduce(%{}, fn def, acc ->
|
|
name = Map.get(def, :name)
|
|
oid = Map.get(def, :oid)
|
|
parent = Map.get(def, :parent)
|
|
sub_index = Map.get(def, :sub_index)
|
|
|
|
cond do
|
|
name && oid -> Map.put(acc, name, oid)
|
|
name && parent && sub_index -> Map.put(acc, name, {String.to_atom(parent), sub_index})
|
|
name && parent -> Map.put(acc, name, {String.to_atom(parent), nil})
|
|
true -> acc
|
|
end
|
|
end)
|
|
|
|
# Apply workaround for parser bug with index 10
|
|
fix_missing_indices(oid_defs, content)
|
|
end
|
|
|
|
# Workaround for parser bug - extract indices from raw MIB content
|
|
defp fix_missing_indices(oid_defs, mib_content) do
|
|
missing =
|
|
Enum.filter(oid_defs, fn {_, oid_def} ->
|
|
case oid_def do
|
|
{_, nil} -> true
|
|
_ -> false
|
|
end
|
|
end)
|
|
|
|
Enum.reduce(missing, oid_defs, fn {name, _}, acc ->
|
|
regex = ~r/#{name}\s+OBJECT\s+IDENTIFIER\s+::=\s+\{\s+(\w+)\s+(\d+)\s+\}/
|
|
|
|
case Regex.run(regex, mib_content) do
|
|
[_, parent, index_str] ->
|
|
index = String.to_integer(index_str)
|
|
parent_atom = String.to_atom(parent)
|
|
Map.put(acc, name, {parent_atom, <<index>>})
|
|
|
|
_ ->
|
|
acc
|
|
end
|
|
end)
|
|
end
|
|
|
|
# Test helper to resolve OID tuples
|
|
defp resolve_oid_def({parent_name, index_binary}, resolved_map) when is_atom(parent_name) do
|
|
parent_str = Atom.to_string(parent_name)
|
|
|
|
case Map.get(resolved_map, parent_str) do
|
|
parent_oid when is_list(parent_oid) ->
|
|
# Decode index - MIB parser uses UTF-8 character for numeric values
|
|
index =
|
|
case String.to_charlist(index_binary) do
|
|
[code_point | _] -> code_point
|
|
[] -> :binary.decode_unsigned(index_binary)
|
|
end
|
|
|
|
{:ok, parent_oid ++ [index]}
|
|
|
|
_ ->
|
|
{:error, :parent_not_resolved}
|
|
end
|
|
end
|
|
|
|
defp resolve_oid_def(oid_list, _resolved_map) when is_list(oid_list) do
|
|
if Enum.all?(oid_list, &is_integer/1) do
|
|
{:ok, oid_list}
|
|
else
|
|
{:error, :not_resolved}
|
|
end
|
|
end
|
|
|
|
defp resolve_oid_def(_, _), do: {:error, :invalid_format}
|
|
|
|
describe "MIB parsing and OID resolution" do
|
|
@tag :yecc_required
|
|
test "workaround fixes missing sub_index for ubntAFLTU" do
|
|
mib_path = Path.join(:code.priv_dir(:towerops), "mibs/ubnt/UBNT-MIB")
|
|
oid_defs = parse_and_extract(mib_path)
|
|
|
|
# ubntAFLTU should have index 10 after workaround
|
|
assert {:ubntMIB, <<10>>} = oid_defs["ubntAFLTU"],
|
|
"ubntAFLTU should have been fixed to {:ubntMIB, <<10>>} by workaround"
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses UBNT-MIB and extracts OID definitions" do
|
|
mib_path = Path.join(:code.priv_dir(:towerops), "mibs/ubnt/UBNT-MIB")
|
|
oid_defs = parse_and_extract(mib_path)
|
|
|
|
# Should have extracted OID definitions
|
|
assert map_size(oid_defs) > 0
|
|
|
|
# Check specific definitions
|
|
assert Map.has_key?(oid_defs, "ubnt")
|
|
assert Map.has_key?(oid_defs, "ubntMIB")
|
|
assert Map.has_key?(oid_defs, "ubntAFLTU")
|
|
|
|
# ubnt should reference enterprises with index 41112
|
|
assert {:enterprises, _} = oid_defs["ubnt"]
|
|
|
|
# ubntMIB should reference ubnt with an index
|
|
assert {:ubnt, _} = oid_defs["ubntMIB"]
|
|
|
|
# ubntAFLTU should reference ubntMIB with index 10 (fixed by workaround)
|
|
assert {:ubntMIB, <<10>>} = oid_defs["ubntAFLTU"]
|
|
end
|
|
|
|
test "resolves OID with parent reference" do
|
|
# Simulate having enterprises already resolved
|
|
resolved_map = %{"enterprises" => [1, 3, 6, 1, 4, 1]}
|
|
|
|
# ubnt = enterprises.41112 (where 41112 is encoded as UTF-8 char "ꂘ")
|
|
ubnt_def = {:enterprises, "ꂘ"}
|
|
assert {:ok, [1, 3, 6, 1, 4, 1, 41_112]} = resolve_oid_def(ubnt_def, resolved_map)
|
|
end
|
|
|
|
test "resolves OID chain iteratively" do
|
|
# Start with standard OIDs
|
|
resolved = %{
|
|
"enterprises" => [1, 3, 6, 1, 4, 1],
|
|
"mib-2" => [1, 3, 6, 1, 2, 1]
|
|
}
|
|
|
|
# First iteration: resolve ubnt
|
|
ubnt_def = {:enterprises, "ꂘ"}
|
|
{:ok, ubnt_oid} = resolve_oid_def(ubnt_def, resolved)
|
|
resolved = Map.put(resolved, "ubnt", ubnt_oid)
|
|
|
|
assert resolved["ubnt"] == [1, 3, 6, 1, 4, 1, 41_112]
|
|
|
|
# Second iteration: resolve ubntMIB (ubnt.1)
|
|
ubnt_mib_def = {:ubnt, <<1>>}
|
|
{:ok, ubnt_mib_oid} = resolve_oid_def(ubnt_mib_def, resolved)
|
|
resolved = Map.put(resolved, "ubntMIB", ubnt_mib_oid)
|
|
|
|
assert resolved["ubntMIB"] == [1, 3, 6, 1, 4, 1, 41_112, 1]
|
|
|
|
# Third iteration: resolve ubntAFLTU (ubntMIB.10)
|
|
ubnt_afltu_def = {:ubntMIB, <<10>>}
|
|
{:ok, ubnt_afltu_oid} = resolve_oid_def(ubnt_afltu_def, resolved)
|
|
resolved = Map.put(resolved, "ubntAFLTU", ubnt_afltu_oid)
|
|
|
|
assert resolved["ubntAFLTU"] == [1, 3, 6, 1, 4, 1, 41_112, 1, 10]
|
|
end
|
|
end
|
|
|
|
describe "MIB GenServer basic operations" do
|
|
test "GenServer starts successfully" do
|
|
assert Process.alive?(Process.whereis(MIB))
|
|
end
|
|
|
|
test "load_standard_mibs returns :ok" do
|
|
assert :ok = MIB.load_standard_mibs()
|
|
end
|
|
end
|
|
|
|
describe "resolve/1" do
|
|
test "resolves standard MIB names" do
|
|
assert {:ok, [1, 3, 6, 1, 2, 1, 1, 1]} = MIB.resolve("sysDescr")
|
|
assert {:ok, [1, 3, 6, 1, 2, 1, 1, 5]} = MIB.resolve("sysName")
|
|
assert {:ok, [1, 3, 6, 1, 2, 1, 2, 2, 1, 2]} = MIB.resolve("ifDescr")
|
|
end
|
|
|
|
test "resolves names with instance suffix" do
|
|
assert {:ok, [1, 3, 6, 1, 2, 1, 1, 1, 0]} = MIB.resolve("sysDescr.0")
|
|
assert {:ok, [1, 3, 6, 1, 2, 1, 2, 2, 1, 2, 1]} = MIB.resolve("ifDescr.1")
|
|
assert {:ok, [1, 3, 6, 1, 2, 1, 2, 2, 1, 2, 1, 2]} = MIB.resolve("ifDescr.1.2")
|
|
end
|
|
|
|
test "returns error for unknown names" do
|
|
assert {:error, :not_found} = MIB.resolve("unknownMibName")
|
|
end
|
|
|
|
test "returns error for invalid input" do
|
|
assert {:error, :invalid_name} = MIB.resolve(nil)
|
|
assert {:error, :invalid_name} = MIB.resolve(123)
|
|
end
|
|
|
|
test "returns error for names with invalid instance parts" do
|
|
assert {:error, :invalid_instance} = MIB.resolve("sysDescr.abc")
|
|
end
|
|
end
|
|
|
|
describe "reverse_lookup/1" do
|
|
test "reverses standard OIDs to names" do
|
|
assert {:ok, "sysDescr"} = MIB.reverse_lookup([1, 3, 6, 1, 2, 1, 1, 1])
|
|
assert {:ok, "sysName"} = MIB.reverse_lookup([1, 3, 6, 1, 2, 1, 1, 5])
|
|
end
|
|
|
|
test "reverses OIDs with instance suffix" do
|
|
assert {:ok, "sysDescr.0"} = MIB.reverse_lookup([1, 3, 6, 1, 2, 1, 1, 1, 0])
|
|
assert {:ok, "ifDescr.1"} = MIB.reverse_lookup([1, 3, 6, 1, 2, 1, 2, 2, 1, 2, 1])
|
|
end
|
|
|
|
test "accepts OID as string" do
|
|
assert {:ok, "sysDescr"} = MIB.reverse_lookup("1.3.6.1.2.1.1.1")
|
|
assert {:ok, "sysDescr.0"} = MIB.reverse_lookup("1.3.6.1.2.1.1.1.0")
|
|
end
|
|
|
|
test "returns error for unknown OIDs" do
|
|
assert {:error, :not_found} = MIB.reverse_lookup([9, 9, 9, 9, 9])
|
|
end
|
|
|
|
test "returns error for empty OID" do
|
|
assert {:error, :empty_oid} = MIB.reverse_lookup([])
|
|
end
|
|
|
|
test "returns error for invalid OID format" do
|
|
assert {:error, :invalid_oid_string} = MIB.reverse_lookup("not.a.valid.oid")
|
|
end
|
|
|
|
test "returns error for binary OID (edge case)" do
|
|
# This tests the guard in find_partial_reverse_match
|
|
assert {:error, :invalid_oid_string} = MIB.reverse_lookup("binary_string")
|
|
end
|
|
end
|
|
|
|
describe "parent/1" do
|
|
test "returns parent OID" do
|
|
assert {:ok, [1, 3, 6, 1, 2, 1, 1]} = MIB.parent([1, 3, 6, 1, 2, 1, 1, 1])
|
|
assert {:ok, [1, 3, 6]} = MIB.parent([1, 3, 6, 1])
|
|
end
|
|
|
|
test "returns empty list for single element" do
|
|
assert {:ok, []} = MIB.parent([1])
|
|
end
|
|
|
|
test "returns error for empty list" do
|
|
assert {:error, :no_parent} = MIB.parent([])
|
|
end
|
|
|
|
test "accepts OID as string" do
|
|
assert {:ok, [1, 3, 6, 1, 2, 1, 1]} = MIB.parent("1.3.6.1.2.1.1.1")
|
|
end
|
|
|
|
test "returns error for invalid string OID" do
|
|
assert {:error, :invalid_oid_string} = MIB.parent("invalid")
|
|
end
|
|
end
|
|
|
|
describe "children/1" do
|
|
test "finds direct children of an OID" do
|
|
# system (1.3.6.1.2.1.1) should have children like sysDescr, sysObjectID, etc.
|
|
{:ok, children} = MIB.children([1, 3, 6, 1, 2, 1, 1])
|
|
assert is_list(children) and children != []
|
|
assert "sysDescr" in children
|
|
assert "sysName" in children
|
|
end
|
|
|
|
test "accepts OID as string" do
|
|
{:ok, children} = MIB.children("1.3.6.1.2.1.1")
|
|
assert is_list(children) and children != []
|
|
assert "sysDescr" in children
|
|
end
|
|
|
|
test "accepts nil as root (returns all top-level entries)" do
|
|
{:ok, children} = MIB.children(nil)
|
|
assert is_list(children) and children != []
|
|
end
|
|
|
|
test "returns empty list for leaf nodes" do
|
|
# sysDescr is a leaf node
|
|
{:ok, children} = MIB.children([1, 3, 6, 1, 2, 1, 1, 1])
|
|
assert children == []
|
|
end
|
|
|
|
test "returns error for invalid OID format" do
|
|
assert {:error, :invalid_parent_oid} = MIB.children("invalid")
|
|
assert {:error, :invalid_parent_oid} = MIB.children(123)
|
|
end
|
|
end
|
|
|
|
describe "walk_tree/2" do
|
|
test "walks tree from root OID" do
|
|
{:ok, descendants} = MIB.walk_tree([1, 3, 6, 1, 2, 1, 1])
|
|
assert is_list(descendants) and descendants != []
|
|
refute Enum.empty?(descendants)
|
|
# Should return tuples of {name, oid}
|
|
assert Enum.all?(descendants, fn {name, oid} -> is_binary(name) and is_list(oid) end)
|
|
end
|
|
|
|
test "accepts OID as string" do
|
|
{:ok, descendants} = MIB.walk_tree("1.3.6.1.2.1.1")
|
|
assert is_list(descendants) and descendants != []
|
|
refute Enum.empty?(descendants)
|
|
end
|
|
|
|
test "accepts nil as root" do
|
|
{:ok, descendants} = MIB.walk_tree(nil)
|
|
assert is_list(descendants) and descendants != []
|
|
# Should return all known OIDs
|
|
refute Enum.empty?(descendants)
|
|
end
|
|
|
|
test "returns empty list for unknown OID" do
|
|
{:ok, descendants} = MIB.walk_tree([9, 9, 9, 9])
|
|
assert descendants == []
|
|
end
|
|
end
|
|
|
|
describe "object_info/1" do
|
|
test "returns enriched metadata for standard objects" do
|
|
{:ok, info} = MIB.object_info("sysDescr")
|
|
assert info.name == "sysDescr"
|
|
assert info.oid == [1, 3, 6, 1, 2, 1, 1, 1]
|
|
assert info.module == "SNMPv2-MIB"
|
|
assert is_map(info.syntax) and map_size(info.syntax) > 0
|
|
assert info.syntax.base == :octet_string
|
|
assert info.syntax.textual_convention == "DisplayString"
|
|
end
|
|
|
|
test "includes instance information when present" do
|
|
{:ok, info} = MIB.object_info("sysDescr.0")
|
|
assert info.name == "sysDescr"
|
|
assert info.instance_index == 0
|
|
assert info.instance_oid == [1, 3, 6, 1, 2, 1, 1, 1, 0]
|
|
end
|
|
|
|
test "handles multi-part instance indices" do
|
|
{:ok, info} = MIB.object_info("ifDescr.1.2")
|
|
assert info.name == "ifDescr"
|
|
assert info.instance_index == [1, 2]
|
|
assert info.instance_oid == [1, 3, 6, 1, 2, 1, 2, 2, 1, 2, 1, 2]
|
|
end
|
|
|
|
test "accepts OID as list" do
|
|
{:ok, info} = MIB.object_info([1, 3, 6, 1, 2, 1, 1, 1])
|
|
assert info.name == "sysDescr"
|
|
end
|
|
|
|
test "accepts OID as dotted string" do
|
|
{:ok, info} = MIB.object_info("1.3.6.1.2.1.1.1")
|
|
assert info.name == "sysDescr"
|
|
end
|
|
|
|
test "returns error for invalid input" do
|
|
assert {:error, _} = MIB.object_info(123)
|
|
assert {:error, _} = MIB.object_info(%{})
|
|
end
|
|
end
|
|
|
|
describe "reverse_lookup_enriched/1" do
|
|
test "is an alias for object_info/1" do
|
|
{:ok, info1} = MIB.object_info("sysDescr")
|
|
{:ok, info2} = MIB.reverse_lookup_enriched("sysDescr")
|
|
assert info1 == info2
|
|
end
|
|
end
|
|
|
|
describe "object_info_many/1" do
|
|
test "returns list of info for multiple names" do
|
|
{:ok, infos} = MIB.object_info_many(["sysDescr", "sysName", "sysLocation"])
|
|
assert length(infos) == 3
|
|
assert Enum.all?(infos, &is_map/1)
|
|
assert Enum.map(infos, & &1.name) == ["sysDescr", "sysName", "sysLocation"]
|
|
end
|
|
|
|
test "returns error if any lookup fails" do
|
|
assert {:error, _} = MIB.object_info_many(["sysDescr", "invalidName"])
|
|
end
|
|
end
|
|
|
|
describe "resolve_enhanced/2" do
|
|
test "resolves names to object info" do
|
|
{:ok, info} = MIB.resolve_enhanced("sysDescr")
|
|
assert is_map(info) and map_size(info) > 0
|
|
assert info.name == "sysDescr"
|
|
assert info.oid == [1, 3, 6, 1, 2, 1, 1, 1]
|
|
end
|
|
end
|
|
|
|
describe "compile/2" do
|
|
test "returns error for non-existent file" do
|
|
assert {:error, _} = MIB.compile("nonexistent.mib")
|
|
end
|
|
|
|
test "handles snmp_lib_not_available gracefully" do
|
|
# The function should return an error, not crash
|
|
result = MIB.compile("test.mib")
|
|
assert match?({:error, _}, result)
|
|
end
|
|
end
|
|
|
|
describe "compile_dir/2" do
|
|
test "returns error for non-existent directory" do
|
|
assert {:error, {:directory_error, :enoent}} = MIB.compile_dir("/nonexistent/path")
|
|
end
|
|
|
|
test "processes existing directory" do
|
|
# Create a temporary directory
|
|
tmp_dir = Path.join(System.tmp_dir!(), "mib_test_#{:rand.uniform(10_000)}")
|
|
File.mkdir_p!(tmp_dir)
|
|
|
|
try do
|
|
# Should return ok with empty results since no .mib files
|
|
result = MIB.compile_dir(tmp_dir)
|
|
assert elem(result, 0) in [:ok, :error]
|
|
after
|
|
File.rm_rf!(tmp_dir)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "parse_mib_file/2" do
|
|
test "returns error for non-existent file" do
|
|
assert {:error, {:file_read_error, :enoent}} = MIB.parse_mib_file("nonexistent.mib")
|
|
end
|
|
end
|
|
|
|
describe "parse_mib_content/2" do
|
|
test "parses basic MIB content" do
|
|
content = """
|
|
TestMIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION "Test object"
|
|
::= { testGroup 1 }
|
|
END
|
|
"""
|
|
|
|
{:ok, result} = MIB.parse_mib_content(content)
|
|
assert is_map(result) and map_size(result) > 0
|
|
assert Map.has_key?(result, :tokens)
|
|
assert Map.has_key?(result, :parsed_objects)
|
|
end
|
|
|
|
test "returns error for invalid MIB content" do
|
|
# Completely invalid content that can't be tokenized
|
|
result = MIB.parse_mib_content("not valid mib at all!!!")
|
|
assert match?({:error, {:tokenization_failed, _}}, result)
|
|
end
|
|
end
|
|
|
|
describe "load/1" do
|
|
test "handles non-existent compiled MIB path" do
|
|
# Should return error, not crash
|
|
result = MIB.load("/nonexistent/compiled.mib")
|
|
valid_result = match?(:ok, result) or match?({:error, _}, result)
|
|
assert valid_result == true
|
|
end
|
|
end
|
|
|
|
describe "load_and_integrate_mib/2" do
|
|
test "returns error when compilation fails" do
|
|
result = MIB.load_and_integrate_mib("nonexistent.mib")
|
|
assert match?({:error, _}, result)
|
|
end
|
|
end
|
|
|
|
describe "syntax parsing" do
|
|
test "handles various syntax types in object_info" do
|
|
# Test different syntax types through object_info
|
|
{:ok, sys_descr_info} = MIB.object_info("sysDescr")
|
|
assert sys_descr_info.syntax.base == :octet_string
|
|
assert sys_descr_info.syntax.textual_convention == "DisplayString"
|
|
|
|
{:ok, if_speed_info} = MIB.object_info("ifSpeed")
|
|
assert if_speed_info.syntax.base == :gauge32
|
|
end
|
|
|
|
test "handles objects without curated syntax metadata" do
|
|
# Enterprise OIDs don't have curated syntax metadata
|
|
{:ok, enterprises_info} = MIB.object_info("enterprises")
|
|
# Should still return syntax map even if empty
|
|
assert %{} = enterprises_info.syntax
|
|
end
|
|
end
|
|
|
|
describe "MIB integration with parsed content" do
|
|
test "integrates MIB data with complex OID definitions" do
|
|
# Simulate parsed MIB data with parent references
|
|
mib_content = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testRoot OBJECT IDENTIFIER ::= { enterprises 99999 }
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
::= { testRoot 1 }
|
|
END
|
|
"""
|
|
|
|
# This will exercise the parse_mib_content path
|
|
{:ok, result} = MIB.parse_mib_content(mib_content)
|
|
assert is_map(result) and map_size(result) > 0
|
|
assert Map.has_key?(result, :tokens)
|
|
end
|
|
end
|
|
|
|
describe "error handling edge cases" do
|
|
test "compile handles SnmpLib.MIB compilation errors gracefully" do
|
|
# Should return error tuple, not crash
|
|
result = MIB.compile("invalid_mib_file.mib")
|
|
assert match?({:error, _}, result)
|
|
end
|
|
|
|
test "compile_dir handles fallback when SnmpLib is not available" do
|
|
# Create a temp directory with a .mib file to test fallback
|
|
tmp_dir = Path.join(System.tmp_dir!(), "mib_fallback_test_#{:rand.uniform(10_000)}")
|
|
File.mkdir_p!(tmp_dir)
|
|
|
|
try do
|
|
# Create a dummy .mib file
|
|
test_mib = Path.join(tmp_dir, "test.mib")
|
|
File.write!(test_mib, "TEST-MIB DEFINITIONS ::= BEGIN END")
|
|
|
|
# This should trigger compile_dir_fallback
|
|
result = MIB.compile_dir(tmp_dir)
|
|
assert elem(result, 0) in [:ok, :error]
|
|
after
|
|
File.rm_rf!(tmp_dir)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "private helper coverage" do
|
|
test "object_info handles dotted OID strings" do
|
|
# This exercises numeric_oid_string? and OID parsing through object_info
|
|
{:ok, info} = MIB.object_info("1.3.6.1.2.1.1.1")
|
|
assert info.name == "sysDescr"
|
|
assert info.oid == [1, 3, 6, 1, 2, 1, 1, 1]
|
|
end
|
|
|
|
test "object_info handles leading dot in OID string" do
|
|
{:ok, info} = MIB.object_info(".1.3.6.1.2.1.1.1")
|
|
assert info.name == "sysDescr"
|
|
assert info.oid == [1, 3, 6, 1, 2, 1, 1, 1]
|
|
end
|
|
|
|
test "object_info handles complex instance indices" do
|
|
# Multi-part instance index
|
|
{:ok, info} = MIB.object_info([1, 3, 6, 1, 2, 1, 2, 2, 1, 2, 1, 0, 0])
|
|
assert info.name == "ifDescr"
|
|
assert info.instance_index == [1, 0, 0]
|
|
end
|
|
|
|
test "children returns sorted results" do
|
|
{:ok, children} = MIB.children([1, 3, 6, 1, 2, 1, 1])
|
|
# Should be sorted alphabetically
|
|
assert children == Enum.sort(children)
|
|
end
|
|
|
|
test "walk_tree returns sorted results by OID" do
|
|
{:ok, descendants} = MIB.walk_tree([1, 3, 6, 1, 2, 1, 1])
|
|
# Extract OIDs and verify they're sorted
|
|
oids = Enum.map(descendants, fn {_name, oid} -> oid end)
|
|
assert oids == Enum.sort(oids)
|
|
end
|
|
end
|
|
|
|
describe "module_for helper" do
|
|
test "identifies correct modules for known prefixes" do
|
|
{:ok, info} = MIB.object_info("ifHCInOctets")
|
|
assert info.module == "IF-MIB"
|
|
|
|
{:ok, info2} = MIB.object_info("sysDescr")
|
|
assert info2.module == "SNMPv2-MIB"
|
|
|
|
{:ok, info3} = MIB.object_info("ifDescr")
|
|
assert info3.module == "IF-MIB"
|
|
end
|
|
|
|
test "handles objects without module mapping" do
|
|
{:ok, info} = MIB.object_info("enterprises")
|
|
# May not have a module mapping, should handle gracefully
|
|
has_valid_module = is_nil(info.module) or is_binary(info.module)
|
|
assert has_valid_module == true
|
|
end
|
|
end
|
|
end
|