towerops/test/snmpkit/snmp_lib/mib/ast_test.exs
mayor 76d70dd3f4
Add comprehensive test coverage for SNMPkit modules
Added test suites for previously untested SNMPkit modules:

- ErrorHandler: Circuit breaker, retry logic, error classification (88.70% coverage)
- MIB.AST: AST node creation, validation, pretty printing (100% coverage)
- MIB.Preprocessor: MIB preprocessing, enumeration simplification (48.28% coverage)
- MIB.Utilities: OID resolution, type validation, error handling (85.60% coverage)
- PDU.V3Encoder: SNMPv3 message encoding/decoding (36.90% coverage)

All 1090 tests passing with no failures.
2026-02-05 17:03:41 -06:00

294 lines
7.7 KiB
Elixir

defmodule SnmpKit.SnmpLib.MIB.ASTTest do
use ExUnit.Case, async: true
alias SnmpKit.SnmpLib.MIB.AST
describe "new_mib/2" do
test "creates basic MIB node" do
mib = AST.new_mib("TEST-MIB")
assert mib.__type__ == :mib
assert mib.name == "TEST-MIB"
assert mib.imports == []
assert mib.definitions == []
assert mib.oid_tree == %{}
end
test "creates MIB node with options" do
mib =
AST.new_mib("TEST-MIB",
last_updated: "202501010000Z",
organization: "Test Org",
contact_info: "test@example.com",
description: "Test MIB",
revision_history: [
%{
__type__: :revision,
date: "202501010000Z",
description: "Initial version",
line: 1
}
]
)
assert mib.last_updated == "202501010000Z"
assert mib.organization == "Test Org"
assert mib.contact_info == "test@example.com"
assert mib.description == "Test MIB"
assert length(mib.revision_history) == 1
end
end
describe "new_object_type/2" do
test "creates object type definition" do
obj =
AST.new_object_type("sysDescr",
syntax: :octet_string,
max_access: :read_only,
status: :current,
description: "System description",
oid: [1, 3, 6, 1, 2, 1, 1, 1],
line: 10
)
assert obj.__type__ == :object_type
assert obj.name == "sysDescr"
assert obj.syntax == :octet_string
assert obj.max_access == :read_only
assert obj.status == :current
assert obj.description == "System description"
assert obj.oid == [1, 3, 6, 1, 2, 1, 1, 1]
assert obj.line == 10
end
test "creates object type with optional fields" do
obj =
AST.new_object_type("ifDescr",
syntax: {:octet_string, [{:size, 0..255}]},
units: "bytes",
max_access: :read_only,
status: :current,
description: "Interface description",
reference: "RFC 2863",
index: {:index, ["ifIndex"]},
oid: [1, 3, 6, 1, 2, 1, 2, 2, 1, 2],
line: 20
)
assert obj.units == "bytes"
assert obj.reference == "RFC 2863"
assert obj.index == {:index, ["ifIndex"]}
end
end
describe "new_object_identity/2" do
test "creates object identity definition" do
obj =
AST.new_object_identity("system",
status: :current,
description: "System group",
oid: [1, 3, 6, 1, 2, 1, 1],
line: 5
)
assert obj.__type__ == :object_identity
assert obj.name == "system"
assert obj.status == :current
assert obj.description == "System group"
assert obj.oid == [1, 3, 6, 1, 2, 1, 1]
end
test "creates object identity with reference" do
obj =
AST.new_object_identity("interfaces",
status: :current,
description: "Interfaces group",
reference: "RFC 2863",
oid: [1, 3, 6, 1, 2, 1, 2],
line: 15
)
assert obj.reference == "RFC 2863"
end
end
describe "new_import/3" do
test "creates import statement" do
import_stmt = AST.new_import(["Counter32", "Gauge32"], "SNMPv2-SMI", 3)
assert import_stmt.__type__ == :import
assert import_stmt.symbols == ["Counter32", "Gauge32"]
assert import_stmt.from_module == "SNMPv2-SMI"
assert import_stmt.line == 3
end
test "creates empty import" do
import_stmt = AST.new_import([], "SNMPv2-TC", 1)
assert import_stmt.symbols == []
end
end
describe "determine_snmp_version/1" do
test "identifies SNMPv2 with MODULE-IDENTITY" do
definitions = [
%{
__type__: :module_identity,
name: "testMIB",
last_updated: "202501010000Z",
organization: "Test",
contact_info: "test@test.com",
description: "Test MIB",
revision_history: [],
oid: [1, 3, 6, 1, 4, 1, 999],
line: 1
}
]
assert :v2c = AST.determine_snmp_version(definitions)
end
test "identifies SNMPv1 without MODULE-IDENTITY" do
definitions = [
%{
__type__: :object_type,
name: "sysDescr",
syntax: :octet_string,
max_access: :read_only,
status: :mandatory,
description: "Test",
oid: [1, 3, 6, 1, 2, 1, 1, 1],
line: 10
}
]
assert :v1 = AST.determine_snmp_version(definitions)
end
test "returns v1 for empty definitions" do
assert :v1 = AST.determine_snmp_version([])
end
end
describe "build_oid_tree/1" do
test "builds OID tree from definitions" do
definitions = [
%{
__type__: :object_type,
name: "sysDescr",
syntax: :octet_string,
max_access: :read_only,
status: :current,
description: "Test",
oid: [1, 3, 6, 1, 2, 1, 1, 1],
line: 1
},
%{
__type__: :object_identity,
name: "system",
status: :current,
description: "System group",
oid: [1, 3, 6, 1, 2, 1, 1],
line: 2
}
]
tid = AST.build_oid_tree(definitions)
# Verify ETS table was created
assert :ets.info(tid) != :undefined
# Verify OID mappings
assert [{[1, 3, 6, 1, 2, 1, 1, 1], "sysDescr"}] =
:ets.lookup(tid, [1, 3, 6, 1, 2, 1, 1, 1])
assert [{"sysDescr", [1, 3, 6, 1, 2, 1, 1, 1]}] = :ets.lookup(tid, "sysDescr")
# Clean up
:ets.delete(tid)
end
test "handles definitions without OIDs" do
definitions = [
%{
__type__: :textual_convention,
name: "DisplayString",
status: :current,
description: "Display string",
syntax: :octet_string,
line: 1
}
]
tid = AST.build_oid_tree(definitions)
# Should create table but with no entries
assert :ets.info(tid, :size) == 0
:ets.delete(tid)
end
end
describe "validate_node/1" do
test "validates mib node" do
node = %{__type__: :mib, name: "TEST-MIB"}
assert {:ok, ^node} = AST.validate_node(node)
end
test "validates object_type node" do
node = %{__type__: :object_type, name: "sysDescr"}
assert {:ok, ^node} = AST.validate_node(node)
end
test "validates all node types" do
valid_types = [
:mib,
:object_type,
:object_identity,
:module_identity,
:object_group,
:notification_type,
:notification_group,
:module_compliance,
:agent_capabilities,
:textual_convention,
:trap_type,
:object_identifier_assignment,
:import,
:revision
]
for type <- valid_types do
node = %{__type__: type, name: "test"}
assert {:ok, ^node} = AST.validate_node(node)
end
end
test "rejects invalid node" do
assert {:error, msg} = AST.validate_node(%{invalid: true})
assert msg =~ "Invalid AST node"
end
test "rejects node with invalid type" do
assert {:error, msg} = AST.validate_node(%{__type__: :invalid_type})
assert msg =~ "Invalid AST node"
end
end
describe "pretty_print/1" do
test "prints node with type and name" do
node = %{__type__: :object_type, name: "sysDescr"}
assert "object_type: sysDescr" = AST.pretty_print(node)
end
test "prints node with type only" do
node = %{__type__: :mib}
assert "mib" = AST.pretty_print(node)
end
test "prints other values with inspect" do
assert "123" = AST.pretty_print(123)
assert ~s("test") = AST.pretty_print("test")
end
end
end