summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/test_config_diff.py11
-rw-r--r--src/tests/test_config_parser.py4
-rw-r--r--src/tests/test_configd_inspect.py210
-rw-r--r--src/tests/test_initial_setup.py4
-rw-r--r--src/tests/test_template.py9
-rw-r--r--src/tests/test_utils_network.py11
6 files changed, 166 insertions, 83 deletions
diff --git a/src/tests/test_config_diff.py b/src/tests/test_config_diff.py
index 39e17613a..4017fff4d 100644
--- a/src/tests/test_config_diff.py
+++ b/src/tests/test_config_diff.py
@@ -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_parser.py b/src/tests/test_config_parser.py
index 9a4f02859..1b4a57311 100644
--- a/src/tests/test_config_parser.py
+++ b/src/tests/test_config_parser.py
@@ -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..a0470221d 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 (C) 2020-2025 VyOS maintainers and contributors
#
# 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_initial_setup.py b/src/tests/test_initial_setup.py
index 4cd5fb169..7737f9df5 100644
--- a/src/tests/test_initial_setup.py
+++ b/src/tests/test_initial_setup.py
@@ -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_template.py b/src/tests/test_template.py
index 6377f6da5..7cae867a0 100644
--- a/src/tests/test_template.py
+++ b/src/tests/test_template.py
@@ -190,3 +190,12 @@ 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 internal_ports
+
+ with self.assertRaises(RuntimeError):
+ vyos.template.get_default_port('UNKNOWN')
+
+ self.assertEqual(vyos.template.get_default_port('certbot_haproxy'),
+ internal_ports['certbot_haproxy'])
diff --git a/src/tests/test_utils_network.py b/src/tests/test_utils_network.py
index d68dec16f..92fde447d 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 (C) 2020-2025 VyOS maintainers and contributors
#
# 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'))