698 lines
18 KiB
Elixir
698 lines
18 KiB
Elixir
defmodule SnmpKit.SnmpLib.MIB.ParserTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias SnmpKit.SnmpLib.MIB.Parser
|
|
|
|
describe "init_parser/0" do
|
|
@tag :yecc_required
|
|
test "returns ok tuple with module name" do
|
|
result = Parser.init_parser()
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "returns same module name on repeated calls" do
|
|
{:ok, module1} = Parser.init_parser()
|
|
{:ok, module2} = Parser.init_parser()
|
|
assert module1 == module2
|
|
end
|
|
end
|
|
|
|
describe "tokenize/1" do
|
|
test "tokenizes empty string" do
|
|
result = Parser.tokenize("")
|
|
assert {:ok, tokens} = result
|
|
refute tokens == []
|
|
end
|
|
|
|
test "tokenizes simple MIB header" do
|
|
mib = "TEST-MIB DEFINITIONS ::= BEGIN END"
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
refute Enum.empty?(tokens)
|
|
end
|
|
|
|
test "tokenizes reserved words" do
|
|
mib = "DEFINITIONS BEGIN END IMPORTS FROM OBJECT-TYPE"
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
assert length(tokens) >= 6
|
|
end
|
|
|
|
test "tokenizes identifiers" do
|
|
mib = "testObject myIdentifier"
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
assert length(tokens) >= 2
|
|
end
|
|
|
|
test "tokenizes numbers" do
|
|
mib = "123 456 789"
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
assert length(tokens) >= 3
|
|
end
|
|
|
|
test "tokenizes special characters" do
|
|
mib = "::= { } ( ) , ;"
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
assert length(tokens) > 5
|
|
end
|
|
|
|
test "tokenizes complete OBJECT-TYPE" do
|
|
mib = """
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION "Test"
|
|
::= { test 1 }
|
|
"""
|
|
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
assert length(tokens) > 10
|
|
end
|
|
|
|
test "handles multiline input" do
|
|
mib = """
|
|
TEST-MIB
|
|
DEFINITIONS
|
|
::=
|
|
BEGIN
|
|
END
|
|
"""
|
|
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
assert length(tokens) > 4
|
|
end
|
|
|
|
test "handles comments" do
|
|
mib = "test -- this is a comment\nOBJECT-TYPE"
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
assert length(tokens) >= 2
|
|
end
|
|
|
|
test "handles hex atoms (8+ characters)" do
|
|
mib = "12345678 abcdef01"
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
refute Enum.empty?(tokens)
|
|
end
|
|
|
|
test "handles short hex-like identifiers" do
|
|
# Short hex patterns like d1, d2 should remain as atoms
|
|
mib = "d1 d2 a1"
|
|
result = Parser.tokenize(mib)
|
|
assert {:ok, tokens} = result
|
|
refute Enum.empty?(tokens)
|
|
end
|
|
end
|
|
|
|
describe "parse/1" do
|
|
@tag :yecc_required
|
|
test "returns error for empty string" do
|
|
result = Parser.parse("")
|
|
assert {:error, _reason} = result
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "returns error for invalid MIB content" do
|
|
result = Parser.parse("invalid mib content")
|
|
assert {:error, _reason} = result
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "accepts minimal MIB header" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses MIB with IMPORTS" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
IMPORTS
|
|
MODULE-IDENTITY FROM SNMPv2-SMI;
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses MIB with OBJECT-TYPE" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION "Test object"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "returns structured error on parse failure" do
|
|
result = Parser.parse("BEGIN END")
|
|
assert {:error, _reason} = result
|
|
end
|
|
end
|
|
|
|
describe "parse_tokens/1" do
|
|
@tag :yecc_required
|
|
test "accepts token list" do
|
|
# Minimal token list representing "TEST-MIB DEFINITIONS ::= BEGIN END"
|
|
tokens = [
|
|
{:variable, 1, :TEST_MIB},
|
|
{:DEFINITIONS, 1},
|
|
{:"::=", 1},
|
|
{:BEGIN, 1},
|
|
{:END, 1},
|
|
{:"$end", 1}
|
|
]
|
|
|
|
result = Parser.parse_tokens(tokens)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "returns error for invalid token sequence" do
|
|
tokens = [{:BEGIN, 1}, {:END, 1}]
|
|
result = Parser.parse_tokens(tokens)
|
|
assert {:error, _reason} = result
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "accepts empty token list" do
|
|
tokens = []
|
|
result = Parser.parse_tokens(tokens)
|
|
assert {:error, _reason} = result
|
|
end
|
|
end
|
|
|
|
describe "mibdirs/1" do
|
|
@tag :yecc_required
|
|
test "accepts empty directory list" do
|
|
result = Parser.mibdirs([])
|
|
assert map_size(result) == 0
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "returns error for non-existent directory" do
|
|
result = Parser.mibdirs(["/nonexistent/path/to/mibs"])
|
|
assert Map.has_key?(result, "/nonexistent/path/to/mibs")
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "processes multiple directories" do
|
|
dirs = ["/tmp/mibs1", "/tmp/mibs2"]
|
|
result = Parser.mibdirs(dirs)
|
|
assert map_size(result) == 2
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "result contains directory structure" do
|
|
result = Parser.mibdirs(["/nonexistent"])
|
|
dir_result = result["/nonexistent"]
|
|
assert Map.has_key?(dir_result, :directory)
|
|
assert Map.has_key?(dir_result, :total)
|
|
assert Map.has_key?(dir_result, :success)
|
|
assert Map.has_key?(dir_result, :failures)
|
|
end
|
|
end
|
|
|
|
describe "tokenize integration" do
|
|
test "converts long hex atoms to integers" do
|
|
# Tokenize content with long hex pattern (8+ characters)
|
|
mib = "12345678"
|
|
{:ok, tokens} = Parser.tokenize(mib)
|
|
|
|
# Should convert long hex atom to integer token
|
|
assert Enum.any?(tokens, fn
|
|
{:integer, _, _} -> true
|
|
_ -> false
|
|
end)
|
|
end
|
|
|
|
test "preserves short hex-like identifiers as atoms" do
|
|
# Short patterns should remain as atoms
|
|
mib = "d1"
|
|
{:ok, tokens} = Parser.tokenize(mib)
|
|
|
|
# Should NOT convert short patterns to integers
|
|
assert Enum.any?(tokens, fn
|
|
{:atom, _, _} -> true
|
|
_ -> false
|
|
end)
|
|
end
|
|
|
|
test "handles mixed hex and regular identifiers" do
|
|
mib = "test 12345678 identifier"
|
|
{:ok, tokens} = Parser.tokenize(mib)
|
|
assert length(tokens) >= 3
|
|
end
|
|
end
|
|
|
|
describe "error handling" do
|
|
@tag :yecc_required
|
|
test "parse handles malformed syntax gracefully" do
|
|
malformed = "OBJECT-TYPE ::= {{{ test"
|
|
result = Parser.parse(malformed)
|
|
assert {:error, _reason} = result
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parse handles incomplete MIB" do
|
|
incomplete = "TEST-MIB DEFINITIONS ::= BEGIN"
|
|
result = Parser.parse(incomplete)
|
|
assert {:error, _reason} = result
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parse handles missing keywords" do
|
|
missing_keyword = "TEST-MIB ::= BEGIN END"
|
|
result = Parser.parse(missing_keyword)
|
|
assert {:error, _reason} = result
|
|
end
|
|
|
|
test "tokenize handles special characters" do
|
|
special = "@#$%^&*"
|
|
result = Parser.tokenize(special)
|
|
# Should either succeed with tokens or return error
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "MIB structure parsing" do
|
|
@tag :yecc_required
|
|
test "parses MODULE-IDENTITY" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testMIB MODULE-IDENTITY
|
|
LAST-UPDATED "202501010000Z"
|
|
ORGANIZATION "Test Org"
|
|
CONTACT-INFO "test@example.com"
|
|
DESCRIPTION "Test MIB"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses TEXTUAL-CONVENTION" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
TestTC ::= TEXTUAL-CONVENTION
|
|
STATUS current
|
|
DESCRIPTION "Test textual convention"
|
|
SYNTAX Integer32
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses OBJECT-GROUP" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testGroup OBJECT-GROUP
|
|
OBJECTS { testObject1, testObject2 }
|
|
STATUS current
|
|
DESCRIPTION "Test object group"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses EXPORTS clause" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
EXPORTS testObject, testGroup;
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "complex MIB parsing" do
|
|
@tag :yecc_required
|
|
test "parses MIB with multiple IMPORTS" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
IMPORTS
|
|
MODULE-IDENTITY, OBJECT-TYPE, Integer32
|
|
FROM SNMPv2-SMI
|
|
TEXTUAL-CONVENTION FROM SNMPv2-TC;
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses MIB with SEQUENCE OF" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
TestTable ::= SEQUENCE OF TestEntry
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses OBJECT-TYPE with INDEX" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testEntry OBJECT-TYPE
|
|
SYNTAX TestEntry
|
|
MAX-ACCESS not-accessible
|
|
STATUS current
|
|
DESCRIPTION "Test entry"
|
|
INDEX { testIndex }
|
|
::= { testTable 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses OBJECT-TYPE with SIZE constraint" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX OCTET STRING (SIZE (0..255))
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION "Test with size"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses OBJECT-TYPE with range constraint" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32 (0..100)
|
|
MAX-ACCESS read-write
|
|
STATUS current
|
|
DESCRIPTION "Test with range"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "description cleaning" do
|
|
@tag :yecc_required
|
|
test "parses OBJECT-TYPE with multiline description" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION "This is a
|
|
multiline
|
|
description"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses OBJECT-TYPE with very long description" do
|
|
long_desc = String.duplicate("This is a very long description. ", 100)
|
|
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION "#{long_desc}"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses OBJECT-TYPE with empty description" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION ""
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "edge cases" do
|
|
@tag :yecc_required
|
|
test "handles very large MIB file" do
|
|
# Generate large MIB with many OBJECT-TYPE definitions
|
|
objects =
|
|
Enum.map(1..100, fn i ->
|
|
"""
|
|
testObject#{i} OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION "Test object #{i}"
|
|
::= { test #{i} }
|
|
"""
|
|
end)
|
|
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
#{Enum.join(objects, "\n")}
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
test "tokenize handles very long identifier" do
|
|
long_id = String.duplicate("a", 200)
|
|
result = Parser.tokenize(long_id)
|
|
assert {:ok, tokens} = result
|
|
refute Enum.empty?(tokens)
|
|
end
|
|
|
|
test "tokenize handles many tokens" do
|
|
identifiers = Enum.map_join(1..500, " ", fn i -> "id#{i}" end)
|
|
result = Parser.tokenize(identifiers)
|
|
assert {:ok, tokens} = result
|
|
refute Enum.empty?(tokens)
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses MIB with deeply nested OIDs" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION "Test"
|
|
::= { iso org dod internet private enterprises test 1 2 3 4 5 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
describe "real-world MIB patterns" do
|
|
@tag :yecc_required
|
|
test "parses SNMPv2-style MIB" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
IMPORTS
|
|
MODULE-IDENTITY, OBJECT-TYPE, Integer32
|
|
FROM SNMPv2-SMI
|
|
MODULE-COMPLIANCE, OBJECT-GROUP
|
|
FROM SNMPv2-CONF;
|
|
|
|
testMIB MODULE-IDENTITY
|
|
LAST-UPDATED "202501010000Z"
|
|
ORGANIZATION "Test Organization"
|
|
CONTACT-INFO "admin@test.com"
|
|
DESCRIPTION "Test MIB for unit testing"
|
|
REVISION "202501010000Z"
|
|
DESCRIPTION "Initial version"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses MIB with table definition" do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testTable OBJECT-TYPE
|
|
SYNTAX SEQUENCE OF TestEntry
|
|
MAX-ACCESS not-accessible
|
|
STATUS current
|
|
DESCRIPTION "Test table"
|
|
::= { test 1 }
|
|
|
|
testEntry OBJECT-TYPE
|
|
SYNTAX TestEntry
|
|
MAX-ACCESS not-accessible
|
|
STATUS current
|
|
DESCRIPTION "Test entry"
|
|
INDEX { testIndex }
|
|
::= { testTable 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses MIB with various access levels" do
|
|
access_levels = ["read-only", "read-write", "read-create", "not-accessible", "accessible-for-notify"]
|
|
|
|
for access <- access_levels do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS #{access}
|
|
STATUS current
|
|
DESCRIPTION "Test"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses MIB with various status values" do
|
|
statuses = ["current", "deprecated", "obsolete"]
|
|
|
|
for status <- statuses do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX Integer32
|
|
MAX-ACCESS read-only
|
|
STATUS #{status}
|
|
DESCRIPTION "Test"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parses MIB with various syntax types" do
|
|
types = [
|
|
"Integer32",
|
|
"OCTET STRING",
|
|
"OBJECT IDENTIFIER",
|
|
"Counter32",
|
|
"Counter64",
|
|
"Gauge32",
|
|
"TimeTicks",
|
|
"IpAddress"
|
|
]
|
|
|
|
for type <- types do
|
|
mib = """
|
|
TEST-MIB DEFINITIONS ::= BEGIN
|
|
testObject OBJECT-TYPE
|
|
SYNTAX #{type}
|
|
MAX-ACCESS read-only
|
|
STATUS current
|
|
DESCRIPTION "Test"
|
|
::= { test 1 }
|
|
END
|
|
"""
|
|
|
|
result = Parser.parse(mib)
|
|
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
|
|
end
|
|
end
|
|
|
|
@tag :yecc_required
|
|
test "parser has known bug with index 10 (newline character)" do
|
|
mib_path = Path.join(:code.priv_dir(:towerops), "mibs/ubnt/UBNT-MIB")
|
|
{:ok, content} = File.read(mib_path)
|
|
{:ok, parsed} = Parser.parse(content)
|
|
|
|
definitions = Map.get(parsed, :definitions, [])
|
|
|
|
# ubntAFLTU is defined as { ubntMIB 10 } in the MIB file
|
|
# But the parser returns sub_index: nil because 10 is the newline character
|
|
ubnt_afltu = Enum.find(definitions, &(Map.get(&1, :name) == "ubntAFLTU"))
|
|
assert %{name: "ubntAFLTU"} = ubnt_afltu
|
|
assert Map.get(ubnt_afltu, :parent) == "ubntMIB"
|
|
assert Map.get(ubnt_afltu, :sub_index) == nil, "Known parser bug: index 10 not extracted"
|
|
end
|
|
end
|
|
end
|