diff options
Diffstat (limited to 'src/tests')
-rw-r--r-- | src/tests/helper.py | 2 | ||||
-rw-r--r-- | src/tests/test_config_diff.py | 13 | ||||
-rw-r--r-- | src/tests/test_config_merge.py | 50 | ||||
-rw-r--r-- | src/tests/test_config_parser.py | 6 | ||||
-rw-r--r-- | src/tests/test_configd_inspect.py | 210 | ||||
-rw-r--r-- | src/tests/test_configverify.py | 2 | ||||
-rw-r--r-- | src/tests/test_dependency_graph.py | 2 | ||||
-rw-r--r-- | src/tests/test_dict_search.py | 2 | ||||
-rw-r--r-- | src/tests/test_find_device_file.py | 2 | ||||
-rw-r--r-- | src/tests/test_initial_setup.py | 6 | ||||
-rw-r--r-- | src/tests/test_op_mode.py | 2 | ||||
-rw-r--r-- | src/tests/test_task_scheduler.py | 2 | ||||
-rw-r--r-- | src/tests/test_template.py | 20 | ||||
-rw-r--r-- | src/tests/test_utils.py | 2 | ||||
-rw-r--r-- | src/tests/test_utils_network.py | 11 |
15 files changed, 237 insertions, 95 deletions
diff --git a/src/tests/helper.py b/src/tests/helper.py index cc0710494..2c4421d0a 100644 --- a/src/tests/helper.py +++ b/src/tests/helper.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/src/tests/test_config_diff.py b/src/tests/test_config_diff.py index 39e17613a..edbc0804e 100644 --- a/src/tests/test_config_diff.py +++ b/src/tests/test_config_diff.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -31,11 +31,11 @@ class TestConfigDiff(TestCase): def test_unit(self): diff = vyos.configtree.DiffTree(self.config_left, self.config_null) sub = diff.sub - self.assertEqual(sub.to_string(), self.config_left.to_string()) + self.assertEqual(sub, self.config_left) diff = vyos.configtree.DiffTree(self.config_null, self.config_left) add = diff.add - self.assertEqual(add.to_string(), self.config_left.to_string()) + self.assertEqual(add, self.config_left) def test_symmetry(self): lr_diff = vyos.configtree.DiffTree(self.config_left, @@ -45,10 +45,10 @@ class TestConfigDiff(TestCase): sub = lr_diff.sub add = rl_diff.add - self.assertEqual(sub.to_string(), add.to_string()) + self.assertEqual(sub, add) add = lr_diff.add sub = rl_diff.sub - self.assertEqual(add.to_string(), sub.to_string()) + self.assertEqual(add, sub) def test_identity(self): lr_diff = vyos.configtree.DiffTree(self.config_left, @@ -61,6 +61,9 @@ class TestConfigDiff(TestCase): r_union = vyos.configtree.union(add, inter) l_union = vyos.configtree.union(sub, inter) + # here we must compare string representations instead of using + # dunder equal, as we assert equivalence of the values list, which + # is optionally ordered at render self.assertEqual(r_union.to_string(), self.config_right.to_string(ordered_values=True)) self.assertEqual(l_union.to_string(), diff --git a/src/tests/test_config_merge.py b/src/tests/test_config_merge.py new file mode 100644 index 000000000..c9b4ad5c8 --- /dev/null +++ b/src/tests/test_config_merge.py @@ -0,0 +1,50 @@ +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import vyos.configtree + +from unittest import TestCase + +class TestConfigDiff(TestCase): + def setUp(self): + with open('tests/data/config.left', 'r') as f: + config_string = f.read() + self.config_left = vyos.configtree.ConfigTree(config_string) + + with open('tests/data/config.right', 'r') as f: + config_string = f.read() + self.config_right = vyos.configtree.ConfigTree(config_string) + + def test_merge_destructive(self): + res = vyos.configtree.merge(self.config_left, self.config_right, + destructive=True) + right_value = self.config_right.return_value(['node1', 'tag_node', 'foo', 'single']) + merge_value = res.return_value(['node1', 'tag_node', 'foo', 'single']) + + # Check includes new value + self.assertEqual(right_value, merge_value) + + # Check preserves non-confliciting paths + self.assertTrue(res.exists(['node3'])) + + def test_merge_non_destructive(self): + res = vyos.configtree.merge(self.config_left, self.config_right) + left_value = self.config_left.return_value(['node1', 'tag_node', 'foo', 'single']) + merge_value = res.return_value(['node1', 'tag_node', 'foo', 'single']) + + # Check includes original value + self.assertEqual(left_value, merge_value) + + # Check preserves non-confliciting paths + self.assertTrue(res.exists(['node3'])) diff --git a/src/tests/test_config_parser.py b/src/tests/test_config_parser.py index 9a4f02859..823d5a7c1 100644 --- a/src/tests/test_config_parser.py +++ b/src/tests/test_config_parser.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -51,3 +51,7 @@ class TestConfigParser(TestCase): def test_rename_duplicate(self): with self.assertRaises(vyos.configtree.ConfigTreeError): self.config.rename(["top-level-tag-node", "foo"], "bar") + + def test_leading_slashes(self): + self.assertTrue(self.config.exists(["normal-node", "value-with-leading-slashes"])) + self.assertEqual(self.config.return_value(["normal-node", "value-with-leading-slashes"]), "//other-value") diff --git a/src/tests/test_configd_inspect.py b/src/tests/test_configd_inspect.py index ccd631893..3363ab653 100644 --- a/src/tests/test_configd_inspect.py +++ b/src/tests/test_configd_inspect.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -12,93 +12,151 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import os -import re +import ast import json -import warnings -import importlib.util -from inspect import signature -from inspect import getsource -from functools import wraps from unittest import TestCase INC_FILE = 'data/configd-include.json' CONF_DIR = 'src/conf_mode' -f_list = ['get_config', 'verify', 'generate', 'apply'] - -def import_script(s): - path = os.path.join(CONF_DIR, s) - name = os.path.splitext(s)[0].replace('-', '_') - spec = importlib.util.spec_from_file_location(name, path) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - return module - -# importing conf_mode scripts imports jinja2 with deprecation warning -def ignore_deprecation_warning(f): - @wraps(f) - def decorated_function(*args, **kwargs): - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - f(*args, **kwargs) - return decorated_function +funcs = ['get_config', 'verify', 'generate', 'apply'] + + +class FunctionSig(ast.NodeVisitor): + def __init__(self): + self.func_sig_len = dict.fromkeys(funcs, None) + self.get_config_default_values = [] + + def visit_FunctionDef(self, node): + func_name = node.name + if func_name in funcs: + self.func_sig_len[func_name] = len(node.args.args) + + if func_name == 'get_config': + for default in node.args.defaults: + if isinstance(default, ast.Constant): + self.get_config_default_values.append(default.value) + + self.generic_visit(node) + + def get_sig_lengths(self): + return self.func_sig_len + + def get_config_default(self): + return self.get_config_default_values[0] + + +class LegacyCall(ast.NodeVisitor): + def __init__(self): + self.legacy_func_count = 0 + + def visit_Constant(self, node): + value = node.value + if isinstance(value, str): + if 'my_set' in value or 'my_delete' in value: + self.legacy_func_count += 1 + + self.generic_visit(node) + + def get_legacy_func_count(self): + return self.legacy_func_count + + +class ConfigInstance(ast.NodeVisitor): + def __init__(self): + self.count = 0 + + def visit_Call(self, node): + if isinstance(node.func, ast.Name): + name = node.func.id + if name == 'Config': + self.count += 1 + self.generic_visit(node) + + def get_count(self): + return self.count + + +class FunctionConfigInstance(ast.NodeVisitor): + def __init__(self): + self.func_config_instance = dict.fromkeys(funcs, 0) + + def visit_FunctionDef(self, node): + func_name = node.name + if func_name in funcs: + config_instance = ConfigInstance() + config_instance.visit(node) + self.func_config_instance[func_name] = config_instance.get_count() + self.generic_visit(node) + + def get_func_config_instance(self): + return self.func_config_instance + class TestConfigdInspect(TestCase): def setUp(self): + self.ast_list = [] + with open(INC_FILE) as f: self.inc_list = json.load(f) - @ignore_deprecation_warning - def test_signatures(self): for s in self.inc_list: - m = import_script(s) - for i in f_list: - f = getattr(m, i, None) - self.assertIsNotNone(f, f"'{s}': missing function '{i}'") - sig = signature(f) - par = sig.parameters - l = len(par) - self.assertEqual(l, 1, - f"'{s}': '{i}' incorrect signature") - if i == 'get_config': - for p in par.values(): - self.assertTrue(p.default is None, - f"'{s}': '{i}' incorrect signature") - - @ignore_deprecation_warning - def test_function_instance(self): - for s in self.inc_list: - m = import_script(s) - for i in f_list: - f = getattr(m, i, None) - if not f: - continue - str_f = getsource(f) - # Regex not XXXConfig() T3108 - n = len(re.findall(r'[^a-zA-Z]Config\(\)', str_f)) - if i == 'get_config': - self.assertEqual(n, 1, - f"'{s}': '{i}' no instance of Config") - if i != 'get_config': - self.assertEqual(n, 0, - f"'{s}': '{i}' instance of Config") - - @ignore_deprecation_warning - def test_file_instance(self): - for s in self.inc_list: - m = import_script(s) - str_m = getsource(m) - # Regex not XXXConfig T3108 - n = len(re.findall(r'[^a-zA-Z]Config\(\)', str_m)) - self.assertEqual(n, 1, - f"'{s}' more than one instance of Config") - - @ignore_deprecation_warning + s_path = f'{CONF_DIR}/{s}' + with open(s_path) as f: + s_str = f.read() + s_tree = ast.parse(s_str) + self.ast_list.append((s, s_tree)) + + def test_signatures(self): + for s, t in self.ast_list: + visitor = FunctionSig() + visitor.visit(t) + sig_lens = visitor.get_sig_lengths() + + for f in funcs: + self.assertIsNotNone(sig_lens[f], f"'{s}': '{f}' missing") + self.assertEqual(sig_lens[f], 1, f"'{s}': '{f}' incorrect signature") + + self.assertEqual( + visitor.get_config_default(), + None, + f"'{s}': 'get_config' incorrect signature", + ) + + def test_file_config_instance(self): + for s, t in self.ast_list: + visitor = ConfigInstance() + visitor.visit(t) + count = visitor.get_count() + + self.assertEqual(count, 1, f"'{s}' more than one instance of Config") + + def test_function_config_instance(self): + for s, t in self.ast_list: + visitor = FunctionConfigInstance() + visitor.visit(t) + func_config_instance = visitor.get_func_config_instance() + + for f in funcs: + if f == 'get_config': + self.assertTrue( + func_config_instance[f] > 0, + f"'{s}': '{f}' no instance of Config", + ) + self.assertTrue( + func_config_instance[f] < 2, + f"'{s}': '{f}' more than one instance of Config", + ) + else: + self.assertEqual( + func_config_instance[f], 0, f"'{s}': '{f}' instance of Config" + ) + def test_config_modification(self): - for s in self.inc_list: - m = import_script(s) - str_m = getsource(m) - n = str_m.count('my_set') - self.assertEqual(n, 0, f"'{s}' modifies config") + for s, t in self.ast_list: + visitor = LegacyCall() + visitor.visit(t) + legacy_func_count = visitor.get_legacy_func_count() + + self.assertEqual(legacy_func_count, 0, f"'{s}' modifies config") diff --git a/src/tests/test_configverify.py b/src/tests/test_configverify.py index f1ec65cd2..8f80365d7 100644 --- a/src/tests/test_configverify.py +++ b/src/tests/test_configverify.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/src/tests/test_dependency_graph.py b/src/tests/test_dependency_graph.py index f3f1db376..12f50a1c4 100644 --- a/src/tests/test_dependency_graph.py +++ b/src/tests/test_dependency_graph.py @@ -1,4 +1,4 @@ -# Copyright (C) 2023-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/src/tests/test_dict_search.py b/src/tests/test_dict_search.py index 6b4bc933a..ed147ec01 100644 --- a/src/tests/test_dict_search.py +++ b/src/tests/test_dict_search.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/src/tests/test_find_device_file.py b/src/tests/test_find_device_file.py index 5b90f2034..02a33c224 100644 --- a/src/tests/test_find_device_file.py +++ b/src/tests/test_find_device_file.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/src/tests/test_initial_setup.py b/src/tests/test_initial_setup.py index 4cd5fb169..09feea7ba 100644 --- a/src/tests/test_initial_setup.py +++ b/src/tests/test_initial_setup.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -92,8 +92,8 @@ class TestInitialSetup(TestCase): vis.set_default_gateway(self.config, '192.0.2.1') self.assertTrue(self.config.exists(['protocols', 'static', 'route', '0.0.0.0/0', 'next-hop', '192.0.2.1'])) - self.assertTrue(self.xml.is_tag(['protocols', 'static', 'multicast', 'route', '0.0.0.0/0', 'next-hop'])) - self.assertTrue(self.xml.is_tag(['protocols', 'static', 'multicast', 'route'])) + self.assertTrue(self.xml.is_tag(['protocols', 'static', 'mroute', '0.0.0.0/0', 'next-hop'])) + self.assertTrue(self.xml.is_tag(['protocols', 'static', 'mroute'])) if __name__ == "__main__": unittest.main() diff --git a/src/tests/test_op_mode.py b/src/tests/test_op_mode.py index 23f709653..848a9666d 100644 --- a/src/tests/test_op_mode.py +++ b/src/tests/test_op_mode.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/src/tests/test_task_scheduler.py b/src/tests/test_task_scheduler.py index 795ffeb9d..0d0319495 100644 --- a/src/tests/test_task_scheduler.py +++ b/src/tests/test_task_scheduler.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/src/tests/test_template.py b/src/tests/test_template.py index 6377f6da5..e2548602d 100644 --- a/src/tests/test_template.py +++ b/src/tests/test_template.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -190,3 +190,21 @@ class TestVyOSTemplate(TestCase): for group_name, group_config in data['ike_group'].items(): ciphers = vyos.template.get_esp_ike_cipher(group_config) self.assertIn(IKEv2_DEFAULT, ','.join(ciphers)) + + def test_get_default_port(self): + from vyos.defaults import config_files + from vyos.defaults import internal_ports + + with self.assertRaises(RuntimeError): + vyos.template.get_default_config_file('UNKNOWN') + with self.assertRaises(RuntimeError): + vyos.template.get_default_port('UNKNOWN') + with self.assertRaises(RuntimeError): + vyos.template.nft_accept_invalid('UNKNOWN') + + self.assertEqual(vyos.template.get_default_config_file('sshd_user_ca'), + config_files['sshd_user_ca']) + self.assertEqual(vyos.template.get_default_port('certbot_haproxy'), + internal_ports['certbot_haproxy']) + self.assertEqual(vyos.template.nft_accept_invalid('arp'), + 'ct state invalid ether type arp counter accept') diff --git a/src/tests/test_utils.py b/src/tests/test_utils.py index 7bfd2618e..5d642f4ea 100644 --- a/src/tests/test_utils.py +++ b/src/tests/test_utils.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as diff --git a/src/tests/test_utils_network.py b/src/tests/test_utils_network.py index d68dec16f..6d9a358c1 100644 --- a/src/tests/test_utils_network.py +++ b/src/tests/test_utils_network.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2024 VyOS maintainers and contributors +# Copyright VyOS maintainers and contributors <maintainers@vyos.io> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -43,3 +43,12 @@ class TestVyOSUtilsNetwork(TestCase): self.assertFalse(vyos.utils.network.is_loopback_addr('::2')) self.assertFalse(vyos.utils.network.is_loopback_addr('192.0.2.1')) + + def test_check_port_availability(self): + self.assertTrue(vyos.utils.network.check_port_availability('::1', 8080)) + self.assertTrue(vyos.utils.network.check_port_availability('127.0.0.1', 8080)) + self.assertTrue(vyos.utils.network.check_port_availability(None, 8080, protocol='udp')) + # We do not have 192.0.2.1 configured on this system + self.assertFalse(vyos.utils.network.check_port_availability('192.0.2.1', 443)) + # We do not have 2001:db8::1 configured on this system + self.assertFalse(vyos.utils.network.check_port_availability('2001:db8::1', 80, protocol='udp')) |