towerops/test/snmpkit/snmp_mgr/errors_test.exs
2026-02-03 10:28:39 -06:00

447 lines
17 KiB
Elixir

defmodule SnmpKit.SnmpMgr.ErrorsTest do
use ExUnit.Case, async: true
alias SnmpKit.SnmpMgr.Errors
describe "code_to_atom/1" do
test "converts standard SNMP error codes to atoms" do
assert Errors.code_to_atom(0) == :no_error
assert Errors.code_to_atom(1) == :too_big
assert Errors.code_to_atom(2) == :no_such_name
assert Errors.code_to_atom(3) == :bad_value
assert Errors.code_to_atom(4) == :read_only
assert Errors.code_to_atom(5) == :gen_err
end
test "converts SNMPv2c error codes to atoms" do
assert Errors.code_to_atom(6) == :no_access
assert Errors.code_to_atom(7) == :wrong_type
assert Errors.code_to_atom(8) == :wrong_length
assert Errors.code_to_atom(9) == :wrong_encoding
assert Errors.code_to_atom(10) == :wrong_value
assert Errors.code_to_atom(11) == :no_creation
assert Errors.code_to_atom(12) == :inconsistent_value
assert Errors.code_to_atom(13) == :resource_unavailable
assert Errors.code_to_atom(14) == :commit_failed
assert Errors.code_to_atom(15) == :undo_failed
assert Errors.code_to_atom(16) == :authorization_error
assert Errors.code_to_atom(17) == :not_writable
assert Errors.code_to_atom(18) == :inconsistent_name
end
test "returns :unknown_error for invalid codes" do
assert Errors.code_to_atom(999) == :unknown_error
assert Errors.code_to_atom(-1) == :unknown_error
end
end
describe "description/1" do
test "provides descriptions for standard errors" do
assert Errors.description(:no_error) == "No error occurred"
assert Errors.description(:too_big) == "Response too big to fit in message"
assert Errors.description(:no_such_name) == "Variable name not found"
assert Errors.description(:bad_value) == "Invalid value for variable"
assert Errors.description(:read_only) == "Variable is read-only"
assert Errors.description(:gen_err) == "General error"
end
test "provides descriptions for SNMPv2c errors" do
assert Errors.description(:no_access) == "Access denied"
assert Errors.description(:wrong_type) == "Wrong data type"
assert Errors.description(:authorization_error) == "Authorization failed"
assert Errors.description(:not_writable) == "Variable not writable"
end
test "returns 'Unknown error' for unknown atoms" do
assert Errors.description(:unknown_error) == "Unknown error"
assert Errors.description(:some_random_atom) == "Unknown error"
end
end
describe "code_to_description/1" do
test "converts code directly to description" do
assert Errors.code_to_description(2) == "Variable name not found"
assert Errors.code_to_description(18) == "Inconsistent name"
assert Errors.code_to_description(5) == "General error"
end
test "handles unknown codes" do
assert Errors.code_to_description(999) == "Unknown error"
end
end
describe "v2c_error?/1" do
test "returns true for SNMPv2c-specific errors" do
assert Errors.v2c_error?(:no_access) == true
assert Errors.v2c_error?(:wrong_type) == true
assert Errors.v2c_error?(:wrong_length) == true
assert Errors.v2c_error?(:authorization_error) == true
assert Errors.v2c_error?(:not_writable) == true
end
test "returns false for SNMPv1 errors" do
assert Errors.v2c_error?(:no_such_name) == false
assert Errors.v2c_error?(:too_big) == false
assert Errors.v2c_error?(:bad_value) == false
assert Errors.v2c_error?(:read_only) == false
assert Errors.v2c_error?(:gen_err) == false
end
test "returns false for unknown errors" do
assert Errors.v2c_error?(:unknown_error) == false
assert Errors.v2c_error?(:timeout) == false
end
end
describe "format_error/1" do
test "formats SNMP errors with integer codes" do
result = Errors.format_error({:snmp_error, 2})
assert result == "SNMP Error (2): Variable name not found"
end
test "formats SNMP errors with atom codes" do
result = Errors.format_error({:snmp_error, :no_such_name})
assert result == "SNMP Error: Variable name not found"
end
test "formats SNMPv2c errors without details" do
result = Errors.format_error({:v2c_error, :no_access})
assert result == "SNMPv2c Error: Access denied"
end
test "formats SNMPv2c errors with details" do
result = Errors.format_error({:v2c_error, :no_access, oid: "1.2.3.4"})
assert result == "SNMPv2c Error: Access denied (OID: 1.2.3.4)"
end
test "formats SNMPv2c errors with multiple details" do
result = Errors.format_error({:v2c_error, :wrong_type, [oid: "1.2.3.4", value: "test"]})
assert result =~ "SNMPv2c Error: Wrong data type"
assert result =~ "OID: 1.2.3.4"
assert result =~ "Value: \"test\""
end
test "formats network errors" do
result = Errors.format_error({:network_error, :timeout})
assert result == "Network Error: :timeout"
end
test "formats timeout errors" do
result = Errors.format_error({:timeout, nil})
assert result == "Timeout Error: Request timed out"
end
test "formats encoding errors" do
result = Errors.format_error({:encoding_error, "bad data"})
assert result == "Encoding Error: \"bad data\""
end
test "formats decoding errors" do
result = Errors.format_error({:decoding_error, "corrupt packet"})
assert result == "Decoding Error: \"corrupt packet\""
end
test "formats unknown errors" do
result = Errors.format_error(:some_random_error)
assert result == "Unknown Error: :some_random_error"
end
end
describe "recoverable?/1" do
test "returns false for unrecoverable network errors" do
assert Errors.recoverable?({:network_error, :host_unreachable}) == false
assert Errors.recoverable?({:network_error, :network_unreachable}) == false
end
test "returns false for permanent SNMP errors" do
assert Errors.recoverable?({:snmp_error, :no_such_name}) == false
assert Errors.recoverable?({:snmp_error, :bad_value}) == false
assert Errors.recoverable?({:snmp_error, :read_only}) == false
end
test "returns false for permanent SNMPv2c errors" do
assert Errors.recoverable?({:v2c_error, :no_access}) == false
assert Errors.recoverable?({:v2c_error, :not_writable}) == false
assert Errors.recoverable?({:v2c_error, :wrong_type}) == false
end
test "returns true for recoverable errors" do
assert Errors.recoverable?(:timeout) == true
assert Errors.recoverable?({:snmp_error, :too_big}) == true
assert Errors.recoverable?({:snmp_error, :gen_err}) == true
end
test "returns false for unknown errors" do
assert Errors.recoverable?(:unknown_error) == false
assert Errors.recoverable?({:custom_error, "test"}) == false
end
end
describe "analyze_error/1" do
test "analyzes SNMP protocol errors with integer codes" do
result = Errors.analyze_error({:snmp_error, 2})
assert result.type == :snmp_protocol
assert result.atom == :no_such_name
assert result.code == 2
assert result.severity == :error
assert result.retriable == false
assert result.category == :user_error
assert result.description == "Variable name not found"
end
test "analyzes SNMP protocol errors with atom codes" do
result = Errors.analyze_error({:snmp_error, :too_big})
assert result.type == :snmp_protocol
assert result.atom == :too_big
assert result.code == 1
assert result.retriable == true
assert result.category == :resource_error
end
test "analyzes network timeout errors" do
result = Errors.analyze_error(:timeout)
assert result.type == :network
assert result.atom == :timeout
assert result.retriable == true
assert result.category == :transient_error
assert result.description == "Operation timed out"
end
test "analyzes host unreachable errors" do
result = Errors.analyze_error(:host_unreachable)
assert result.type == :network
assert result.atom == :host_unreachable
assert result.retriable == false
assert result.category == :configuration_error
end
test "analyzes connection refused errors" do
result = Errors.analyze_error(:connection_refused)
assert result.type == :network
assert result.retriable == false
assert result.category == :service_error
end
test "analyzes unknown errors" do
result = Errors.analyze_error({:custom_error, "test"})
assert result.type == :other
assert result.retriable == false
# classify_error returns a tuple for unknown errors, but as subcategory
assert result.category == :unknown_error
assert result.subcategory == {:custom_error, "test"}
end
end
describe "classify_error/2" do
test "classifies user errors" do
assert Errors.classify_error({:snmp_error, :no_such_name}) == {:user_error, :invalid_oid}
assert Errors.classify_error({:snmp_error, :bad_value}) == {:user_error, :invalid_value}
assert Errors.classify_error({:snmp_error, :read_only}) == {:user_error, :write_to_readonly}
assert Errors.classify_error({:v2c_error, :wrong_type}) == {:user_error, :type_mismatch}
end
test "classifies security errors" do
assert Errors.classify_error({:v2c_error, :no_access}) == {:security_error, :access_denied}
assert Errors.classify_error({:v2c_error, :authorization_error}) ==
{:security_error, :authorization_failed}
end
test "classifies device errors" do
assert Errors.classify_error({:snmp_error, :gen_err}) == {:device_error, :general_failure}
assert Errors.classify_error({:v2c_error, :resource_unavailable}) ==
{:device_error, :resource_exhausted}
end
test "classifies recoverable errors" do
assert Errors.classify_error({:snmp_error, :too_big}) ==
{:recoverable_error, :response_too_large}
end
test "classifies network errors" do
assert Errors.classify_error({:network_error, :timeout}) ==
{:transient_error, :network_timeout}
assert Errors.classify_error({:network_error, :host_unreachable}) ==
{:configuration_error, :unreachable_host}
assert Errors.classify_error({:network_error, :network_unreachable}) ==
{:configuration_error, :network_unavailable}
end
test "classifies protocol errors" do
assert Errors.classify_error({:encoding_error, "bad data"}) ==
{:protocol_error, :message_encoding_failed}
assert Errors.classify_error({:decoding_error, "corrupt"}) ==
{:protocol_error, :message_decoding_failed}
end
test "classifies validation errors" do
assert Errors.classify_error(:invalid_oid_values) == :validation_error
assert Errors.classify_error({:snmp_encoding_error, "test"}) == :validation_error
assert Errors.classify_error(:encoding_failed) == :validation_error
end
test "classifies authentication errors" do
assert Errors.classify_error(:authentication_error) == :authentication_error
assert Errors.classify_error(:invalid_community) == :invalid_community
assert Errors.classify_error(:bad_community) == :invalid_community
end
test "classifies system errors" do
assert Errors.classify_error(:snmp_modules_not_available) == :system_error
end
test "classifies timeout errors" do
assert Errors.classify_error(:timeout) == {:transient_error, :operation_timeout}
end
test "accepts optional context parameter" do
# Context doesn't change behavior but is accepted
assert Errors.classify_error({:snmp_error, :no_such_name}, "Get request") ==
{:user_error, :invalid_oid}
end
test "classifies unknown errors" do
assert Errors.classify_error(:unknown_atom) == {:unknown_error, :unknown_atom}
assert Errors.classify_error({:custom, "test"}) == {:unknown_error, {:custom, "test"}}
end
end
describe "format_user_friendly_error/2" do
test "formats SNMP errors with context" do
assert Errors.format_user_friendly_error({:snmp_error, :no_such_name}, "Getting sysDescr") ==
"Getting sysDescr failed: The requested OID does not exist on the device"
assert Errors.format_user_friendly_error({:snmp_error, :bad_value}, "Setting value") ==
"Setting value failed: The provided value is invalid for this OID"
end
test "formats network errors with context" do
assert Errors.format_user_friendly_error({:network_error, :timeout}, "Polling device") ==
"Polling device failed: The device did not respond within the timeout period"
assert Errors.format_user_friendly_error(
{:network_error, :host_unreachable},
"Connecting"
) ==
"Connecting failed: Cannot reach the device. Check the IP address and network connectivity"
end
test "formats timeout errors" do
assert Errors.format_user_friendly_error(:timeout, "Discovery") ==
"Discovery timed out: The operation took too long to complete"
end
test "uses default context when not provided" do
assert Errors.format_user_friendly_error({:snmp_error, :no_such_name}) ==
"Operation failed: The requested OID does not exist on the device"
end
test "formats encoding/decoding errors" do
assert Errors.format_user_friendly_error({:encoding_error, "test"}, "Sending request") ==
"Sending request failed: Unable to encode the SNMP message"
assert Errors.format_user_friendly_error({:decoding_error, "test"}, "Reading response") ==
"Reading response failed: Unable to decode the SNMP response"
end
test "formats unknown errors" do
result = Errors.format_user_friendly_error({:custom_error, "test"}, "Testing")
assert result =~ "Testing failed:"
end
end
describe "get_recovery_suggestions/1" do
test "provides suggestions for no_such_name errors" do
suggestions = Errors.get_recovery_suggestions({:snmp_error, :no_such_name})
assert "Verify the OID is correct" in suggestions
assert "Check if the OID is supported by this device" in suggestions
assert "Try using MIB browser to explore available OIDs" in suggestions
assert "Ensure you're using the correct SNMP version" in suggestions
end
test "provides suggestions for bad_value errors" do
suggestions = Errors.get_recovery_suggestions({:snmp_error, :bad_value})
assert "Check the value format and type" in suggestions
assert "Verify the value is within acceptable range" in suggestions
end
test "provides suggestions for read_only errors" do
suggestions = Errors.get_recovery_suggestions({:snmp_error, :read_only})
assert "This OID cannot be modified" in suggestions
assert "Use GET operation instead of SET" in suggestions
end
test "provides suggestions for too_big errors" do
suggestions = Errors.get_recovery_suggestions({:snmp_error, :too_big})
assert "Reduce the number of OIDs in the request" in suggestions
assert "Use GETBULK operation with smaller max-repetitions" in suggestions
assert "Split the request into multiple smaller requests" in suggestions
end
test "provides suggestions for access errors" do
suggestions = Errors.get_recovery_suggestions({:v2c_error, :no_access})
assert "Verify the community string is correct" in suggestions
assert "Check device SNMP access configuration" in suggestions
end
test "provides suggestions for timeout errors" do
suggestions = Errors.get_recovery_suggestions({:network_error, :timeout})
assert "Increase timeout value" in suggestions
assert "Check network connectivity to the device" in suggestions
assert "Verify device is responding to ping" in suggestions
end
test "provides suggestions for host unreachable errors" do
suggestions = Errors.get_recovery_suggestions({:network_error, :host_unreachable})
assert "Verify the IP address is correct" in suggestions
assert "Check network routing" in suggestions
assert "Test basic connectivity with ping" in suggestions
end
test "provides suggestions for encoding errors" do
suggestions = Errors.get_recovery_suggestions({:encoding_error, "test"})
assert "Check OID format" in suggestions
assert "Verify value types are supported" in suggestions
end
test "provides suggestions for decoding errors" do
suggestions = Errors.get_recovery_suggestions({:decoding_error, "test"})
assert "Device may not support SNMP" in suggestions
assert "Check SNMP version compatibility" in suggestions
end
test "provides suggestions for timeout atom" do
suggestions = Errors.get_recovery_suggestions(:timeout)
assert "Increase operation timeout" in suggestions
assert "Check if operation is too complex" in suggestions
end
test "provides generic suggestions for unknown errors" do
suggestions = Errors.get_recovery_suggestions({:custom_error, "test"})
assert "Check device documentation" in suggestions
assert "Verify SNMP configuration" in suggestions
assert "Contact device vendor support" in suggestions
end
end
end