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.
294 lines
7.7 KiB
Elixir
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
|