defmodule SnmpKit.SnmpLib.PDU.ConstantsTest do use ExUnit.Case, async: true alias SnmpKit.SnmpLib.PDU.Constants describe "error status code accessors" do test "returns correct error codes" do assert Constants.no_error() == 0 assert Constants.too_big() == 1 assert Constants.no_such_name() == 2 assert Constants.bad_value() == 3 assert Constants.read_only() == 4 assert Constants.gen_err() == 5 end end describe "SNMPv3 constants" do test "returns USM security model constant" do assert Constants.usm_security_model() == 3 end test "returns default max message size" do assert Constants.default_max_message_size() == 65_507 end end describe "PDU type constants accessors" do test "returns correct PDU type tags" do assert Constants.get_request() == 0xA0 assert Constants.getnext_request() == 0xA1 assert Constants.get_response() == 0xA2 assert Constants.set_request() == 0xA3 assert Constants.getbulk_request() == 0xA5 end end describe "data type constants accessors" do test "returns correct data type tags" do assert Constants.integer() == 0x02 assert Constants.octet_string() == 0x04 assert Constants.null() == 0x05 assert Constants.object_identifier() == 0x06 assert Constants.counter32() == 0x41 assert Constants.gauge32() == 0x42 assert Constants.timeticks() == 0x43 assert Constants.counter64() == 0x46 assert Constants.ip_address() == 0x40 assert Constants.opaque_type() == 0x44 assert Constants.no_such_object() == 0x80 assert Constants.no_such_instance() == 0x81 assert Constants.end_of_mib_view() == 0x82 end end describe "normalize_version/1" do test "normalizes SNMP version atoms to integers" do assert Constants.normalize_version(:v1) == 0 assert Constants.normalize_version(:v2c) == 1 assert Constants.normalize_version(:v2) == 1 assert Constants.normalize_version(:v3) == 3 end test "passes through integer versions" do assert Constants.normalize_version(0) == 0 assert Constants.normalize_version(1) == 1 assert Constants.normalize_version(3) == 3 end test "returns 0 for invalid versions" do assert Constants.normalize_version(:invalid) == 0 assert Constants.normalize_version("v1") == 0 assert Constants.normalize_version(nil) == 0 end end describe "normalize_oid/1" do test "passes through list OIDs unchanged" do oid = [1, 3, 6, 1, 2, 1, 1, 1, 0] assert Constants.normalize_oid(oid) == oid end test "converts string OIDs to list format" do assert Constants.normalize_oid("1.3.6.1.2.1.1.1.0") == [1, 3, 6, 1, 2, 1, 1, 1, 0] assert Constants.normalize_oid("1.3.6.1") == [1, 3, 6, 1] assert Constants.normalize_oid("0.0") == [0, 0] end test "returns default OID for invalid string OIDs" do assert Constants.normalize_oid("invalid.oid") == [1, 3, 6, 1] assert Constants.normalize_oid("1.2.abc.4") == [1, 3, 6, 1] assert Constants.normalize_oid("1.-2.3") == [1, 3, 6, 1] end test "returns default OID for non-list, non-string types" do assert Constants.normalize_oid(nil) == [1, 3, 6, 1] assert Constants.normalize_oid(123) == [1, 3, 6, 1] assert Constants.normalize_oid(%{}) == [1, 3, 6, 1] end end describe "pdu_type_to_tag/1" do test "converts PDU type atoms to tag values" do assert Constants.pdu_type_to_tag(:get_request) == 0xA0 assert Constants.pdu_type_to_tag(:get_next_request) == 0xA1 assert Constants.pdu_type_to_tag(:get_response) == 0xA2 assert Constants.pdu_type_to_tag(:set_request) == 0xA3 assert Constants.pdu_type_to_tag(:get_bulk_request) == 0xA5 end end describe "tag_to_pdu_type/1" do test "converts tag values to PDU type atoms" do assert Constants.tag_to_pdu_type(0xA0) == :get_request assert Constants.tag_to_pdu_type(0xA1) == :get_next_request assert Constants.tag_to_pdu_type(0xA2) == :get_response assert Constants.tag_to_pdu_type(0xA3) == :set_request assert Constants.tag_to_pdu_type(0xA5) == :get_bulk_request end test "returns nil for invalid tags" do assert Constants.tag_to_pdu_type(0xFF) == nil assert Constants.tag_to_pdu_type(0x00) == nil end end describe "data_type_to_tag/1" do test "converts data type atoms to tag values" do assert Constants.data_type_to_tag(:integer) == 0x02 assert Constants.data_type_to_tag(:octet_string) == 0x04 assert Constants.data_type_to_tag(:null) == 0x05 assert Constants.data_type_to_tag(:object_identifier) == 0x06 assert Constants.data_type_to_tag(:counter32) == 0x41 assert Constants.data_type_to_tag(:gauge32) == 0x42 assert Constants.data_type_to_tag(:timeticks) == 0x43 assert Constants.data_type_to_tag(:counter64) == 0x46 assert Constants.data_type_to_tag(:ip_address) == 0x40 assert Constants.data_type_to_tag(:opaque) == 0x44 assert Constants.data_type_to_tag(:no_such_object) == 0x80 assert Constants.data_type_to_tag(:no_such_instance) == 0x81 assert Constants.data_type_to_tag(:end_of_mib_view) == 0x82 end end describe "tag_to_data_type/1" do test "converts tag values to data type atoms" do assert Constants.tag_to_data_type(0x02) == :integer assert Constants.tag_to_data_type(0x04) == :octet_string assert Constants.tag_to_data_type(0x05) == :null assert Constants.tag_to_data_type(0x06) == :object_identifier assert Constants.tag_to_data_type(0x41) == :counter32 assert Constants.tag_to_data_type(0x42) == :gauge32 assert Constants.tag_to_data_type(0x43) == :timeticks assert Constants.tag_to_data_type(0x46) == :counter64 assert Constants.tag_to_data_type(0x40) == :ip_address assert Constants.tag_to_data_type(0x44) == :opaque assert Constants.tag_to_data_type(0x80) == :no_such_object assert Constants.tag_to_data_type(0x81) == :no_such_instance assert Constants.tag_to_data_type(0x82) == :end_of_mib_view end test "returns nil for invalid tags" do assert Constants.tag_to_data_type(0xFF) == nil assert Constants.tag_to_data_type(0x00) == nil end end describe "normalize_type/1" do test "normalizes string type to octet_string" do assert Constants.normalize_type(:string) == :octet_string end test "passes through other types unchanged" do assert Constants.normalize_type(:integer) == :integer assert Constants.normalize_type(:counter32) == :counter32 assert Constants.normalize_type(:gauge32) == :gauge32 assert Constants.normalize_type(:null) == :null end end describe "error_status_to_code/1" do test "converts error status atoms to numeric codes" do assert Constants.error_status_to_code(:no_error) == 0 assert Constants.error_status_to_code(:too_big) == 1 assert Constants.error_status_to_code(:no_such_name) == 2 assert Constants.error_status_to_code(:bad_value) == 3 assert Constants.error_status_to_code(:read_only) == 4 assert Constants.error_status_to_code(:gen_err) == 5 end test "passes through integer codes unchanged" do assert Constants.error_status_to_code(0) == 0 assert Constants.error_status_to_code(1) == 1 assert Constants.error_status_to_code(5) == 5 assert Constants.error_status_to_code(99) == 99 end end describe "error_status_to_atom/1" do test "converts error status codes to atoms" do assert Constants.error_status_to_atom(0) == :no_error assert Constants.error_status_to_atom(1) == :too_big assert Constants.error_status_to_atom(2) == :no_such_name assert Constants.error_status_to_atom(3) == :bad_value assert Constants.error_status_to_atom(4) == :read_only assert Constants.error_status_to_atom(5) == :gen_err end test "passes through unknown codes unchanged" do assert Constants.error_status_to_atom(99) == 99 assert Constants.error_status_to_atom(100) == 100 end end describe "encode_msg_flags/1" do test "encodes message flags with no security" do flags = %{auth: false, priv: false, reportable: false} assert Constants.encode_msg_flags(flags) == <<0>> end test "encodes message flags with auth only" do flags = %{auth: true, priv: false, reportable: false} assert Constants.encode_msg_flags(flags) == <<0x01>> end test "encodes message flags with priv only" do flags = %{auth: false, priv: true, reportable: false} assert Constants.encode_msg_flags(flags) == <<0x02>> end test "encodes message flags with reportable only" do flags = %{auth: false, priv: false, reportable: true} assert Constants.encode_msg_flags(flags) == <<0x04>> end test "encodes message flags with auth and priv" do flags = %{auth: true, priv: true, reportable: false} assert Constants.encode_msg_flags(flags) == <<0x03>> end test "encodes message flags with all flags set" do flags = %{auth: true, priv: true, reportable: true} assert Constants.encode_msg_flags(flags) == <<0x07>> end end describe "decode_msg_flags/1" do test "decodes message flags with no security" do assert Constants.decode_msg_flags(<<0>>) == %{ auth: false, priv: false, reportable: false } end test "decodes message flags with auth only" do assert Constants.decode_msg_flags(<<0x01>>) == %{ auth: true, priv: false, reportable: false } end test "decodes message flags with priv only" do assert Constants.decode_msg_flags(<<0x02>>) == %{ auth: false, priv: true, reportable: false } end test "decodes message flags with reportable only" do assert Constants.decode_msg_flags(<<0x04>>) == %{ auth: false, priv: false, reportable: true } end test "decodes message flags with auth and priv" do assert Constants.decode_msg_flags(<<0x03>>) == %{ auth: true, priv: true, reportable: false } end test "decodes message flags with all flags set" do assert Constants.decode_msg_flags(<<0x07>>) == %{ auth: true, priv: true, reportable: true } end end describe "default_msg_flags/1" do test "returns correct flags for no_auth_no_priv" do assert Constants.default_msg_flags(:no_auth_no_priv) == %{ auth: false, priv: false, reportable: true } end test "returns correct flags for auth_no_priv" do assert Constants.default_msg_flags(:auth_no_priv) == %{ auth: true, priv: false, reportable: true } end test "returns correct flags for auth_priv" do assert Constants.default_msg_flags(:auth_priv) == %{ auth: true, priv: true, reportable: true } end end describe "roundtrip encoding/decoding" do test "message flags roundtrip correctly" do test_cases = [ %{auth: false, priv: false, reportable: false}, %{auth: true, priv: false, reportable: false}, %{auth: false, priv: true, reportable: false}, %{auth: false, priv: false, reportable: true}, %{auth: true, priv: true, reportable: false}, %{auth: true, priv: false, reportable: true}, %{auth: false, priv: true, reportable: true}, %{auth: true, priv: true, reportable: true} ] for original_flags <- test_cases do encoded = Constants.encode_msg_flags(original_flags) decoded = Constants.decode_msg_flags(encoded) assert decoded == original_flags end end test "PDU type tags roundtrip correctly" do pdu_types = [ :get_request, :get_next_request, :get_response, :set_request, :get_bulk_request ] for pdu_type <- pdu_types do tag = Constants.pdu_type_to_tag(pdu_type) assert Constants.tag_to_pdu_type(tag) == pdu_type end end test "data type tags roundtrip correctly" do data_types = [ :integer, :octet_string, :null, :object_identifier, :counter32, :gauge32, :timeticks, :counter64, :ip_address, :opaque, :no_such_object, :no_such_instance, :end_of_mib_view ] for data_type <- data_types do tag = Constants.data_type_to_tag(data_type) assert Constants.tag_to_data_type(tag) == data_type end end test "error status codes roundtrip correctly" do error_statuses = [ :no_error, :too_big, :no_such_name, :bad_value, :read_only, :gen_err ] for error_status <- error_statuses do code = Constants.error_status_to_code(error_status) assert Constants.error_status_to_atom(code) == error_status end end end end