# Copyright 2020 Istvan Ruzman # SPDX-License-Identifier: MIT OR Apache-2.0 from io import StringIO import pytest from pyrad3.dictionary import Dictionary, ParseError from pyrad3.types import Encrypt @pytest.mark.parametrize( "filename", ["dictionaries/self_recursive", "dictionaries/mutual_recursive"] ) def test_dictionary_recursion(filename): with pytest.raises(ParseError): Dictionary("tests/" + filename) @pytest.mark.parametrize( "line", [ "$INCLUDE", "BEGIN-VENDOR", "END-VENDOR", "VENDOR", "VENDOR NAME", "ATTRIBUTE", "ATTRIBUTE NAME", "VALUE", "VALUE ATTRNAME", "VALUE ATTRNAME VALUENAME", ], ) def test_lines_missing_tokens(line): dictionary = StringIO(line) with pytest.raises(ParseError): Dictionary("", dictionary) def test_invalid_token(): dictionary = StringIO("invalid_token") with pytest.raises(ParseError): Dictionary("", dictionary) @pytest.mark.parametrize( "vendor", [ "VENDOR test 1234", "VENDOR test 1234 format=1,1", "VENDOR test 1234 format=2,2", "VENDOR test 1234 format=1,2", "VENDOR test 1234 format=4,2", "VENDOR test 1234 format=4,0", "VENDOR WiMAX 1234 format=1,1,c", ], ) def test_valid_vendor_definitions(vendor): dictionary = StringIO(vendor) Dictionary("", dictionary) def test_closing_wrong_vendor(): dictionary = StringIO( "VENDOR TEST-VENDOR 1234\n" "BEGIN-VENDOR TEST-VENDOR\n" "END-VENDOR WRONG-VENDOR" ) with pytest.raises(ParseError): Dictionary("", dictionary) def test_nested_vendor(): dictionary = StringIO( "VENDOR TEST-VENDOR1 1234\n" "VENDOR TEST-VENDOR2 1235\n" "BEGIN-VENDOR TEST-VENDOR1\n" "BEGIN-VENDOR TEST-VENDOR2" ) with pytest.raises(ParseError): Dictionary("", dictionary) def test_begin_vendor_without_definition(): dictionary = StringIO("BEGIN-VENDOR TEST-VENDOR") with pytest.raises(ParseError): Dictionary("", dictionary) @pytest.mark.parametrize( "vendor", [ "VENDOR test 1234 1,1", "VENDOR test 1234 format=3,1", "VENDOR test 1234 format=2", "VENDOR test 1234 format=1,2,c", "VENDOR test 1234 format=1,9", "VENDOR test 1234 format=4,4 suffix", "VENDOR test 1234 format=a,b suffix", ], ) def test_invalid_vendor_definitions(vendor): dictionary = StringIO(vendor) with pytest.raises(ParseError): Dictionary("", dictionary) @pytest.mark.parametrize( "number", [ "ATTRIBUTE NAME 0x01 byte", "ATTRIBUTE NAME 0x0001 byte", "ATTRIBUTE NAME 0o123 byte", "ATTRIBUTE NAME 5 byte", ], ) def test_valid_attribute_numbers(number): dictionary = StringIO(number) Dictionary("", dictionary) @pytest.mark.parametrize( "invalid_number", ["1000", "ABCD", "-1", "inf", "INF", "-INF", "2e4", "2.5e3"], ) def test_invalid_attribute_numbers(invalid_number): dictionary = StringIO(f"ATTRIBUTE NAME {invalid_number} integer64") with pytest.raises(ParseError): Dictionary("", dictionary) @pytest.mark.parametrize("type_length", [1, 2, 4]) def test_attribute_number_limits(type_length): too_big = 2 ** (8 * type_length) max_value = too_big - 1 dictionary = StringIO( f"VENDOR TEST 1234 format={type_length},1\n" "BEGIN-VENDOR TEST\n" f"ATTRIBUTE TEST {max_value} byte\n" "END-VENDOR TEST\n" ) Dictionary("", dictionary) dictionary = StringIO( f"VENDOR TEST 1234 format={type_length},1\n" "BEGIN-VENDOR TEST\n" f"ATTRIBUTE TEST {too_big} byte\n" "END-VENDOR TEST\n" ) with pytest.raises(ParseError): Dictionary("", dictionary) def test_invalid_attr_type(): dictionary = StringIO("ATTRIBUTE NAME 2 invalid") with pytest.raises(ParseError): Dictionary("", dictionary) @pytest.mark.parametrize("value", ["1", "0x1", "0o1"]) def test_value_definition(value): dictionary = StringIO( "ATTRIBUTE TEST-ATTRIBUTE 1 byte\n" f"VALUE TEST-ATTRIBUTE TEST-VALUE {value}" ) Dictionary("", dictionary) @pytest.mark.parametrize( "value_num, attr_type", [ (0, "byte"), (255, "byte"), (0, "short"), (2 ** 16 - 1, "short"), (0, "integer"), (2 ** 32 - 1, "integer"), ((-(2 ** 31)), "signed"), (2 ** 31 - 1, "signed"), (0, "integer64"), (2 ** 64 - 1, "integer64"), ], ) def test_value_number_within_limit(value_num, attr_type): dictionary = StringIO( f"ATTRIBUTE TEST-ATTRIBUTE 1 {attr_type}\n" f"VALUE TEST-ATTRIBUTE TEST-VALUE {value_num}" ) Dictionary("", dictionary) dictionary = StringIO( "VENDOR TEST-VENDOR 1234\n" "BEGIN-VENDOR TEST-VENDOR\n" f"ATTRIBUTE TEST-ATTRIBUTE 1 {attr_type}\n" f"VALUE TEST-ATTRIBUTE TEST-VALUE {value_num}\n" "END-VENDOR TEST-VEDNOR" ) @pytest.mark.parametrize( "value_num, attr_type", [ (-1, "byte"), (256, "byte"), (-1, "short"), (2 ** 16, "short"), (-1, "integer"), (2 ** 32, "integer"), (2 ** 31, "signed"), ((-(2 ** 31)) - 1, "signed"), (-1, "integer64"), (2 ** 64, "integer64"), ], ) def test_value_number_out_of_limit(value_num, attr_type): dictionary = StringIO( f"ATTRIBUTE TEST-ATTRIBUTE 1 {attr_type}\n" f"VALUE TEST-ATTRIBUTE TEST-VALUE {value_num}" ) with pytest.raises(ParseError): Dictionary("", dictionary) dictionary = StringIO( "VENDOR TEST-VENDOR 1234\n" "BEGIN-VENDOR TEST-VENDOR\n" f"ATTRIBUTE TEST-ATTRIBUTE 1 {attr_type}\n" f"VALUE TEST-ATTRIBUTE TEST-VALUE {value_num}\n" "END-VENDOR TEST-VEDNOR" ) with pytest.raises(ParseError): Dictionary("", dictionary) @pytest.mark.parametrize( "datatype", [ "string", "octets", "abinary", "byte", "short", "integer", "signed", "integer64", "ipaddr", "ipv4prefix", "ipv6addr", "ipv6prefix", "combo-ip", "ifid", "ether", "concat", "tlv", "extended", "long-extended", "evs", ], ) def test_all_datatypes_rfc_space(datatype): dictionary = StringIO(f"ATTRIBUTE TEST-ATTRIBUTE 1 {datatype}\n") Dictionary("", dictionary) @pytest.mark.parametrize( "datatype", [ "string", "octets", "abinary", "byte", "short", "integer", "signed", "integer64", "ipaddr", "ipv4prefix", "ipv6addr", "ipv6prefix", "combo-ip", "ifid", "ether", "tlv", ], ) def test_valid_datatypes_in_vendor_space(datatype): dictionary = StringIO( "VENDOR TEST 1234\n" "BEGIN-VENDOR TEST\n" f"ATTRIBUTE TEST-ATTRIBUTE 1 {datatype}\n" "END-VENDOR TEST\n" ) Dictionary("", dictionary) @pytest.mark.parametrize( "datatype", ["concat", "extended", "long-extended", "evs"] ) def test_invalid_datatypes_in_vendor_space(datatype): dictionary = StringIO( "VENDOR TEST 1234\n" "BEGIN-VENDOR TEST\n" f"ATTRIBUTE TEST-ATTRIBUTE 1 {datatype}\n" "END-VENDOR TEST\n" ) with pytest.raises(ParseError): Dictionary("", dictionary) @pytest.mark.parametrize( "invalid_number", ["ABCD", "-1", "inf", "INF", "-INF", "0.1", "2e4", "2.5e3"], ) def test_invalid_value_numbers(invalid_number): dictionary = StringIO( f"ATTRIBUTE TEST-ATTRIBUTE 1 integer\n" f"VALUE TEST-ATTRIBUTE TEST-VALUE {invalid_number}" ) with pytest.raises(ParseError): Dictionary("", dictionary) def test_value_for_non_existing_attribute(): dictionary = StringIO("VALUE ATTRNAME VALUENAME 1234") with pytest.raises(ParseError): Dictionary("", dictionary) @pytest.mark.parametrize( "datatype", [ "string", "octets", "abinary", "ipaddr", "ipv4prefix", "ipv6addr", "ipv6prefix", "combo-ip", "ifid", "ether", "tlv", ], ) def test_value_for_wrong_datatype(datatype): dictionary = StringIO( f"ATTRIBUTE NAME 123 {datatype}\n" "VALUE NAME VNAME 256" ) with pytest.raises(ParseError): Dictionary("", dictionary) def test_unimplemented_tlvs(): dictionary = StringIO("BEGIN-TLV") with pytest.raises(NotImplementedError): Dictionary("", dictionary) dictionary = StringIO("END-TLV") with pytest.raises(NotImplementedError): Dictionary("", dictionary) @pytest.mark.parametrize("flag", [1, 2, 3]) def test_valid_attribute_encrpytion_flags(flag): dictionary = StringIO(f"ATTRIBUTE NAME 123 octets encrypt={flag}") rad_dict = Dictionary("", dictionary) assert rad_dict.attrindex[123].encrypt == Encrypt(flag) @pytest.mark.parametrize("flag", ["0.1", "0", "4", "0x1", "0o2", "user", ""]) def test_invalid_attribute_encrpytion_flags(flag): dictionary = StringIO(f"ATTRIBUTE NAME 123 octets encrypt={flag}") with pytest.raises(ParseError): Dictionary("", dictionary) def test_has_tag_flag(): dictionary = StringIO("ATTRIBUTE NAME 123 octets has_tag") rad_dict = Dictionary("", dictionary) assert rad_dict.attrindex[123].has_tag @pytest.mark.parametrize("invalid_flag", ["blablub", "encrypt=2=2", "concat"]) def test_invalid_attribute_flags(invalid_flag): dictionary = StringIO(f"ATTRIBUTE NAME 123 octets {invalid_flag}") with pytest.raises(ParseError): Dictionary("", dictionary) @pytest.mark.parametrize( "attribute", ["RFC-ATTRIBUTE", "VENDOR-ATTRIBUTE", 7, (26, 5555, 7)] ) def test_get_attributes_from_dictionaries(attribute): dictionary = StringIO( "ATTRIBUTE RFC-ATTRIBUTE 7 integer\n" "VENDOR TEST-VENDOR 5555\n" "BEGIN-VENDOR TEST-VENDOR\n" "ATTRIBUTE VENDOR-ATTRIBUTE 7 integer\n" "END-VENDOR TEST-VENDOR" ) dd = Dictionary("", dictionary) _ = dd[attribute] @pytest.mark.parametrize( "attribute", ["RFC-ATTRIBUTE1", 8, (26, 5556, 7), (26, 5555, 8)] ) def test_get_nonexisting_attributes_from_dictionaries(attribute): dictionary = StringIO( "ATTRIBUTE RFC-ATTRIBUTE 7 integer\n" "VENDOR TEST-VENDOR 5555\n" "BEGIN-VENDOR TEST-VENDOR\n" "ATTRIBUTE VENDOR-ATTRIBUTE 7 integer\n" "END-VENDOR TEST-VENDOR" ) dd = Dictionary("", dictionary) with pytest.raises(KeyError): _ = dd[attribute]