towerops/test/snmpkit/snmp_lib/pdu/encoder_test.exs
2026-06-15 11:15:46 -05:00

1563 lines
40 KiB
Elixir

defmodule SnmpKit.SnmpLib.PDU.EncoderTest do
use ExUnit.Case, async: true
alias SnmpKit.SnmpLib.PDU.Encoder
describe "encode_message/1" do
test "encodes SNMPv1 message" do
message = %{
version: 0,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
# Should start with SEQUENCE tag
assert <<0x30, _::binary>> = encoded
end
test "encodes SNMPv2c message" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 100,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 5, 0], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "delegates SNMPv3 messages to V3Encoder" do
message = %{
version: 3,
msgID: 1,
msgMaxSize: 65_507,
msgFlags: <<0x04>>,
msgSecurityModel: 3,
msgSecurityParameters: "",
contextEngineID: "",
contextName: "",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
# Should delegate to V3Encoder (may fail if V3Encoder requires specific fields)
result = Encoder.encode_message(message)
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
end
test "returns error for invalid message format" do
assert {:error, :invalid_message_format} = Encoder.encode_message(%{invalid: :format})
assert {:error, :invalid_message_format} = Encoder.encode_message(%{version: 1})
assert {:error, :invalid_message_format} = Encoder.encode_message(%{})
end
test "encodes message with multiple varbinds" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [
{[1, 3, 6, 1, 2, 1, 1, 1, 0], :null, nil},
{[1, 3, 6, 1, 2, 1, 1, 3, 0], :null, nil},
{[1, 3, 6, 1, 2, 1, 1, 5, 0], :null, nil}
]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes message with empty community string" do
message = %{
version: 0,
community: "",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "encode_message/2" do
test "delegates SNMPv3 messages with user to V3Encoder" do
message = %{
version: 3,
msgID: 1,
msgMaxSize: 65_507,
msgFlags: <<0x04>>,
msgSecurityModel: 3,
msgSecurityParameters: "",
contextEngineID: "",
contextName: "",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
user = %{
auth_protocol: :md5,
auth_key: "authentication_key",
priv_protocol: :des,
priv_key: "privacy_key"
}
# Should delegate to V3Encoder (may fail if V3Encoder requires specific fields)
result = Encoder.encode_message(message, user)
assert is_tuple(result) and elem(result, 0) in [:ok, :error]
end
test "falls back to regular encoding for v1/v2c with user parameter" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
user = %{auth_protocol: :md5}
assert {:ok, encoded} = Encoder.encode_message(message, user)
assert byte_size(encoded) > 0
end
test "ignores user parameter for non-v3 messages" do
message = %{
version: 0,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
assert {:ok, encoded1} = Encoder.encode_message(message, nil)
assert {:ok, encoded2} = Encoder.encode_message(message, %{some: "user"})
assert encoded1 == encoded2
end
end
describe "encode_pdu/1" do
test "encodes get_request PDU" do
pdu = %{
type: :get_request,
request_id: 123,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :null, nil}]
}
assert {:ok, encoded} = Encoder.encode_pdu(pdu)
assert byte_size(encoded) > 0
# Should start with get_request tag (0xA0)
assert <<0xA0, _::binary>> = encoded
end
test "encodes get_next_request PDU" do
pdu = %{
type: :get_next_request,
request_id: 456,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6], :null, nil}]
}
assert {:ok, encoded} = Encoder.encode_pdu(pdu)
assert byte_size(encoded) > 0
# Should start with getnext_request tag (0xA1)
assert <<0xA1, _::binary>> = encoded
end
test "encodes get_response PDU" do
pdu = %{
type: :get_response,
request_id: 789,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :octet_string, "System description"}]
}
assert {:ok, encoded} = Encoder.encode_pdu(pdu)
assert byte_size(encoded) > 0
# Should start with get_response tag (0xA2)
assert <<0xA2, _::binary>> = encoded
end
test "encodes set_request PDU" do
pdu = %{
type: :set_request,
request_id: 111,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 5, 0], :octet_string, "NewName"}]
}
assert {:ok, encoded} = Encoder.encode_pdu(pdu)
assert byte_size(encoded) > 0
# Should start with set_request tag (0xA3)
assert <<0xA3, _::binary>> = encoded
end
test "encodes get_bulk_request PDU" do
pdu = %{
type: :get_bulk_request,
request_id: 999,
non_repeaters: 0,
max_repetitions: 10,
varbinds: [{[1, 3, 6, 1, 2, 1, 2, 2], :null, nil}]
}
assert {:ok, encoded} = Encoder.encode_pdu(pdu)
assert byte_size(encoded) > 0
# Should start with getbulk_request tag (0xA5)
assert <<0xA5, _::binary>> = encoded
end
test "returns error for unsupported PDU type" do
pdu = %{
type: :unsupported_type,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
assert {:error, :unsupported_pdu_type} = Encoder.encode_pdu(pdu)
end
test "encodes PDU with empty varbinds" do
pdu = %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
assert {:ok, encoded} = Encoder.encode_pdu(pdu)
assert byte_size(encoded) > 0
end
test "encodes PDU with multiple varbinds" do
pdu = %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [
{[1, 3, 6, 1, 2, 1, 1, 1, 0], :null, nil},
{[1, 3, 6, 1, 2, 1, 1, 3, 0], :null, nil},
{[1, 3, 6, 1, 2, 1, 1, 5, 0], :null, nil}
]
}
assert {:ok, encoded} = Encoder.encode_pdu(pdu)
assert byte_size(encoded) > 0
end
test "encodes PDU with large request_id" do
pdu = %{
type: :get_request,
request_id: 2_147_483_647,
error_status: 0,
error_index: 0,
varbinds: []
}
assert {:ok, encoded} = Encoder.encode_pdu(pdu)
assert byte_size(encoded) > 0
end
end
describe "encode/1 and encode_snmp_packet/1" do
test "encode/1 is an alias for encode_message/1" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
assert {:ok, encoded1} = Encoder.encode(message)
assert {:ok, encoded2} = Encoder.encode_message(message)
assert encoded1 == encoded2
end
test "encode_snmp_packet/1 is an alias for encode/1" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
assert {:ok, encoded1} = Encoder.encode_snmp_packet(message)
assert {:ok, encoded2} = Encoder.encode(message)
assert encoded1 == encoded2
end
end
describe "varbind encoding" do
test "encodes varbind with explicit null type" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with integer value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 7, 0], :integer, 72}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with octet_string value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :octet_string, "System description"}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with counter32 value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 2, 1, 10, 1], :counter32, 1_234_567}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with gauge32 value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 2, 1, 5, 1], :gauge32, 1_000_000}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with timeticks value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 3, 0], :timeticks, 12_345_678}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with counter64 value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 6, 1], :counter64, 9_876_543_210}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with ip_address value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 4, 20, 1, 1, 1], :ip_address, <<192, 168, 1, 1>>}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with opaque value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 4, 1, 9, 2, 1, 57, 0], :opaque, <<0x9F, 0x78, 0x04, 0x41, 0x70, 0x00, 0x00>>}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with object_identifier value (list)" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 2, 0], :object_identifier, [1, 3, 6, 1, 4, 1]}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with object_identifier value (string)" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 2, 0], :object_identifier, "1.3.6.1.4.1"}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "auto type varbind encoding" do
test "encodes varbind with auto type and integer value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 7, 0], :auto, 72}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with auto type and string value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 5, 0], :auto, "router1"}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with auto type and OID list" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 2, 0], :auto, [1, 3, 6, 1, 4, 1, 9]}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with auto type and OID string" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 2, 0], :auto, "1.3.6.1.4.1.9"}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with auto type and null value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :auto, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with auto type and :null atom" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :auto, :null}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with auto type and typed tuple" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [
{[1, 3, 6, 1, 2, 1, 2, 1, 10, 1], :auto, {:counter32, 1_234_567}},
{[1, 3, 6, 1, 2, 1, 2, 1, 5, 1], :auto, {:gauge32, 1_000_000}},
{[1, 3, 6, 1, 2, 1, 1, 3, 0], :auto, {:timeticks, 12_345}},
{[1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 6, 1], :auto, {:counter64, 9_876_543_210}}
]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind with auto type and exception values" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [
{[1, 3, 6, 1, 2, 1, 99, 1], :auto, {:no_such_object, nil}},
{[1, 3, 6, 1, 2, 1, 99, 2], :auto, {:no_such_instance, nil}},
{[1, 3, 6, 1, 2, 1, 99, 3], :auto, {:end_of_mib_view, nil}}
]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "exception value encoding" do
test "encodes no_such_object exception" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 99, 1], :no_such_object, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes no_such_instance exception" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 99, 2], :no_such_instance, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes end_of_mib_view exception" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 99, 3], :end_of_mib_view, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "integer encoding edge cases" do
test "encodes zero integer" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 0,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :integer, 0}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes small positive integer (<128)" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :integer, 42}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes large positive integer" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :integer, 2_147_483_647}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes negative integer" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :integer, -100}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes large negative integer" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :integer, -2_147_483_648}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "counter32/gauge32/timeticks encoding" do
test "encodes counter32 min value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :counter32, 0}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes counter32 max value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :counter32, 4_294_967_295}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes gauge32 min value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :gauge32, 0}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes gauge32 max value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :gauge32, 4_294_967_295}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes timeticks min value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 3, 0], :timeticks, 0}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes timeticks max value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 3, 0], :timeticks, 4_294_967_295}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "counter64 encoding" do
test "encodes counter64 min value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 6, 1], :counter64, 0}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes counter64 large value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 6, 1], :counter64, 9_999_999_999_999}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes counter64 max value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 31, 1, 1, 1, 6, 1], :counter64, 18_446_744_073_709_551_615}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "string encoding" do
test "encodes empty string" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :octet_string, ""}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes short string" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :octet_string, "test"}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes long string" do
long_string = String.duplicate("A", 256)
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :octet_string, long_string}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes string with special characters" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :octet_string, "Test\nString\twith\rspecial"}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes binary string" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :octet_string, <<0x00, 0x01, 0x02, 0xFF>>}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "OID encoding" do
test "encodes simple OID" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes OID with large subidentifiers" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 2, 2, 1, 1, 1000], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes OID with very large subidentifiers" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 4, 1, 9, 9, 1, 1, 1, 1, 1_000_000], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes long OID" do
long_oid = [1, 3, 6, 1, 2, 1, 2, 2, 1, 1] ++ Enum.to_list(1..50)
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{long_oid, :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes minimum OID (two components)" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[0, 0], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes OID starting with 2" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[2, 5, 29, 17], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "bulk request encoding" do
test "encodes bulk request with non_repeaters" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_bulk_request,
request_id: 1,
non_repeaters: 1,
max_repetitions: 10,
varbinds: [
{[1, 3, 6, 1, 2, 1, 1, 1, 0], :null, nil},
{[1, 3, 6, 1, 2, 1, 2, 2, 1, 10], :null, nil}
]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes bulk request with zero non_repeaters" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_bulk_request,
request_id: 1,
non_repeaters: 0,
max_repetitions: 25,
varbinds: [{[1, 3, 6, 1, 2, 1, 2, 2, 1, 10], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes bulk request with large max_repetitions" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_bulk_request,
request_id: 1,
non_repeaters: 0,
max_repetitions: 1000,
varbinds: [{[1, 3, 6, 1, 2, 1, 2, 2], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "error handling" do
test "handles encoding errors gracefully" do
# Invalid varbind format should be caught
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: ["invalid_varbind"]
}
}
assert {:error, _reason} = Encoder.encode_message(message)
end
test "handles missing PDU fields" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request
# Missing required fields
}
}
assert {:error, _reason} = Encoder.encode_message(message)
end
end
describe "PDU with error status" do
test "encodes PDU with non-zero error_status" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 2,
error_index: 1,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], :null, nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes PDU with various error statuses" do
for error_status <- 0..5 do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: error_status,
error_index: 0,
varbinds: []
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
end
describe "community string variations" do
test "encodes message with short community" do
message = %{
version: 1,
community: "p",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes message with long community" do
long_community = String.duplicate("community", 20)
message = %{
version: 1,
community: long_community,
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes message with special characters in community" do
message = %{
version: 1,
community: "test@community#123",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: []
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "varbind format variations" do
test "encodes varbind as two-tuple (implicit auto type)" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], nil}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes varbind as two-tuple with value" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1, 2, 1, 1, 1, 0], "Test"}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "length encoding edge cases" do
test "encodes value with length < 128 (short form)" do
# Short string (< 128 bytes)
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :octet_string, "Short"}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes value with length 128-255 (long form 1 byte)" do
# String of 150 bytes
string = String.duplicate("A", 150)
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :octet_string, string}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes value with length 256-65535 (long form 2 bytes)" do
# String of 300 bytes
string = String.duplicate("B", 300)
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 1,
error_status: 0,
error_index: 0,
varbinds: [{[1, 3, 6, 1], :octet_string, string}]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
describe "comprehensive integration tests" do
test "encodes complete SNMP GET request" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_request,
request_id: 12_345,
error_status: 0,
error_index: 0,
varbinds: [
{[1, 3, 6, 1, 2, 1, 1, 1, 0], :null, nil},
{[1, 3, 6, 1, 2, 1, 1, 3, 0], :null, nil},
{[1, 3, 6, 1, 2, 1, 1, 5, 0], :null, nil}
]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
assert byte_size(encoded) > 0
end
test "encodes complete SNMP GET response with multiple types" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_response,
request_id: 12_345,
error_status: 0,
error_index: 0,
varbinds: [
{[1, 3, 6, 1, 2, 1, 1, 1, 0], :octet_string, "Linux router 4.19.0"},
{[1, 3, 6, 1, 2, 1, 1, 3, 0], :timeticks, 123_456_789},
{[1, 3, 6, 1, 2, 1, 1, 5, 0], :octet_string, "router1.example.com"},
{[1, 3, 6, 1, 2, 1, 1, 7, 0], :integer, 72},
{[1, 3, 6, 1, 2, 1, 2, 1, 10, 1], :counter32, 987_654_321}
]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
assert byte_size(encoded) > 0
end
test "encodes SNMP SET request" do
message = %{
version: 1,
community: "private",
pdu: %{
type: :set_request,
request_id: 9999,
error_status: 0,
error_index: 0,
varbinds: [
{[1, 3, 6, 1, 2, 1, 1, 5, 0], :octet_string, "NewHostName"},
{[1, 3, 6, 1, 2, 1, 1, 6, 0], :octet_string, "Data Center A"}
]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes SNMP GETNEXT request" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_next_request,
request_id: 5555,
error_status: 0,
error_index: 0,
varbinds: [
{[1, 3, 6, 1, 2, 1, 2, 2, 1, 1], :null, nil}
]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
test "encodes SNMP GETBULK request" do
message = %{
version: 1,
community: "public",
pdu: %{
type: :get_bulk_request,
request_id: 7777,
non_repeaters: 1,
max_repetitions: 10,
varbinds: [
{[1, 3, 6, 1, 2, 1, 1, 1, 0], :null, nil},
{[1, 3, 6, 1, 2, 1, 2, 2, 1, 2], :null, nil}
]
}
}
assert {:ok, encoded} = Encoder.encode_message(message)
assert byte_size(encoded) > 0
end
end
end