414 lines
11 KiB
Elixir
414 lines
11 KiB
Elixir
defmodule SnmpKit.SnmpLib.MIB.UtilitiesTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias SnmpKit.SnmpLib.MIB.Utilities
|
|
|
|
describe "register_oid/4" do
|
|
test "registers a new OID" do
|
|
oid_table = %{}
|
|
|
|
result = Utilities.register_oid("sysDescr", [1, 3, 6, 1, 2, 1, 1, 1], :resolved, oid_table)
|
|
|
|
assert Map.has_key?(result, "sysDescr")
|
|
entry = result["sysDescr"]
|
|
assert entry.name == "sysDescr"
|
|
assert entry.oid == [1, 3, 6, 1, 2, 1, 1, 1]
|
|
assert entry.status == :resolved
|
|
assert entry.parent == [1, 3, 6, 1, 2, 1, 1]
|
|
end
|
|
|
|
test "registers root OID without parent" do
|
|
oid_table = %{}
|
|
|
|
result = Utilities.register_oid("root", [1], :resolved, oid_table)
|
|
|
|
entry = result["root"]
|
|
assert entry.parent == nil
|
|
end
|
|
end
|
|
|
|
describe "resolve_oids/1" do
|
|
test "resolves OID tree successfully" do
|
|
oid_table = %{
|
|
"root" => %{
|
|
name: "root",
|
|
oid: [1],
|
|
status: :resolved,
|
|
parent: nil,
|
|
children: []
|
|
},
|
|
"child" => %{
|
|
name: "child",
|
|
oid: [%{name: "root", value: 3}],
|
|
status: :unresolved,
|
|
parent: nil,
|
|
children: []
|
|
}
|
|
}
|
|
|
|
# This tests OID resolution - should either succeed or return error
|
|
result = Utilities.resolve_oids(oid_table)
|
|
|
|
# Should return either ok or error
|
|
valid? = match?({:ok, _}, result) or match?({:error, _}, result)
|
|
assert valid?
|
|
end
|
|
|
|
test "handles already resolved OIDs" do
|
|
oid_table = %{
|
|
"sysDescr" => %{
|
|
name: "sysDescr",
|
|
oid: [1, 3, 6, 1, 2, 1, 1, 1],
|
|
status: :resolved,
|
|
parent: [1, 3, 6, 1, 2, 1, 1],
|
|
children: []
|
|
}
|
|
}
|
|
|
|
assert {:ok, result} = Utilities.resolve_oids(oid_table)
|
|
assert result == oid_table
|
|
end
|
|
end
|
|
|
|
describe "tr_oid/2" do
|
|
test "translates resolved OID name to numeric" do
|
|
oid_table = %{
|
|
"sysDescr" => %{
|
|
name: "sysDescr",
|
|
oid: [1, 3, 6, 1, 2, 1, 1, 1],
|
|
status: :resolved,
|
|
parent: nil,
|
|
children: []
|
|
}
|
|
}
|
|
|
|
assert {:ok, [1, 3, 6, 1, 2, 1, 1, 1]} = Utilities.tr_oid("sysDescr", oid_table)
|
|
end
|
|
|
|
test "returns error for unresolved OID" do
|
|
oid_table = %{
|
|
"test" => %{
|
|
name: "test",
|
|
oid: [1, 2, 3],
|
|
status: :unresolved,
|
|
parent: nil,
|
|
children: []
|
|
}
|
|
}
|
|
|
|
assert {:error, :unresolved} = Utilities.tr_oid("test", oid_table)
|
|
end
|
|
|
|
test "returns error for unknown OID" do
|
|
oid_table = %{}
|
|
|
|
assert {:error, :not_found} = Utilities.tr_oid("unknown", oid_table)
|
|
end
|
|
end
|
|
|
|
describe "update_me_oids/3" do
|
|
test "updates MIB entries with resolved OIDs" do
|
|
oid_table = %{
|
|
"system" => %{
|
|
name: "system",
|
|
oid: [1, 3, 6, 1, 2, 1, 1],
|
|
status: :resolved,
|
|
parent: nil,
|
|
children: []
|
|
}
|
|
}
|
|
|
|
entries = [
|
|
%{name: "sysDescr", oid: "system"},
|
|
%{name: "other", value: 123}
|
|
]
|
|
|
|
result = Utilities.update_me_oids(entries, oid_table, :silent)
|
|
|
|
# First entry should have resolved OID
|
|
first = Enum.at(result, 0)
|
|
assert first.oid == [1, 3, 6, 1, 2, 1, 1]
|
|
|
|
# Second entry has no oid field, should remain unchanged
|
|
second = Enum.at(result, 1)
|
|
assert second.value == 123
|
|
end
|
|
|
|
test "handles entries without OIDs" do
|
|
entries = [%{name: "test", value: 123}]
|
|
result = Utilities.update_me_oids(entries, %{}, :debug)
|
|
|
|
assert result == entries
|
|
end
|
|
end
|
|
|
|
describe "make_asn1_type/1" do
|
|
test "validates integer type" do
|
|
assert {:ok, type_def} = Utilities.make_asn1_type({:integer, []})
|
|
assert type_def.type == :integer
|
|
end
|
|
|
|
test "validates integer with range constraint" do
|
|
assert {:ok, type_def} = Utilities.make_asn1_type({:integer, [{:range, 0, 100}]})
|
|
assert type_def.type == :integer
|
|
assert type_def.constraints == [{:range, 0, 100}]
|
|
end
|
|
|
|
test "validates octet string type" do
|
|
assert {:ok, type_def} = Utilities.make_asn1_type({:octet_string, []})
|
|
assert type_def.type == :octet_string
|
|
end
|
|
|
|
test "validates octet string with size constraint" do
|
|
assert {:ok, type_def} = Utilities.make_asn1_type({:octet_string, [1, {:range, 0, 255}]})
|
|
assert type_def.type == :octet_string
|
|
end
|
|
|
|
test "validates object identifier type" do
|
|
assert {:ok, type_def} = Utilities.make_asn1_type({:object_identifier})
|
|
assert type_def.type == :object_identifier
|
|
end
|
|
|
|
test "validates named type" do
|
|
assert {:ok, type_def} = Utilities.make_asn1_type({:named_type, "DisplayString"})
|
|
assert type_def.type == :named_type
|
|
assert type_def.name == "DisplayString"
|
|
end
|
|
|
|
test "validates atom type" do
|
|
assert {:ok, type_def} = Utilities.make_asn1_type(:integer)
|
|
assert type_def.type == :integer
|
|
end
|
|
|
|
test "rejects invalid type" do
|
|
assert {:error, msg} = Utilities.make_asn1_type({:invalid_type, "bad"})
|
|
assert msg =~ "Invalid type definition"
|
|
end
|
|
|
|
test "rejects invalid integer constraint" do
|
|
assert {:error, msg} = Utilities.make_asn1_type({:integer, [{:range, 100, 0}]})
|
|
assert msg =~ "Invalid integer constraint"
|
|
end
|
|
|
|
test "rejects invalid size constraint" do
|
|
assert {:error, msg} = Utilities.make_asn1_type({:octet_string, [-1]})
|
|
assert msg =~ "Invalid size constraint"
|
|
end
|
|
end
|
|
|
|
describe "test_kibbles/2" do
|
|
test "validates unique bit names and values" do
|
|
bits = [
|
|
%{name: "bit1", value: 0},
|
|
%{name: "bit2", value: 1},
|
|
%{name: "bit3", value: 2}
|
|
]
|
|
|
|
assert :ok = Utilities.test_kibbles(bits, :debug)
|
|
end
|
|
|
|
test "rejects duplicate bit names" do
|
|
bits = [
|
|
%{name: "bit1", value: 0},
|
|
%{name: "bit1", value: 1}
|
|
]
|
|
|
|
assert {:error, msg} = Utilities.test_kibbles(bits, :warning)
|
|
assert msg =~ "Duplicate bit names"
|
|
end
|
|
|
|
test "rejects duplicate bit values" do
|
|
bits = [
|
|
%{name: "bit1", value: 0},
|
|
%{name: "bit2", value: 0}
|
|
]
|
|
|
|
assert {:error, msg} = Utilities.test_kibbles(bits, :warning)
|
|
assert msg =~ "Duplicate bit values"
|
|
end
|
|
end
|
|
|
|
describe "allow_size_rfc1902/1" do
|
|
test "allows size for octet_string" do
|
|
assert Utilities.allow_size_rfc1902(:octet_string)
|
|
end
|
|
|
|
test "disallows size for integer" do
|
|
refute Utilities.allow_size_rfc1902(:integer)
|
|
end
|
|
|
|
test "disallows size for object_identifier" do
|
|
refute Utilities.allow_size_rfc1902(:object_identifier)
|
|
end
|
|
end
|
|
|
|
describe "check_sub_ids/3" do
|
|
test "accepts valid sub-identifiers" do
|
|
assert :ok = Utilities.check_sub_ids([1, 3, 6, 1, 2, 1], 0, 255)
|
|
end
|
|
|
|
test "rejects sub-identifier below minimum" do
|
|
assert {:error, msg} = Utilities.check_sub_ids([1, 3, -1], 0, 255)
|
|
assert msg =~ "out of range"
|
|
end
|
|
|
|
test "rejects sub-identifier above maximum" do
|
|
assert {:error, msg} = Utilities.check_sub_ids([1, 3, 300], 0, 255)
|
|
assert msg =~ "out of range"
|
|
end
|
|
end
|
|
|
|
describe "print_error/2" do
|
|
test "prints error when not silent" do
|
|
assert :ok = Utilities.print_error("Test error", :warning)
|
|
end
|
|
|
|
test "suppresses error when silent" do
|
|
assert :ok = Utilities.print_error("Test error", :silent)
|
|
end
|
|
end
|
|
|
|
describe "print_error/3" do
|
|
test "prints formatted error when not silent" do
|
|
assert :ok = Utilities.print_error("Error: ~p", ["test"], :info)
|
|
end
|
|
|
|
test "suppresses formatted error when silent" do
|
|
assert :ok = Utilities.print_error("Error: ~p", ["test"], :silent)
|
|
end
|
|
end
|
|
|
|
describe "compilation_error/1" do
|
|
test "throws error with message" do
|
|
assert catch_throw(Utilities.compilation_error("Test error")) == {:error, "Test error"}
|
|
end
|
|
end
|
|
|
|
describe "compilation_error/2" do
|
|
test "throws formatted error" do
|
|
result = catch_throw(Utilities.compilation_error("Error: ~s", ["test"]))
|
|
assert {:error, msg} = result
|
|
assert msg =~ "Error: test"
|
|
end
|
|
end
|
|
|
|
describe "vprint/6" do
|
|
test "prints debug message when verbosity is high enough" do
|
|
assert :ok =
|
|
Utilities.vprint(:debug, :debug, "TestModule", "test_function", "Message: ~s", [
|
|
"test"
|
|
])
|
|
end
|
|
|
|
test "suppresses message when verbosity is too low" do
|
|
assert :ok =
|
|
Utilities.vprint(:silent, :debug, "TestModule", "test_function", "Message", [])
|
|
end
|
|
end
|
|
|
|
describe "printable?/2" do
|
|
test "returns true when current >= required" do
|
|
assert Utilities.printable?(:debug, :warning)
|
|
assert Utilities.printable?(:info, :warning)
|
|
assert Utilities.printable?(:warning, :warning)
|
|
end
|
|
|
|
test "returns false when current < required" do
|
|
refute Utilities.printable?(:warning, :debug)
|
|
refute Utilities.printable?(:silent, :info)
|
|
end
|
|
end
|
|
|
|
describe "vvalidate/1" do
|
|
test "validates valid verbosity levels" do
|
|
assert {:ok, :silent} = Utilities.vvalidate(:silent)
|
|
assert {:ok, :warning} = Utilities.vvalidate(:warning)
|
|
assert {:ok, :info} = Utilities.vvalidate(:info)
|
|
assert {:ok, :debug} = Utilities.vvalidate(:debug)
|
|
end
|
|
|
|
test "rejects invalid verbosity" do
|
|
assert {:error, msg} = Utilities.vvalidate(:invalid)
|
|
assert msg =~ "Invalid verbosity level"
|
|
end
|
|
|
|
test "rejects non-atom verbosity" do
|
|
assert {:error, msg} = Utilities.vvalidate("debug")
|
|
assert msg =~ "Invalid verbosity level"
|
|
end
|
|
end
|
|
|
|
describe "key1search/2" do
|
|
test "finds tuple by first element" do
|
|
list = [{:a, 1}, {:b, 2}, {:c, 3}]
|
|
|
|
assert {:value, {:b, 2}} = Utilities.key1search(:b, list)
|
|
end
|
|
|
|
test "returns false when not found" do
|
|
list = [{:a, 1}, {:b, 2}]
|
|
|
|
assert false == Utilities.key1search(:c, list)
|
|
end
|
|
end
|
|
|
|
describe "key1search/3" do
|
|
test "returns tuple when found" do
|
|
list = [{:a, 1}, {:b, 2}]
|
|
|
|
assert {:b, 2} = Utilities.key1search(:b, list, :default)
|
|
end
|
|
|
|
test "returns default when not found" do
|
|
list = [{:a, 1}]
|
|
|
|
assert :default = Utilities.key1search(:b, list, :default)
|
|
end
|
|
end
|
|
|
|
describe "set_dir/2" do
|
|
test "joins directory when filename has no path" do
|
|
result = Utilities.set_dir("test.mib", "/usr/share/mibs")
|
|
|
|
assert result == "/usr/share/mibs/test.mib"
|
|
end
|
|
|
|
test "preserves absolute path" do
|
|
result = Utilities.set_dir("/home/user/test.mib", "/usr/share/mibs")
|
|
|
|
assert result == "/home/user/test.mib"
|
|
end
|
|
|
|
test "preserves relative path with directory" do
|
|
result = Utilities.set_dir("mibs/test.mib", "/usr/share/mibs")
|
|
|
|
assert result == "mibs/test.mib"
|
|
end
|
|
end
|
|
|
|
describe "lookup/2" do
|
|
test "finds item by key in tuple list" do
|
|
list = [{:a, 1}, {:b, 2}]
|
|
|
|
assert {:ok, {:b, 2}} = Utilities.lookup(:b, list)
|
|
end
|
|
|
|
test "finds item by name in map list" do
|
|
list = [%{name: "test1", value: 1}, %{name: "test2", value: 2}]
|
|
|
|
assert {:ok, %{name: "test2", value: 2}} = Utilities.lookup("test2", list)
|
|
end
|
|
|
|
test "finds item by id in map list" do
|
|
list = [%{id: 1, data: "a"}, %{id: 2, data: "b"}]
|
|
|
|
assert {:ok, %{id: 2, data: "b"}} = Utilities.lookup(2, list)
|
|
end
|
|
|
|
test "returns error when not found" do
|
|
list = [{:a, 1}]
|
|
|
|
assert {:error, :not_found} = Utilities.lookup(:b, list)
|
|
end
|
|
end
|
|
end
|