summaryrefslogtreecommitdiffstats
path: root/tests/units/test_custom_types.py
blob: 8119849a6f44956e64dbb99abc89fbc9d5fa6128 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# Copyright (c) 2023-2024 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
"""Tests for `anta.custom_types`.

The intention is only to test here what is not used already in other places.

TODO: Expand later.
"""

from __future__ import annotations

import re

import pytest

from anta.custom_types import (
    REGEX_BGP_IPV4_MPLS_VPN,
    REGEX_BGP_IPV4_UNICAST,
    REGEXP_BGP_IPV4_MPLS_LABELS,
    REGEXP_BGP_L2VPN_AFI,
    REGEXP_EOS_BLACKLIST_CMDS,
    REGEXP_INTERFACE_ID,
    REGEXP_PATH_MARKERS,
    REGEXP_TYPE_EOS_INTERFACE,
    REGEXP_TYPE_HOSTNAME,
    REGEXP_TYPE_VXLAN_SRC_INTERFACE,
    aaa_group_prefix,
    bgp_multiprotocol_capabilities_abbreviations,
    interface_autocomplete,
    interface_case_sensitivity,
)

# ------------------------------------------------------------------------------
# TEST custom_types.py regular expressions
# ------------------------------------------------------------------------------


def test_regexp_path_markers() -> None:
    """Test REGEXP_PATH_MARKERS."""
    # Test strings that should match the pattern
    assert re.search(REGEXP_PATH_MARKERS, "show/bgp/interfaces") is not None
    assert re.search(REGEXP_PATH_MARKERS, "show\\bgp") is not None
    assert re.search(REGEXP_PATH_MARKERS, "show bgp") is not None

    # Test strings that should not match the pattern
    assert re.search(REGEXP_PATH_MARKERS, "aaaa") is None
    assert re.search(REGEXP_PATH_MARKERS, "11111") is None
    assert re.search(REGEXP_PATH_MARKERS, ".[]?<>") is None


def test_regexp_bgp_l2vpn_afi() -> None:
    """Test REGEXP_BGP_L2VPN_AFI."""
    # Test strings that should match the pattern
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2vpn-evpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2 vpn evpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2-vpn evpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2vpn evpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2vpnevpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2 vpnevpn") is not None

    # Test strings that should not match the pattern
    assert re.search(REGEXP_BGP_L2VPN_AFI, "al2vpn evpn") is None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2vpn-evpna") is None


def test_regexp_bgp_ipv4_mpls_labels() -> None:
    """Test REGEXP_BGP_IPV4_MPLS_LABELS."""
    assert re.search(REGEXP_BGP_IPV4_MPLS_LABELS, "ipv4-mpls-label") is not None
    assert re.search(REGEXP_BGP_IPV4_MPLS_LABELS, "ipv4 mpls labels") is not None
    assert re.search(REGEXP_BGP_IPV4_MPLS_LABELS, "ipv4Mplslabel") is None


def test_regex_bgp_ipv4_mpls_vpn() -> None:
    """Test REGEX_BGP_IPV4_MPLS_VPN."""
    assert re.search(REGEX_BGP_IPV4_MPLS_VPN, "ipv4-mpls-vpn") is not None
    assert re.search(REGEX_BGP_IPV4_MPLS_VPN, "ipv4_mplsvpn") is None


def test_regex_bgp_ipv4_unicast() -> None:
    """Test REGEX_BGP_IPV4_UNICAST."""
    assert re.search(REGEX_BGP_IPV4_UNICAST, "ipv4-uni-cast") is not None
    assert re.search(REGEX_BGP_IPV4_UNICAST, "ipv4+unicast") is None


def test_regexp_type_interface_id() -> None:
    """Test REGEXP_INTERFACE_ID."""
    intf_id_re = re.compile(f"{REGEXP_INTERFACE_ID}")

    # Test strings that should match the pattern
    assert intf_id_re.search("123") is not None
    assert intf_id_re.search("123/456") is not None
    assert intf_id_re.search("123.456") is not None
    assert intf_id_re.search("123/456.789") is not None


def test_regexp_type_eos_interface() -> None:
    """Test REGEXP_TYPE_EOS_INTERFACE."""
    # Test strings that should match the pattern
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Ethernet0") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Vlan100") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Port-Channel1/0") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Loopback0.1") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Management0/0/0") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Tunnel1") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Vxlan1") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Fabric1") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Dps1") is not None

    # Test strings that should not match the pattern
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Ethernet") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Vlan") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Port-Channel") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Loopback.") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Management/") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Tunnel") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Vxlan") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Fabric") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Dps") is None

    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Ethernet1/a") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Port-Channel-100") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Loopback.10") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Management/10") is None


def test_regexp_type_vxlan_src_interface() -> None:
    """Test REGEXP_TYPE_VXLAN_SRC_INTERFACE."""
    # Test strings that should match the pattern
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback0") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback1") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback99") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback100") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback8190") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback8199") is not None

    # Test strings that should not match the pattern
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback") is None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback9001") is None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback9000") is None


def test_regexp_type_hostname() -> None:
    """Test REGEXP_TYPE_HOSTNAME."""
    # Test strings that should match the pattern
    assert re.match(REGEXP_TYPE_HOSTNAME, "hostname") is not None
    assert re.match(REGEXP_TYPE_HOSTNAME, "hostname.com") is not None
    assert re.match(REGEXP_TYPE_HOSTNAME, "host-name.com") is not None
    assert re.match(REGEXP_TYPE_HOSTNAME, "host.name.com") is not None
    assert re.match(REGEXP_TYPE_HOSTNAME, "host-name1.com") is not None

    # Test strings that should not match the pattern
    assert re.match(REGEXP_TYPE_HOSTNAME, "-hostname.com") is None
    assert re.match(REGEXP_TYPE_HOSTNAME, ".hostname.com") is None
    assert re.match(REGEXP_TYPE_HOSTNAME, "hostname-.com") is None
    assert re.match(REGEXP_TYPE_HOSTNAME, "hostname..com") is None


@pytest.mark.parametrize(
    ("test_string", "expected"),
    [
        ("reload", True),  # matches "^reload.*"
        ("reload now", True),  # matches "^reload.*"
        ("configure terminal", True),  # matches "^conf\w*\s*(terminal|session)*"
        ("conf t", True),  # matches "^conf\w*\s*(terminal|session)*"
        ("write memory", True),  # matches "^wr\w*\s*\w+"
        ("wr mem", True),  # matches "^wr\w*\s*\w+"
        ("show running-config", False),  # does not match any regex
        ("no shutdown", False),  # does not match any regex
        ("", False),  # empty string does not match any regex
    ],
)
def test_regexp_eos_blacklist_cmds(test_string: str, expected: bool) -> None:
    """Test REGEXP_EOS_BLACKLIST_CMDS."""

    def matches_any_regex(string: str, regex_list: list[str]) -> bool:
        """
        Check if a string matches at least one regular expression in a list.

        :param string: The string to check.
        :param regex_list: A list of regular expressions.
        :return: True if the string matches at least one regular expression, False otherwise.
        """
        return any(re.match(regex, string) for regex in regex_list)

    assert matches_any_regex(test_string, REGEXP_EOS_BLACKLIST_CMDS) == expected


# ------------------------------------------------------------------------------
# TEST custom_types.py functions
# ------------------------------------------------------------------------------


def test_interface_autocomplete_success() -> None:
    """Test interface_autocomplete with valid inputs."""
    assert interface_autocomplete("et1") == "Ethernet1"
    assert interface_autocomplete("et1/1") == "Ethernet1/1"
    assert interface_autocomplete("et1.1") == "Ethernet1.1"
    assert interface_autocomplete("et1/1.1") == "Ethernet1/1.1"
    assert interface_autocomplete("eth2") == "Ethernet2"
    assert interface_autocomplete("po3") == "Port-Channel3"
    assert interface_autocomplete("lo4") == "Loopback4"


def test_interface_autocomplete_no_alias() -> None:
    """Test interface_autocomplete with inputs that don't have aliases."""
    assert interface_autocomplete("GigabitEthernet1") == "GigabitEthernet1"
    assert interface_autocomplete("Vlan10") == "Vlan10"
    assert interface_autocomplete("Tunnel100") == "Tunnel100"


def test_interface_autocomplete_failure() -> None:
    """Trigger ValueError for interface_autocomplete."""
    with pytest.raises(ValueError, match="Could not parse interface ID in interface"):
        interface_autocomplete("ThisIsNotAnInterface")


@pytest.mark.parametrize(
    ("str_input", "expected_output"),
    [
        pytest.param("L2VPNEVPN", "l2VpnEvpn", id="l2VpnEvpn"),
        pytest.param("ipv4-mplsLabels", "ipv4MplsLabels", id="ipv4MplsLabels"),
        pytest.param("ipv4-mpls-vpn", "ipv4MplsVpn", id="ipv4MplsVpn"),
        pytest.param("ipv4-unicast", "ipv4Unicast", id="ipv4Unicast"),
        pytest.param("BLAH", "BLAH", id="unmatched"),
    ],
)
def test_bgp_multiprotocol_capabilities_abbreviationsh(str_input: str, expected_output: str) -> None:
    """Test bgp_multiprotocol_capabilities_abbreviations."""
    assert bgp_multiprotocol_capabilities_abbreviations(str_input) == expected_output


def test_aaa_group_prefix_known_method() -> None:
    """Test aaa_group_prefix with a known method."""
    assert aaa_group_prefix("local") == "local"
    assert aaa_group_prefix("none") == "none"
    assert aaa_group_prefix("logging") == "logging"


def test_aaa_group_prefix_unknown_method() -> None:
    """Test aaa_group_prefix with an unknown method."""
    assert aaa_group_prefix("demo") == "group demo"
    assert aaa_group_prefix("group1") == "group group1"


def test_interface_case_sensitivity_lowercase() -> None:
    """Test interface_case_sensitivity with lowercase inputs."""
    assert interface_case_sensitivity("ethernet") == "Ethernet"
    assert interface_case_sensitivity("vlan") == "Vlan"
    assert interface_case_sensitivity("loopback") == "Loopback"


def test_interface_case_sensitivity_mixed_case() -> None:
    """Test interface_case_sensitivity with mixed case inputs."""
    assert interface_case_sensitivity("Ethernet") == "Ethernet"
    assert interface_case_sensitivity("Vlan") == "Vlan"
    assert interface_case_sensitivity("Loopback") == "Loopback"


def test_interface_case_sensitivity_uppercase() -> None:
    """Test interface_case_sensitivity with uppercase inputs."""
    assert interface_case_sensitivity("ETHERNET") == "ETHERNET"
    assert interface_case_sensitivity("VLAN") == "VLAN"
    assert interface_case_sensitivity("LOOPBACK") == "LOOPBACK"