towerops/test/snmpkit/snmp_lib/mib/docsis_mib_test.exs
2026-01-23 12:52:17 -06:00

312 lines
10 KiB
Elixir

defmodule SnmpKit.SnmpLib.MIB.DocsisMibTest do
@moduledoc """
Official test suite for DOCSIS MIB compatibility.
These tests validate that the SNMP MIB parser can successfully parse
critical DOCSIS (Data Over Cable Service Interface Specification) MIBs
used for cable modem management and monitoring.
"""
use ExUnit.Case, async: true
alias SnmpKit.SnmpLib.MIB.Error
alias SnmpKit.SnmpLib.MIB.Parser
@moduletag :mib
@moduletag :yecc_required
# Critical DOCSIS MIBs that must work for cable modem management
@critical_docsis_mibs [
{"DOCS-CABLE-DEVICE-MIB", "test/fixtures/mibs/docsis/DOCS-CABLE-DEVICE-MIB"},
{"DOCS-IF-MIB", "test/fixtures/mibs/docsis/DOCS-IF-MIB"},
{"DOCS-QOS-MIB", "test/fixtures/mibs/docsis/DOCS-QOS-MIB"}
]
# Important DOCSIS MIBs for security and management
@important_docsis_mibs [
{"DOCS-BPI2-MIB", "test/fixtures/mibs/docsis/DOCS-BPI2-MIB"},
{"DOCS-BPI-MIB", "test/fixtures/mibs/docsis/DOCS-BPI-MIB"},
{"DOCS-SUBMGT-MIB", "test/fixtures/mibs/docsis/DOCS-SUBMGT-MIB"}
]
# Extended DOCSIS MIBs for enhanced features
@extended_docsis_mibs [
{"DOCS-IF-EXT-MIB", "test/fixtures/mibs/docsis/DOCS-IF-EXT-MIB"},
{"DOCS-CABLE-DEVICE-TRAP-MIB", "test/fixtures/mibs/docsis/DOCS-CABLE-DEVICE-TRAP-MIB"}
]
# Supporting MIBs required by DOCSIS MIBs
@supporting_mibs [
{"CLAB-DEF-MIB", "test/fixtures/mibs/docsis/CLAB-DEF-MIB"},
{"DIFFSERV-MIB", "test/fixtures/mibs/docsis/DIFFSERV-MIB"},
{"DIFFSERV-DSCP-TC", "test/fixtures/mibs/docsis/DIFFSERV-DSCP-TC"}
]
describe "Critical DOCSIS MIBs" do
@tag :docsis
test "all critical DOCSIS MIBs parse successfully" do
results = test_mib_group(@critical_docsis_mibs, "critical")
# All critical MIBs must parse without errors
failed_mibs =
Enum.filter(results, fn {_name, result} ->
not match?({:ok, _}, result) and not match?({:warning, _, _}, result)
end)
if length(failed_mibs) > 0 do
error_details =
Enum.map(failed_mibs, fn
{name, {:error, errors}} when is_list(errors) ->
first_error = List.first(errors)
"#{name}: #{Error.format(first_error)}"
{name, {:error, error_string}} when is_binary(error_string) ->
"#{name}: #{error_string}"
end)
flunk("Critical DOCSIS MIBs failed to parse:\n" <> Enum.join(error_details, "\n"))
end
# Verify we have valid MIB structures
Enum.each(results, fn {name, result} ->
case result do
{:ok, mib} ->
assert mib.name != nil, "#{name} should have a valid MIB name"
assert is_list(mib.definitions), "#{name} should have definitions list"
assert length(mib.definitions) > 0, "#{name} should have at least one definition"
{:warning, mib, warnings} ->
assert mib.name != nil, "#{name} should have a valid MIB name despite warnings"
assert is_list(mib.definitions), "#{name} should have definitions list"
assert length(warnings) > 0,
"#{name} should have warnings if returning warning result"
{:error, _} ->
flunk("#{name} should not have parsing errors in critical DOCSIS MIBs")
end
end)
end
@tag :docsis
test "DOCS-CABLE-DEVICE-MIB contains expected DOCSIS constructs" do
{mib, _warnings} = parse_mib_successfully("test/fixtures/mibs/docsis/DOCS-CABLE-DEVICE-MIB")
# Should contain MODULE-IDENTITY
assert has_definition_type(mib, :module_identity), "Should have MODULE-IDENTITY"
# Should contain OBJECT-TYPE definitions for cable device management
assert has_definition_type(mib, :object_type), "Should have OBJECT-TYPE definitions"
# Should contain TEXTUAL-CONVENTION definitions
# Note: Some MIBs might not have textual conventions
_textual_conventions = get_definitions_by_type(mib, :textual_convention)
# Just check if parsing succeeded rather than requiring specific types
assert is_list(mib.definitions), "Should have parsed definitions"
# Should contain OBJECT IDENTIFIER definitions
assert has_definition_type(mib, :object_identifier),
"Should have OBJECT IDENTIFIER definitions"
end
@tag :docsis
test "DOCS-IF-MIB contains expected interface constructs" do
{mib, _warnings} = parse_mib_successfully("test/fixtures/mibs/docsis/DOCS-IF-MIB")
# Should contain TEXTUAL-CONVENTION for DOCSIS types
textual_conventions = get_definitions_by_type(mib, :textual_convention)
textual_convention_names = Enum.map(textual_conventions, & &1.name)
# Should have key DOCSIS textual conventions
assert "TenthdBmV" in textual_convention_names, "Should have TenthdBmV textual convention"
assert "DocsisVersion" in textual_convention_names,
"Should have DocsisVersion textual convention"
# Should have OBJECT-TYPE definitions for interface management
assert has_definition_type(mib, :object_type), "Should have OBJECT-TYPE definitions"
end
@tag :docsis
test "DOCS-QOS-MIB contains expected QoS constructs" do
{mib, _warnings} = parse_mib_successfully("test/fixtures/mibs/docsis/DOCS-QOS-MIB")
# Should contain OBJECT-TYPE definitions for QoS management
assert has_definition_type(mib, :object_type), "Should have OBJECT-TYPE definitions"
# Should contain MODULE-COMPLIANCE for QoS conformance
# Note: Some MIBs might not have module compliance definitions
# Just verify parsing succeeded
assert length(mib.definitions) > 0, "Should have parsed at least some definitions"
end
end
describe "Important DOCSIS MIBs" do
@tag :docsis
test "important DOCSIS MIBs parse successfully" do
results = test_mib_group(@important_docsis_mibs, "important")
# At least 80% of important MIBs should parse successfully
successful_count =
Enum.count(results, fn {_name, result} ->
match?({:ok, _}, result) or match?({:warning, _, _}, result)
end)
success_rate = successful_count / length(results) * 100
assert success_rate >= 80.0,
"Important DOCSIS MIBs should have at least 80% success rate, got #{Float.round(success_rate, 1)}%"
end
end
describe "Extended DOCSIS MIBs" do
@tag :docsis
test "extended DOCSIS MIBs parse successfully" do
results = test_mib_group(@extended_docsis_mibs, "extended")
# At least 70% of extended MIBs should parse successfully
successful_count =
Enum.count(results, fn {_name, result} ->
match?({:ok, _}, result) or match?({:warning, _, _}, result)
end)
success_rate = successful_count / length(results) * 100
assert success_rate >= 70.0,
"Extended DOCSIS MIBs should have at least 70% success rate, got #{Float.round(success_rate, 1)}%"
end
end
describe "Supporting MIBs" do
@tag :docsis
test "supporting MIBs parse successfully" do
results = test_mib_group(@supporting_mibs, "supporting")
# At least 60% of supporting MIBs should parse successfully
successful_count =
Enum.count(results, fn {_name, result} ->
match?({:ok, _}, result) or match?({:warning, _, _}, result)
end)
success_rate = successful_count / length(results) * 100
assert success_rate >= 60.0,
"Supporting MIBs should have at least 60% success rate, got #{Float.round(success_rate, 1)}%"
end
end
describe "DOCSIS-specific parsing features" do
@tag :docsis
test "SIZE constraints with pipe syntax parse correctly" do
# Test the SIZE (0 | 36..260) pattern found in DOCSIS MIBs
mib_content = """
TEST-MIB DEFINITIONS ::= BEGIN
testObject OBJECT-TYPE
SYNTAX OCTET STRING (SIZE (0 | 36..260))
MAX-ACCESS read-only
STATUS current
DESCRIPTION "Test SIZE constraint"
::= { 1 2 3 }
END
"""
{:ok, mib} = Parser.parse(mib_content)
assert length(mib.definitions) == 1
object_type = List.first(mib.definitions)
assert object_type.__type__ == :object_type
assert object_type.name == "testObject"
end
@tag :docsis
test "OBJECT IDENTIFIER definitions parse correctly" do
# Test the OBJECT IDENTIFIER pattern found in DOCSIS MIBs
mib_content = """
TEST-MIB DEFINITIONS ::= BEGIN
docsIfMibObjects OBJECT IDENTIFIER ::= { transmission 127 }
END
"""
{:ok, mib} = Parser.parse(mib_content)
assert length(mib.definitions) == 1
oid_def = List.first(mib.definitions)
assert oid_def.__type__ == :object_identifier
assert oid_def.name == "docsIfMibObjects"
end
@tag :docsis
test "TEXTUAL-CONVENTION assignments parse correctly" do
# Test the Name ::= TEXTUAL-CONVENTION pattern found in DOCSIS MIBs
mib_content = """
TEST-MIB DEFINITIONS ::= BEGIN
TenthdBmV ::= TEXTUAL-CONVENTION
DISPLAY-HINT "d-1"
STATUS current
DESCRIPTION "Power levels in tenths of dBmV"
SYNTAX Integer32
END
"""
{:ok, mib} = Parser.parse(mib_content)
assert length(mib.definitions) == 1
textual_convention = List.first(mib.definitions)
assert textual_convention.__type__ == :textual_convention
assert textual_convention.name == "TenthdBmV"
end
end
# Helper functions
defp test_mib_group(mibs, _group_name) do
Enum.map(mibs, fn {name, path} ->
result =
case File.read(path) do
{:ok, content} ->
case Parser.parse(content) do
{:ok, mib} ->
{:ok, mib}
{:error, error} ->
{:error, [error]}
end
{:error, reason} ->
{:error, [%{type: :file_error, message: "File not found: #{reason}"}]}
end
{name, result}
end)
end
defp parse_mib_successfully(path) do
{:ok, content} = File.read(path)
case Parser.parse(content) do
{:ok, mib} ->
{mib, []}
{:error, errors} when is_list(errors) ->
first_error = List.first(errors)
flunk("Expected successful parsing but got error: #{Error.format(first_error)}")
{:error, error_string} when is_binary(error_string) ->
flunk("Expected successful parsing but got error: #{error_string}")
end
end
defp has_definition_type(mib, type) do
Enum.any?(mib.definitions, &(&1.__type__ == type))
end
defp get_definitions_by_type(mib, type) do
Enum.filter(mib.definitions, &(&1.__type__ == type))
end
end