diff options
Diffstat (limited to 'tests/unit/modules')
| -rw-r--r-- | tests/unit/modules/__init__.py | 0 | ||||
| -rw-r--r-- | tests/unit/modules/base.py | 53 | ||||
| -rw-r--r-- | tests/unit/modules/conftest.py | 2 | ||||
| -rw-r--r-- | tests/unit/modules/test_vyos_configure.py | 64 | ||||
| -rw-r--r-- | tests/unit/modules/test_vyos_logging_global.py | 240 | ||||
| -rw-r--r-- | tests/unit/modules/test_vyos_ntp_global.py | 183 | ||||
| -rw-r--r-- | tests/unit/modules/test_vyos_route_maps.py | 193 |
7 files changed, 735 insertions, 0 deletions
diff --git a/tests/unit/modules/__init__.py b/tests/unit/modules/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/unit/modules/__init__.py diff --git a/tests/unit/modules/base.py b/tests/unit/modules/base.py new file mode 100644 index 0000000..4d49fbb --- /dev/null +++ b/tests/unit/modules/base.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# GNU General Public License v3.0+ + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import json +import os +import unittest + +from unittest.mock import MagicMock # noqa: F401 + + +def load_fixture(filename): + """Load a JSON fixture file from tests/unit/fixtures/.""" + fixtures_dir = os.path.join(os.path.dirname(__file__), "..", "fixtures") + path = os.path.join(fixtures_dir, filename) + with open(path) as f: + return json.load(f) + + +class VyOSModuleTestCase(unittest.TestCase): + """ + Base class for vyos.rest module unit tests. + + Provides a mock VyOSModule that returns fixture data from + get_config() without any device connection. + + Usage: + class TestVyOSNtpGlobal(VyOSModuleTestCase): + def setUp(self): + super().setUp() + self.fixture = load_fixture("ntp_global_running.json") + + def test_merged_adds_new_server(self): + have = self.module.get_running_config_from_fixture(self.fixture) + commands = build_commands(want, have, "merged") + self.assertIn(("set", ["service", "ntp", "server", "1.2.3.4"]), commands) + """ + + def setUp(self): + self.mock_module = MagicMock() + self.mock_module.params = {} + self.mock_module.check_mode = False + + self.mock_vyos = MagicMock() + self.mock_vyos.get_config = MagicMock(return_value={}) + + def set_running_config(self, data): + """Configure mock get_config to return given data.""" + self.mock_vyos.get_config.return_value = data diff --git a/tests/unit/modules/conftest.py b/tests/unit/modules/conftest.py new file mode 100644 index 0000000..26b7d8b --- /dev/null +++ b/tests/unit/modules/conftest.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +# conftest.py — shared fixtures for vyos.rest unit tests diff --git a/tests/unit/modules/test_vyos_configure.py b/tests/unit/modules/test_vyos_configure.py new file mode 100644 index 0000000..af1b325 --- /dev/null +++ b/tests/unit/modules/test_vyos_configure.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import unittest + +from ansible_collections.vyos.rest.plugins.modules.vyos_configure import ( + _parse_command, +) + + +class TestVyOSConfigureParseCommand(unittest.TestCase): + + def test_set_simple(self): + result = _parse_command("set system host-name vyos") + self.assertEqual(result, ("set", ["system", "host-name", "vyos"])) + + def test_set_with_address(self): + result = _parse_command("set interfaces loopback lo address 20.1.1.1/32") + self.assertEqual( + result, + ("set", ["interfaces", "loopback", "lo", "address", "20.1.1.1/32"]), + ) + + def test_delete_simple(self): + result = _parse_command("delete service snmp") + self.assertEqual(result, ("delete", ["service", "snmp"])) + + def test_delete_with_path(self): + result = _parse_command("delete interfaces loopback lo address 20.1.1.1/32") + self.assertEqual( + result, + ("delete", ["interfaces", "loopback", "lo", "address", "20.1.1.1/32"]), + ) + + def test_strips_leading_whitespace(self): + result = _parse_command(" set system host-name vyos") + self.assertEqual(result, ("set", ["system", "host-name", "vyos"])) + + def test_invalid_command_returns_none(self): + result = _parse_command("commit") + self.assertIsNone(result) + + def test_empty_string_returns_none(self): + result = _parse_command("") + self.assertIsNone(result) + + def test_unknown_op_returns_none(self): + result = _parse_command("show interfaces") + self.assertIsNone(result) + + def test_set_single_token_path(self): + result = _parse_command("set service") + self.assertEqual(result, ("set", ["service"])) + + def test_delete_single_token_path(self): + result = _parse_command("delete service") + self.assertEqual(result, ("delete", ["service"])) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unit/modules/test_vyos_logging_global.py b/tests/unit/modules/test_vyos_logging_global.py new file mode 100644 index 0000000..8593f1f --- /dev/null +++ b/tests/unit/modules/test_vyos_logging_global.py @@ -0,0 +1,240 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import os +import sys +import unittest + + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) + +from ansible_collections.vyos.rest.plugins.modules.vyos_logging_global import ( + build_commands, + normalize_config, + normalize_running, +) + + +class TestVyOSLoggingGlobalNormalize(unittest.TestCase): + + def test_normalize_config_console_severity_is_string(self): + cfg = { + "console": { + "facilities": [{"facility": "local7", "severity": "err"}], + }, + } + result = normalize_config(cfg) + self.assertIn("local7", result["console"]["facilities"]) + # severity is stored as plain string, not dict + self.assertEqual(result["console"]["facilities"]["local7"], "err") + + def test_normalize_config_console_no_severity(self): + cfg = { + "console": { + "facilities": [{"facility": "all"}], + }, + } + result = normalize_config(cfg) + self.assertIsNone(result["console"]["facilities"]["all"]) + + def test_normalize_config_hosts(self): + cfg = { + "hosts": [ + { + "hostname": "172.16.0.1", + "port": 514, + "facilities": [ + {"facility": "local7", "severity": "all"}, + {"facility": "all", "protocol": "udp"}, + ], + }, + ], + } + result = normalize_config(cfg) + self.assertIn("172.16.0.1", result["hosts"]) + host = result["hosts"]["172.16.0.1"] + self.assertEqual(host["port"], 514) + self.assertIn("local7", host["facilities"]) + # host facilities are dicts with severity/protocol + self.assertEqual(host["facilities"]["local7"]["severity"], "all") + self.assertEqual(host["facilities"]["all"]["protocol"], "udp") + + def test_normalize_config_global_preserve_fqdn(self): + cfg = {"global_params": {"preserve_fqdn": True}} + result = normalize_config(cfg) + self.assertTrue(result["global"]["preserve_fqdn"]) + + def test_normalize_config_global_archive(self): + cfg = {"global_params": {"archive": {"file_num": 2, "size": 111}}} + result = normalize_config(cfg) + self.assertEqual(result["global"]["archive"]["file_num"], 2) + self.assertEqual(result["global"]["archive"]["size"], 111) + + def test_normalize_config_empty(self): + result = normalize_config({}) + self.assertEqual(result["console"]["facilities"], {}) + self.assertEqual(result["hosts"], {}) + self.assertEqual(result["files"], {}) + self.assertEqual(result["users"], {}) + + def test_normalize_running_console_severity_is_string(self): + raw = { + "console": { + "facility": { + "local7": {"level": "err"}, + "all": {}, + }, + }, + } + result = normalize_running(raw) + self.assertIn("local7", result["console"]["facilities"]) + # severity is plain string from "level" key + self.assertEqual(result["console"]["facilities"]["local7"], "err") + self.assertIsNone(result["console"]["facilities"]["all"]) + + def test_normalize_running_host_port_not_cast(self): + """Port is NOT cast to int — stored as-is from API response.""" + raw = { + "host": { + "172.16.0.1": { + "port": "514", + "facility": {}, + }, + }, + } + result = normalize_running(raw) + # port stays as string — module does not cast + self.assertEqual(result["hosts"]["172.16.0.1"]["port"], "514") + + def test_normalize_running_global_archive_key(self): + """Archive stored under 'archive' key — no file_num remapping.""" + raw = { + "global": { + "archive": {"file": "2", "size": "111"}, + "marker": {"interval": "111"}, + "preserve-fqdn": {}, + }, + } + result = normalize_running(raw) + # archive stored as-is from API + self.assertEqual(result["global"]["archive"]["file"], "2") + self.assertEqual(result["global"]["archive"]["size"], "111") + # marker_interval stored as string — no cast + self.assertEqual(result["global"]["marker_interval"], "111") + self.assertTrue(result["global"]["preserve_fqdn"]) + + def test_normalize_running_empty(self): + result = normalize_running({}) + self.assertEqual(result["console"]["facilities"], {}) + self.assertEqual(result["hosts"], {}) + + def test_normalize_running_host_facilities(self): + raw = { + "host": { + "172.16.0.1": { + "facility": { + "local7": {"level": "all"}, + "all": {"protocol": "udp"}, + }, + "port": "223", + }, + }, + } + result = normalize_running(raw) + h = result["hosts"]["172.16.0.1"] + self.assertEqual(h["facilities"]["local7"]["severity"], "all") + self.assertEqual(h["facilities"]["all"]["protocol"], "udp") + + +class TestVyOSLoggingGlobalBuildCommands(unittest.TestCase): + + def _empty_have(self): + return { + "console": {"facilities": {}}, + "global": {"facilities": {}}, + "hosts": {}, + "files": {}, + "users": {}, + } + + def test_merged_adds_console_facility_with_severity(self): + want = self._empty_have() + want["console"]["facilities"]["local7"] = "err" + cmds = build_commands(want, self._empty_have(), "merged") + self.assertIn( + ("set", ["system", "syslog", "console", "facility", "local7", "level", "err"]), + cmds, + ) + + def test_merged_adds_console_facility_no_severity(self): + want = self._empty_have() + want["console"]["facilities"]["all"] = None + cmds = build_commands(want, self._empty_have(), "merged") + self.assertIn( + ("set", ["system", "syslog", "console", "facility", "all"]), + cmds, + ) + + def test_merged_idempotent_console(self): + facs = {"local7": "err"} + want = self._empty_have() + have = self._empty_have() + want["console"]["facilities"] = facs + have["console"]["facilities"] = dict(facs) + cmds = build_commands(want, have, "merged") + self.assertEqual(cmds, []) + + def test_merged_adds_host(self): + want = self._empty_have() + want["hosts"]["172.16.0.1"] = { + "port": 514, + "facilities": {"local7": {"severity": "all", "protocol": None}}, + } + cmds = build_commands(want, self._empty_have(), "merged") + paths = [c[1] for c in cmds] + # diff_map only adds the host key, not per-facility details + self.assertIn(["system", "syslog", "host", "172.16.0.1"], paths) + + def test_replaced_removes_extra_host(self): + want = self._empty_have() + have = self._empty_have() + have["hosts"]["172.16.0.1"] = {"port": None, "facilities": {}} + cmds = build_commands(want, have, "replaced") + self.assertIn(("delete", ["system", "syslog", "host", "172.16.0.1"]), cmds) + + def test_deleted_removes_per_field(self): + """deleted state removes per-facility entries, not single subtree.""" + have = self._empty_have() + have["console"]["facilities"]["all"] = None + cmds = build_commands(self._empty_have(), have, "deleted") + self.assertIn( + ("delete", ["system", "syslog", "console", "facility", "all"]), + cmds, + ) + + def test_overridden_deletes_all_then_merges(self): + want = self._empty_have() + want["console"]["facilities"]["local7"] = "err" + have = self._empty_have() + have["console"]["facilities"]["all"] = None + cmds = build_commands(want, have, "overridden") + # first command is full syslog delete + self.assertEqual(cmds[0], ("delete", ["system", "syslog"])) + # then adds wanted facility + self.assertIn( + ("set", ["system", "syslog", "console", "facility", "local7", "level", "err"]), + cmds, + ) + + def test_no_commands_when_already_correct(self): + state = self._empty_have() + state["console"]["facilities"]["local7"] = "err" + cmds = build_commands(state, state, "merged") + self.assertEqual(cmds, []) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unit/modules/test_vyos_ntp_global.py b/tests/unit/modules/test_vyos_ntp_global.py new file mode 100644 index 0000000..c536141 --- /dev/null +++ b/tests/unit/modules/test_vyos_ntp_global.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import json +import os +import unittest + +from unittest.mock import MagicMock + +from ansible_collections.vyos.rest.plugins.modules.vyos_ntp_global import ( + build_commands, + get_running_config, + normalize_config, + normalize_servers, +) + + +def load_fixture(filename): + fixtures_dir = os.path.join(os.path.dirname(__file__), "..", "fixtures") + path = os.path.join(fixtures_dir, filename) + with open(path) as f: + return json.load(f) + + +class VyOSModuleTestCase(unittest.TestCase): + def setUp(self): + self.mock_vyos = MagicMock() + self.mock_vyos.get_config = MagicMock(return_value={}) + + def set_running_config(self, data): + self.mock_vyos.get_config.return_value = data + + +class TestVyOSNtpGlobalNormalize(unittest.TestCase): + """Test normalize_config and normalize_servers — no device needed.""" + + def test_normalize_config_empty(self): + result = normalize_config({}) + self.assertEqual(result["allow_clients"], []) + self.assertEqual(result["listen_addresses"], []) + self.assertEqual(result["servers"], {}) + + def test_normalize_config_servers_sorted(self): + config = { + "servers": [ + {"server": "b.example.com", "options": ["prefer", "noselect"]}, + {"server": "a.example.com"}, + ], + } + result = normalize_config(config) + self.assertIn("a.example.com", result["servers"]) + self.assertIn("b.example.com", result["servers"]) + self.assertEqual(result["servers"]["b.example.com"], ["noselect", "prefer"]) + + def test_normalize_servers_dict_with_options(self): + raw = { + "time1.vyos.net": {}, + "203.0.113.0": {"prefer": {}}, + } + result = normalize_servers(raw) + self.assertEqual(result["time1.vyos.net"], []) + self.assertEqual(result["203.0.113.0"], ["prefer"]) + + def test_normalize_servers_list(self): + raw = ["time1.vyos.net", "time2.vyos.net"] + result = normalize_servers(raw) + self.assertEqual(result["time1.vyos.net"], []) + + def test_normalize_servers_string(self): + result = normalize_servers("time1.vyos.net") + self.assertEqual(result["time1.vyos.net"], []) + + +class TestVyOSNtpGlobalGetRunning(VyOSModuleTestCase): + """Test get_running_config parsing against fixture API responses.""" + + def setUp(self): + super().setUp() + self.fixture = load_fixture("ntp_global_running.json") + + def test_parses_allow_clients(self): + self.set_running_config(self.fixture) + result = get_running_config(self.mock_vyos) + self.assertIn("10.6.6.0/24", result["allow_clients"]) + + def test_parses_listen_addresses(self): + self.set_running_config(self.fixture) + result = get_running_config(self.mock_vyos) + self.assertIn("10.1.3.1", result["listen_addresses"]) + + def test_parses_servers(self): + self.set_running_config(self.fixture) + result = get_running_config(self.mock_vyos) + self.assertIn("time1.vyos.net", result["servers"]) + self.assertIn("203.0.113.0", result["servers"]) + self.assertIn("prefer", result["servers"]["203.0.113.0"]) + + def test_empty_config_returns_empty(self): + self.set_running_config({}) + result = get_running_config(self.mock_vyos) + self.assertEqual(result["allow_clients"], []) + self.assertEqual(result["servers"], {}) + + +class TestVyOSNtpGlobalBuildCommands(unittest.TestCase): + """Test build_commands diff logic — no device needed.""" + + def _have(self, **kwargs): + base = {"allow_clients": [], "listen_addresses": [], "servers": {}} + base.update(kwargs) + return base + + def _want(self, **kwargs): + return self._have(**kwargs) + + def test_merged_adds_new_server(self): + want = self._want(servers={"new.server.com": []}) + have = self._have(servers={}) + cmds = build_commands(want, have, "merged") + self.assertIn(("set", ["service", "ntp", "server", "new.server.com"]), cmds) + + def test_merged_idempotent_existing_server(self): + want = self._want(servers={"time1.vyos.net": []}) + have = self._have(servers={"time1.vyos.net": []}) + cmds = build_commands(want, have, "merged") + self.assertEqual(cmds, []) + + def test_merged_adds_server_option(self): + want = self._want(servers={"time1.vyos.net": ["prefer"]}) + have = self._have(servers={"time1.vyos.net": []}) + cmds = build_commands(want, have, "merged") + self.assertIn(("set", ["service", "ntp", "server", "time1.vyos.net", "prefer"]), cmds) + + def test_replaced_removes_extra_server(self): + want = self._want(servers={"time1.vyos.net": []}) + have = self._have(servers={"time1.vyos.net": [], "time2.vyos.net": []}) + cmds = build_commands(want, have, "replaced") + self.assertIn(("delete", ["service", "ntp", "server", "time2.vyos.net"]), cmds) + + def test_replaced_removes_extra_allow_client(self): + want = self._want(allow_clients=["10.1.0.0/24"]) + have = self._have(allow_clients=["10.1.0.0/24", "10.2.0.0/24"]) + cmds = build_commands(want, have, "replaced") + self.assertIn( + ("delete", ["service", "ntp", "allow-client", "address", "10.2.0.0/24"]), + cmds, + ) + + def test_deleted_removes_all(self): + have = self._have( + servers={"time1.vyos.net": []}, + allow_clients=["10.0.0.0/24"], + listen_addresses=["192.168.1.1"], + ) + cmds = build_commands({}, have, "deleted") + self.assertEqual(len(cmds), 1) + self.assertEqual(cmds[0], ("delete", ["service", "ntp"])) + + def test_deleted_idempotent_when_empty(self): + have = self._have(servers={}, allow_clients=[], listen_addresses=[]) + cmds = build_commands({}, have, "deleted") + self.assertEqual(cmds, []) + + def test_overridden_deletes_then_merges(self): + want = self._want(servers={"new.server.com": []}) + have = self._have(servers={"old.server.com": []}) + cmds = build_commands(want, have, "overridden") + ops_paths = [(c[0], c[1]) for c in cmds] + self.assertIn(("delete", ["service", "ntp", "server", "old.server.com"]), ops_paths) + self.assertIn(("set", ["service", "ntp", "server", "new.server.com"]), ops_paths) + self.assertNotIn(("delete", ["service", "ntp", "server"]), ops_paths) + + def test_no_commands_when_already_correct(self): + state = {"allow_clients": ["10.0.0.0/24"], "listen_addresses": [], "servers": {}} + cmds = build_commands(state, state, "merged") + self.assertEqual(cmds, []) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unit/modules/test_vyos_route_maps.py b/tests/unit/modules/test_vyos_route_maps.py new file mode 100644 index 0000000..97d814b --- /dev/null +++ b/tests/unit/modules/test_vyos_route_maps.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +import json +import os +import unittest + +from unittest.mock import MagicMock + +from ansible_collections.vyos.rest.plugins.modules.vyos_route_maps import ( + _want_to_api_match, + _want_to_api_set, + build_commands, + get_running_config, +) + + +def load_fixture(filename): + fixtures_dir = os.path.join(os.path.dirname(__file__), "..", "fixtures") + path = os.path.join(fixtures_dir, filename) + with open(path) as f: + return json.load(f) + + +class VyOSModuleTestCase(unittest.TestCase): + def setUp(self): + self.mock_vyos = MagicMock() + self.mock_vyos.get_config = MagicMock(return_value={}) + + def set_running_config(self, data): + self.mock_vyos.get_config.return_value = data + + +class TestVyOSRouteMapsGetRunning(VyOSModuleTestCase): + + def setUp(self): + super().setUp() + self.fixture = load_fixture("route_maps_running.json") + + def test_unwraps_route_map_nesting(self): + """API returns {"route-map": {"NAME": {...}}} — must unwrap.""" + self.set_running_config(self.fixture) + result = get_running_config(self.mock_vyos) + names = [e["route_map"] for e in result] + self.assertIn("RM-TEST-EXPORT-POLICY", names) + self.assertIn("rm1", names) + # "route-map" itself must NOT appear as a route map name + self.assertNotIn("route-map", names) + + def test_parses_rule_action(self): + self.set_running_config(self.fixture) + result = get_running_config(self.mock_vyos) + rm = next(e for e in result if e["route_map"] == "RM-TEST-EXPORT-POLICY") + rule = rm["entries"][0] + self.assertEqual(rule["action"], "permit") + self.assertEqual(rule["sequence"], 10) + + def test_parses_match_peer(self): + self.set_running_config(self.fixture) + result = get_running_config(self.mock_vyos) + rm = next(e for e in result if e["route_map"] == "RM-TEST-EXPORT-POLICY") + rule = rm["entries"][0] + self.assertEqual(rule["match"]["peer"], "192.0.2.32") + + def test_parses_set_fields(self): + self.set_running_config(self.fixture) + result = get_running_config(self.mock_vyos) + rm = next(e for e in result if e["route_map"] == "RM-TEST-EXPORT-POLICY") + rule = rm["entries"][0] + self.assertEqual(rule["set"]["metric"], "5") + self.assertEqual(rule["set"]["aggregator"]["as"], "100") + self.assertEqual(rule["set"]["as-path"]["exclude"], "111") + + def test_empty_returns_empty_list(self): + self.set_running_config({}) + result = get_running_config(self.mock_vyos) + self.assertEqual(result, []) + + +class TestVyOSRouteMapsWantToApi(unittest.TestCase): + + def test_as_path_exclude_nested(self): + """as_path_exclude maps to nested as-path.exclude.""" + result = _want_to_api_set({"as_path_exclude": "111"}) + self.assertEqual(result["as-path"]["exclude"], "111") + + def test_metric_flat(self): + result = _want_to_api_set({"metric": "5"}) + self.assertEqual(result["metric"], "5") + + def test_aggregator_as(self): + result = _want_to_api_set({"aggregator": {"as": 100}}) + self.assertEqual(result["aggregator"]["as"], "100") + + def test_aggregator_as_underscore(self): + """aggregator.as_ is an alias for aggregator.as.""" + result = _want_to_api_set({"aggregator": {"as_": 100}}) + self.assertEqual(result["aggregator"]["as"], "100") + + def test_large_community_presence_node(self): + result = _want_to_api_set({"large_community": "none"}) + self.assertEqual(result["large-community"], {"none": {}}) + + def test_match_peer(self): + result = _want_to_api_match({"peer": "192.0.2.32"}) + self.assertEqual(result["peer"], "192.0.2.32") + + +class TestVyOSRouteMapsBuildCommands(unittest.TestCase): + + def _have_empty(self): + return [] + + def _have_with_rm(self): + return [ + { + "route_map": "RM1", + "entries": [ + { + "sequence": 10, + "action": "permit", + "match": {"peer": "192.0.2.32"}, + "set": {"metric": "5", "as-path": {"exclude": "111"}}, + }, + ], + }, + ] + + def test_merged_adds_new_rm(self): + config = [ + { + "route_map": "RM-NEW", + "entries": [{"sequence": 10, "action": "permit"}], + }, + ] + cmds = build_commands(config, self._have_empty(), "merged") + paths = [c[1] for c in cmds] + self.assertIn(["policy", "route-map", "RM-NEW", "rule", "10", "action", "permit"], paths) + + def test_merged_idempotent(self): + config = [ + { + "route_map": "RM1", + "entries": [ + { + "sequence": 10, + "action": "permit", + "match": {"peer": "192.0.2.32"}, + "set": {"metric": "5", "as_path_exclude": "111"}, + }, + ], + }, + ] + cmds = build_commands(config, self._have_with_rm(), "merged") + self.assertEqual(cmds, []) + + def test_deleted_no_config_deletes_all(self): + cmds = build_commands([], self._have_with_rm(), "deleted") + self.assertIn(("delete", ["policy", "route-map"]), cmds) + + def test_deleted_with_config_deletes_named(self): + config = [{"route_map": "RM1"}] + cmds = build_commands(config, self._have_with_rm(), "deleted") + self.assertIn(("delete", ["policy", "route-map", "RM1"]), cmds) + + def test_replaced_deletes_then_resets(self): + config = [ + { + "route_map": "RM1", + "entries": [{"sequence": 10, "action": "deny"}], + }, + ] + cmds = build_commands(config, self._have_with_rm(), "replaced") + ops = [c[0] for c in cmds] + # delete must come before set + self.assertIn("delete", ops) + self.assertIn("set", ops) + delete_idx = ops.index("delete") + set_idx = ops.index("set") + self.assertLess(delete_idx, set_idx) + + def test_overridden_removes_extra_rm(self): + config = [{"route_map": "RM-NEW", "entries": []}] + have = self._have_with_rm() # has RM1 + cmds = build_commands(config, have, "overridden") + self.assertIn(("delete", ["policy", "route-map", "RM1"]), cmds) + + +if __name__ == "__main__": + unittest.main() |
