diff --git a/src/tests/helper.py b/src/tests/helper.py
index f7033148a..cc0710494 100644
--- a/src/tests/helper.py
+++ b/src/tests/helper.py
@@ -1,24 +1,22 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-2024 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
 # 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 sys
 import importlib.util
 
 def prepare_module(file_path='', module_name=''):
     spec = importlib.util.spec_from_file_location(module_name, file_path)
     module = importlib.util.module_from_spec(spec)
     spec.loader.exec_module(module)
     sys.modules[module_name] = module
diff --git a/src/tests/test_config_diff.py b/src/tests/test_config_diff.py
index 61a2f3487..39e17613a 100644
--- a/src/tests/test_config_diff.py
+++ b/src/tests/test_config_diff.py
@@ -1,69 +1,67 @@
-#!/usr/bin/env python3
-#
 # Copyright (C) 2023-2024 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
 # 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)
 
         self.config_null = vyos.configtree.ConfigTree('')
 
     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())
 
         diff = vyos.configtree.DiffTree(self.config_null, self.config_left)
         add = diff.add
         self.assertEqual(add.to_string(), self.config_left.to_string())
 
     def test_symmetry(self):
         lr_diff = vyos.configtree.DiffTree(self.config_left,
                                            self.config_right)
         rl_diff = vyos.configtree.DiffTree(self.config_right,
                                            self.config_left)
 
         sub = lr_diff.sub
         add = rl_diff.add
         self.assertEqual(sub.to_string(), add.to_string())
         add = lr_diff.add
         sub = rl_diff.sub
         self.assertEqual(add.to_string(), sub.to_string())
 
     def test_identity(self):
         lr_diff = vyos.configtree.DiffTree(self.config_left,
                                            self.config_right)
 
         sub = lr_diff.sub
         inter = lr_diff.inter
         add = lr_diff.add
 
         r_union = vyos.configtree.union(add, inter)
         l_union = vyos.configtree.union(sub, inter)
 
         self.assertEqual(r_union.to_string(),
                          self.config_right.to_string(ordered_values=True))
         self.assertEqual(l_union.to_string(),
                          self.config_left.to_string(ordered_values=True))
diff --git a/src/tests/test_config_parser.py b/src/tests/test_config_parser.py
index c69732daa..9a4f02859 100644
--- a/src/tests/test_config_parser.py
+++ b/src/tests/test_config_parser.py
@@ -1,55 +1,53 @@
-#!/usr/bin/env python3
-#
 # Copyright (C) 2018-2024 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
 # 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 TestConfigParser(TestCase):
     def setUp(self):
         with open('tests/data/config.valid', 'r') as f:
             config_string = f.read()
             self.config = vyos.configtree.ConfigTree(config_string)
 
     def test_top_level_valueless(self):
         self.assertTrue(self.config.exists(["top-level-valueless-node"]))
 
     def test_top_level_leaf(self):
         self.assertTrue(self.config.exists(["top-level-leaf-node"]))
         self.assertEqual(self.config.return_value(["top-level-leaf-node"]), "foo")
 
     def test_top_level_tag(self):
         self.assertTrue(self.config.exists(["top-level-tag-node"]))
         # Sorting is now intentional, during parsing of config
         self.assertEqual(self.config.list_nodes(["top-level-tag-node"]), ["bar", "foo"])
 
     def test_copy(self):
         self.config.copy(["top-level-tag-node", "bar"], ["top-level-tag-node", "baz"])
         print(self.config.to_string())
         self.assertTrue(self.config.exists(["top-level-tag-node", "baz"]))
 
     def test_copy_duplicate(self):
         with self.assertRaises(vyos.configtree.ConfigTreeError):
             self.config.copy(["top-level-tag-node", "foo"], ["top-level-tag-node", "bar"])
 
     def test_rename(self):
         self.config.rename(["top-level-tag-node", "bar"], "quux")
         print(self.config.to_string())
         self.assertTrue(self.config.exists(["top-level-tag-node", "quux"]))
 
     def test_rename_duplicate(self):
         with self.assertRaises(vyos.configtree.ConfigTreeError):
             self.config.rename(["top-level-tag-node", "foo"], "bar")
diff --git a/src/tests/test_configverify.py b/src/tests/test_configverify.py
index 15ccdf13d..f1ec65cd2 100644
--- a/src/tests/test_configverify.py
+++ b/src/tests/test_configverify.py
@@ -1,33 +1,31 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 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
 # 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/>.
 
 from unittest import TestCase
 from vyos.configverify import verify_diffie_hellman_length
 from vyos.utils.process import cmd
 
 dh_file = '/tmp/dh.pem'
 
 class TestDictSearch(TestCase):
     def setUp(self):
         pass
 
     def test_dh_key_none(self):
         self.assertFalse(verify_diffie_hellman_length('/tmp/non_existing_file', '1024'))
 
     def test_dh_key_512(self):
         key_len = '512'
         cmd(f'openssl dhparam -out {dh_file} {key_len}')
         self.assertTrue(verify_diffie_hellman_length(dh_file, key_len))
diff --git a/src/tests/test_dependency_graph.py b/src/tests/test_dependency_graph.py
index f682e87bb..f3f1db376 100644
--- a/src/tests/test_dependency_graph.py
+++ b/src/tests/test_dependency_graph.py
@@ -1,31 +1,29 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2023 VyOS maintainers and contributors
+# Copyright (C) 2023-2024 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
 # 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 os
 from vyos.configdep import check_dependency_graph
 
 _here = os.path.dirname(__file__)
 ddir = os.path.join(_here, '../../data/config-mode-dependencies')
 
 from unittest import TestCase
 
 class TestDependencyGraph(TestCase):
     def setUp(self):
         pass
 
     def test_acyclic(self):
         res = check_dependency_graph(dependency_dir=ddir)
         self.assertTrue(res)
diff --git a/src/tests/test_dict_search.py b/src/tests/test_dict_search.py
index 2435d89c7..6b4bc933a 100644
--- a/src/tests/test_dict_search.py
+++ b/src/tests/test_dict_search.py
@@ -1,84 +1,82 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 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
 # 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/>.
 
 from unittest import TestCase
 from vyos.utils.dict import dict_search
 from vyos.utils.dict import dict_search_recursive
 
 data = {
     'string': 'fooo',
     'nested': {'string': 'bar', 'empty': '', 'list': ['foo', 'bar']},
     'non': {},
     'list': ['bar', 'baz'],
     'dict': {'key_1': {}, 'key_2': 'vyos'},
     'interfaces': {'dummy': {'dum0': {'address': ['192.0.2.17/29']}},
                 'ethernet': {'eth0': {'address': ['2001:db8::1/64', '192.0.2.1/29'],
                                       'description': 'Test123',
                                       'duplex': 'auto',
                                       'hw_id': '00:00:00:00:00:01',
                                       'speed': 'auto'},
                              'eth1': {'address': ['192.0.2.9/29'],
                                       'description': 'Test456',
                                       'duplex': 'auto',
                                       'hw_id': '00:00:00:00:00:02',
                                       'speed': 'auto'}}}
 }
 
 class TestDictSearch(TestCase):
     def setUp(self):
         pass
 
     def test_non_existing_keys(self):
         # TestDictSearch: Return False when querying for non-existent key
         self.assertEqual(dict_search('non_existing', data), None)
         self.assertEqual(dict_search('non.existing.fancy.key', data), None)
 
     def test_string(self):
         # TestDictSearch: Return value when querying string
         self.assertEqual(dict_search('string', data), data['string'])
 
     def test_list(self):
         # TestDictSearch: Return list items when querying list
         self.assertEqual(dict_search('list', data), data['list'])
 
     def test_dict_key_value(self):
         # TestDictSearch: Return dictionary keys value when value is present
         self.assertEqual(dict_search('dict.key_2', data), data['dict']['key_2'])
 
     def test_nested_dict_key_value(self):
         # TestDictSearch: Return string value of last key when querying for a nested string
         self.assertEqual(dict_search('nested.string', data), data['nested']['string'])
 
     def test_nested_dict_key_empty(self):
         # TestDictSearch: Return False when querying for a nested string whose last key is empty
         self.assertEqual(dict_search('nested.empty', data), '')
         self.assertFalse(dict_search('nested.empty', data))
 
     def test_nested_list(self):
         # TestDictSearch: Return list items when querying nested list
         self.assertEqual(dict_search('nested.list', data), data['nested']['list'])
 
     def test_invalid_input(self):
         # TestDictSearch: Return list items when querying nested list
         self.assertEqual(dict_search('nested.list', None), None)
         self.assertEqual(dict_search(None, data), None)
 
     def test_dict_search_recursive(self):
         # Test nested search in dictionary
         tmp = list(dict_search_recursive(data, 'hw_id'))
         self.assertEqual(len(tmp), 2)
         tmp = list(dict_search_recursive(data, 'address'))
         self.assertEqual(len(tmp), 3)
diff --git a/src/tests/test_find_device_file.py b/src/tests/test_find_device_file.py
old mode 100755
new mode 100644
index f18043d65..21fc113f9
--- a/src/tests/test_find_device_file.py
+++ b/src/tests/test_find_device_file.py
@@ -1,35 +1,33 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 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
 # 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/>.
 
 from unittest import TestCase
 from vyos.utils.system import find_device_file
 
 class TestDeviceFile(TestCase):
     """ used to find USB devices on target """
     def setUp(self):
         pass
 
     def test_null(self):
         self.assertEqual(find_device_file('null'), '/dev/null')
 
     def test_zero(self):
         self.assertEqual(find_device_file('zero'), '/dev/zero')
 
     def test_input_event(self):
         self.assertEqual(find_device_file('event0'), '/dev/input/event0')
 
     def test_non_existing(self):
         self.assertFalse(find_device_file('vyos'))
diff --git a/src/tests/test_initial_setup.py b/src/tests/test_initial_setup.py
index f85bf1265..4cd5fb169 100644
--- a/src/tests/test_initial_setup.py
+++ b/src/tests/test_initial_setup.py
@@ -1,101 +1,99 @@
-#!/usr/bin/env python3
-#
 # Copyright (C) 2018-2024 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
 # 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 unittest
 import vyos.configtree
 import vyos.initialsetup as vis
 
 from unittest import TestCase
 from vyos.xml_ref import definition
 from vyos.xml_ref.pkg_cache.vyos_1x_cache import reference
 
 class TestInitialSetup(TestCase):
     def setUp(self):
         with open('tests/data/config.boot.default', 'r') as f:
             config_string = f.read()
             self.config = vyos.configtree.ConfigTree(config_string)
             self.xml = definition.Xml()
             self.xml.define(reference)
 
     def test_set_user_password(self):
         vis.set_user_password(self.config, 'vyos', 'vyosvyos')
 
         # Old password hash from the default config
         old_pw = '$6$QxPS.uk6mfo$9QBSo8u1FkH16gMyAVhus6fU3LOzvLR9Z9.82m3tiHFAxTtIkhaZSWssSgzt4v4dGAL8rhVQxTg0oAG9/q11h/'
         new_pw = self.config.return_value(["system", "login", "user", "vyos", "authentication", "encrypted-password"])
 
         # Just check it changed the hash, don't try to check if hash is good
         self.assertNotEqual(old_pw, new_pw)
 
     def test_disable_user_password(self):
         vis.disable_user_password(self.config, 'vyos')
         new_pw = self.config.return_value(["system", "login", "user", "vyos", "authentication", "encrypted-password"])
 
         self.assertEqual(new_pw, '!')
 
     def test_set_ssh_key_with_name(self):
         test_ssh_key = " ssh-rsa fakedata vyos@vyos "
         vis.set_user_ssh_key(self.config, 'vyos', test_ssh_key)
 
         key_type = self.config.return_value(["system", "login", "user", "vyos", "authentication", "public-keys", "vyos@vyos", "type"])
         key_data = self.config.return_value(["system", "login", "user", "vyos", "authentication", "public-keys", "vyos@vyos", "key"])
 
         self.assertEqual(key_type, 'ssh-rsa')
         self.assertEqual(key_data, 'fakedata')
         self.assertTrue(self.xml.is_tag(["system", "login", "user", "vyos", "authentication", "public-keys"]))
 
     def test_set_ssh_key_without_name(self):
         # If key file doesn't include a name, the function will use user name for the key name
 
         test_ssh_key = " ssh-rsa fakedata  "
         vis.set_user_ssh_key(self.config, 'vyos', test_ssh_key)
 
         key_type = self.config.return_value(["system", "login", "user", "vyos", "authentication", "public-keys", "vyos", "type"])
         key_data = self.config.return_value(["system", "login", "user", "vyos", "authentication", "public-keys", "vyos", "key"])
 
         self.assertEqual(key_type, 'ssh-rsa')
         self.assertEqual(key_data, 'fakedata')
         self.assertTrue(self.xml.is_tag(["system", "login", "user", "vyos", "authentication", "public-keys"]))
 
     def test_create_user(self):
         vis.create_user(self.config, 'jrandomhacker', password='qwerty', key=" ssh-rsa fakedata jrandomhacker@foovax ")
 
         self.assertTrue(self.config.exists(["system", "login", "user", "jrandomhacker"]))
         self.assertTrue(self.config.exists(["system", "login", "user", "jrandomhacker", "authentication", "public-keys", "jrandomhacker@foovax"]))
         self.assertTrue(self.config.exists(["system", "login", "user", "jrandomhacker", "authentication", "encrypted-password"]))
         self.assertEqual(self.config.return_value(["system", "login", "user", "jrandomhacker", "level"]), "admin")
 
     def test_set_hostname(self):
         vis.set_host_name(self.config, "vyos-test")
 
         self.assertEqual(self.config.return_value(["system", "host-name"]), "vyos-test")
 
     def test_set_name_servers(self):
         vis.set_name_servers(self.config, ["192.0.2.10", "203.0.113.20"])
         servers = self.config.return_values(["system", "name-server"])
 
         self.assertIn("192.0.2.10", servers)
         self.assertIn("203.0.113.20", servers)
 
     def test_set_gateway(self):
         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']))
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/src/tests/test_op_mode.py b/src/tests/test_op_mode.py
index 90963b3c5..23f709653 100644
--- a/src/tests/test_op_mode.py
+++ b/src/tests/test_op_mode.py
@@ -1,65 +1,62 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-2024 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
 # 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/>.
 
 from unittest import TestCase
 
 import vyos.opmode
 
 class TestVyOSOpMode(TestCase):
     def test_field_name_normalization(self):
         from vyos.opmode import _normalize_field_name
 
         self.assertEqual(_normalize_field_name(" foo bar "), "foo_bar")
         self.assertEqual(_normalize_field_name("foo-bar"), "foo_bar")
         self.assertEqual(_normalize_field_name("foo (bar) baz"), "foo_bar_baz")
         self.assertEqual(_normalize_field_name("load%"), "load_percentage")
 
     def test_dict_fields_normalization_non_unique(self):
         from vyos.opmode import _normalize_field_names
 
         # Space and dot are both replaced by an underscore,
         # so dicts like this cannor be normalized uniquely
         data = {"foo bar": True, "foo.bar": False}
 
         with self.assertRaises(vyos.opmode.InternalError):
             _normalize_field_names(data)
 
     def test_dict_fields_normalization_simple_dict(self):
         from vyos.opmode import _normalize_field_names
 
         data = {"foo bar": True, "Bar-Baz": False}
         self.assertEqual(_normalize_field_names(data), {"foo_bar": True, "bar_baz": False})
 
     def test_dict_fields_normalization_nested_dict(self):
         from vyos.opmode import _normalize_field_names
 
         data = {"foo bar": True, "bar-baz": {"baz-quux": {"quux-xyzzy": False}}}
         self.assertEqual(_normalize_field_names(data),
           {"foo_bar": True, "bar_baz": {"baz_quux": {"quux_xyzzy": False}}})
 
     def test_dict_fields_normalization_mixed(self):
         from vyos.opmode import _normalize_field_names
 
         data = [{"foo bar": True, "bar-baz": [{"baz-quux": {"quux-xyzzy": [False]}}]}]
         self.assertEqual(_normalize_field_names(data),
           [{"foo_bar": True, "bar_baz": [{"baz_quux": {"quux_xyzzy": [False]}}]}])
 
     def test_dict_fields_normalization_primitive(self):
         from vyos.opmode import _normalize_field_names
 
         data = [1, False, "foo"]
         self.assertEqual(_normalize_field_names(data), [1, False, "foo"])
-
diff --git a/src/tests/test_task_scheduler.py b/src/tests/test_task_scheduler.py
index 130f825e6..795ffeb9d 100644
--- a/src/tests/test_task_scheduler.py
+++ b/src/tests/test_task_scheduler.py
@@ -1,129 +1,127 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018-2023 VyOS maintainers and contributors
+# Copyright (C) 2018-2024 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
 # 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 os
 import tempfile
 import unittest
 import importlib
 
 from vyos import ConfigError
 
 try:
     task_scheduler = importlib.import_module("src.conf_mode.system_task-scheduler")
 except ModuleNotFoundError:  # for unittest.main()
     import sys
     sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
     task_scheduler = importlib.import_module("src.conf_mode.system_task-scheduler")
 
 class TestUpdateCrontab(unittest.TestCase):
 
     def test_verify(self):
         tests = [
             {'name': 'one_task',
              'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': None
              },
             {'name': 'has_interval_and_spec',
              'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '0 * * * *', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': ConfigError
              },
             {'name': 'has_no_interval_and_spec',
              'tasks': [{'name': 'aaa', 'interval': '', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': ConfigError
              },
             {'name': 'invalid_interval',
              'tasks': [{'name': 'aaa', 'interval': '1y', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': ConfigError
              },
             {'name': 'invalid_interval_min',
              'tasks': [{'name': 'aaa', 'interval': '61m', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': ConfigError
              },
             {'name': 'invalid_interval_hour',
              'tasks': [{'name': 'aaa', 'interval': '25h', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': ConfigError
              },
             {'name': 'invalid_interval_day',
              'tasks': [{'name': 'aaa', 'interval': '32d', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': ConfigError
              },
             {'name': 'no_executable',
              'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '', 'args': ''}],
              'expected': ConfigError
              },
             {'name': 'invalid_executable',
              'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '/bin/aaa', 'args': ''}],
              'expected': ConfigError
              }
         ]
         for t in tests:
             with self.subTest(msg=t['name'], tasks=t['tasks'], expected=t['expected']):
                 if t['expected'] is not None:
                     with self.assertRaises(t['expected']):
                         task_scheduler.verify(t['tasks'])
                 else:
                     task_scheduler.verify(t['tasks'])
 
     def test_generate(self):
         tests = [
             {'name': 'zero_task',
              'tasks': [],
              'expected': []
              },
             {'name': 'one_task',
              'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': [
                  '### Generated by vyos-update-crontab.py ###',
                  '*/60 * * * * root sg vyattacfg \"/bin/ls -l\"']
              },
             {'name': 'one_task_with_hour',
              'tasks': [{'name': 'aaa', 'interval': '10h', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': [
                  '### Generated by vyos-update-crontab.py ###',
                  '0 */10 * * * root sg vyattacfg \"/bin/ls -l\"']
              },
             {'name': 'one_task_with_day',
              'tasks': [{'name': 'aaa', 'interval': '10d', 'spec': '', 'executable': '/bin/ls', 'args': '-l'}],
              'expected': [
                  '### Generated by vyos-update-crontab.py ###',
                  '0 0 */10 * * root sg vyattacfg \"/bin/ls -l\"']
              },
             {'name': 'multiple_tasks',
              'tasks': [{'name': 'aaa', 'interval': '60m', 'spec': '', 'executable': '/bin/ls', 'args': '-l'},
                        {'name': 'bbb', 'interval': '', 'spec': '0 0 * * *', 'executable': '/bin/ls', 'args': '-ltr'}
                        ],
              'expected': [
                  '### Generated by vyos-update-crontab.py ###',
                  '*/60 * * * * root sg vyattacfg \"/bin/ls -l\"',
                  '0 0 * * * root sg vyattacfg \"/bin/ls -ltr\"']
              }
         ]
         for t in tests:
             with self.subTest(msg=t['name'], tasks=t['tasks'], expected=t['expected']):
                 task_scheduler.crontab_file = tempfile.mkstemp()[1]
                 task_scheduler.generate(t['tasks'])
                 if len(t['expected']) > 0:
                     self.assertTrue(os.path.isfile(task_scheduler.crontab_file))
                     with open(task_scheduler.crontab_file) as f:
                         actual = f.read()
                         self.assertEqual(t['expected'], actual.splitlines())
                     os.remove(task_scheduler.crontab_file)
                 else:
                     self.assertFalse(os.path.isfile(task_scheduler.crontab_file))
 
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/src/tests/test_template.py b/src/tests/test_template.py
index dbb86b40b..6377f6da5 100644
--- a/src/tests/test_template.py
+++ b/src/tests/test_template.py
@@ -1,194 +1,192 @@
-#!/usr/bin/env python3
-#
 # Copyright (C) 2020-2024 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
 # 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.template
 
 from vyos.utils.network import interface_exists
 from ipaddress import ip_network
 from unittest import TestCase
 
 class TestVyOSTemplate(TestCase):
     def setUp(self):
         pass
 
     def test_is_interface(self):
         for interface in ['lo', 'eth0']:
             if interface_exists(interface):
                 self.assertTrue(vyos.template.is_interface(interface))
             else:
                 self.assertFalse(vyos.template.is_interface(interface))
         self.assertFalse(vyos.template.is_interface('non-existent'))
 
     def test_is_ip(self):
         self.assertTrue(vyos.template.is_ip('192.0.2.1'))
         self.assertTrue(vyos.template.is_ip('2001:db8::1'))
         self.assertFalse(vyos.template.is_ip('VyOS'))
 
     def test_is_ipv4(self):
         self.assertTrue(vyos.template.is_ipv4('192.0.2.1'))
         self.assertTrue(vyos.template.is_ipv4('192.0.2.0/24'))
         self.assertTrue(vyos.template.is_ipv4('192.0.2.1/32'))
 
         self.assertFalse(vyos.template.is_ipv4('2001:db8::1'))
         self.assertFalse(vyos.template.is_ipv4('2001:db8::/64'))
         self.assertFalse(vyos.template.is_ipv4('VyOS'))
 
     def test_is_ipv6(self):
         self.assertTrue(vyos.template.is_ipv6('2001:db8::1'))
         self.assertTrue(vyos.template.is_ipv6('2001:db8::/64'))
         self.assertTrue(vyos.template.is_ipv6('2001:db8::1/64'))
 
         self.assertFalse(vyos.template.is_ipv6('192.0.2.1'))
         self.assertFalse(vyos.template.is_ipv6('192.0.2.0/24'))
         self.assertFalse(vyos.template.is_ipv6('192.0.2.1/32'))
         self.assertFalse(vyos.template.is_ipv6('VyOS'))
 
     def test_address_from_cidr(self):
         self.assertEqual(vyos.template.address_from_cidr('192.0.2.0/24'),  '192.0.2.0')
         self.assertEqual(vyos.template.address_from_cidr('2001:db8::/48'), '2001:db8::')
 
         with self.assertRaises(ValueError):
             # ValueError: 192.0.2.1/24 has host bits set
             self.assertEqual(vyos.template.address_from_cidr('192.0.2.1/24'),  '192.0.2.1')
 
         with self.assertRaises(ValueError):
             # ValueError: 2001:db8::1/48 has host bits set
             self.assertEqual(vyos.template.address_from_cidr('2001:db8::1/48'), '2001:db8::1')
 
         network_v4 = '192.0.2.0/26'
         self.assertEqual(vyos.template.address_from_cidr(network_v4), str(ip_network(network_v4).network_address))
 
     def test_netmask_from_cidr(self):
         self.assertEqual(vyos.template.netmask_from_cidr('192.0.2.0/24'),  '255.255.255.0')
         self.assertEqual(vyos.template.netmask_from_cidr('192.0.2.128/25'),  '255.255.255.128')
         self.assertEqual(vyos.template.netmask_from_cidr('2001:db8::/48'), 'ffff:ffff:ffff::')
 
         with self.assertRaises(ValueError):
             # ValueError: 192.0.2.1/24 has host bits set
             self.assertEqual(vyos.template.netmask_from_cidr('192.0.2.1/24'),  '255.255.255.0')
 
         with self.assertRaises(ValueError):
             # ValueError: 2001:db8:1:/64 has host bits set
             self.assertEqual(vyos.template.netmask_from_cidr('2001:db8:1:/64'), 'ffff:ffff:ffff:ffff::')
 
         network_v4 = '192.0.2.0/26'
         self.assertEqual(vyos.template.netmask_from_cidr(network_v4), str(ip_network(network_v4).netmask))
 
     def test_first_host_address(self):
         self.assertEqual(vyos.template.first_host_address('10.0.0.0/24'), '10.0.0.1')
         self.assertEqual(vyos.template.first_host_address('10.0.0.10/24'), '10.0.0.1')
         self.assertEqual(vyos.template.first_host_address('10.0.0.255/24'), '10.0.0.1')
         self.assertEqual(vyos.template.first_host_address('10.0.0.128/25'), '10.0.0.129')
         self.assertEqual(vyos.template.first_host_address('2001:db8::/64'), '2001:db8::1')
         self.assertEqual(vyos.template.first_host_address('2001:db8::1000/64'), '2001:db8::1')
         self.assertEqual(vyos.template.first_host_address('2001:db8::ffff:ffff:ffff:ffff/64'), '2001:db8::1')
 
     def test_last_host_address(self):
         self.assertEqual(vyos.template.last_host_address('10.0.0.0/24'), '10.0.0.254')
         self.assertEqual(vyos.template.last_host_address('10.0.0.128/25'), '10.0.0.254')
         self.assertEqual(vyos.template.last_host_address('2001:db8::/64'), '2001:db8::ffff:ffff:ffff:ffff')
 
     def test_increment_ip(self):
         self.assertEqual(vyos.template.inc_ip('10.0.0.0/24', '2'), '10.0.0.2')
         self.assertEqual(vyos.template.inc_ip('10.0.0.0', '2'), '10.0.0.2')
         self.assertEqual(vyos.template.inc_ip('10.0.0.0', '10'), '10.0.0.10')
         self.assertEqual(vyos.template.inc_ip('2001:db8::/64', '2'), '2001:db8::2')
         self.assertEqual(vyos.template.inc_ip('2001:db8::', '10'), '2001:db8::a')
 
     def test_decrement_ip(self):
         self.assertEqual(vyos.template.dec_ip('10.0.0.100/24', '1'), '10.0.0.99')
         self.assertEqual(vyos.template.dec_ip('10.0.0.90', '10'), '10.0.0.80')
         self.assertEqual(vyos.template.dec_ip('2001:db8::b/64', '10'), '2001:db8::1')
         self.assertEqual(vyos.template.dec_ip('2001:db8::f', '5'), '2001:db8::a')
 
     def test_is_network(self):
         self.assertFalse(vyos.template.is_ip_network('192.0.2.0'))
         self.assertFalse(vyos.template.is_ip_network('192.0.2.1/24'))
         self.assertTrue(vyos.template.is_ip_network('192.0.2.0/24'))
 
         self.assertFalse(vyos.template.is_ip_network('2001:db8::'))
         self.assertFalse(vyos.template.is_ip_network('2001:db8::ffff'))
         self.assertTrue(vyos.template.is_ip_network('2001:db8::/48'))
         self.assertTrue(vyos.template.is_ip_network('2001:db8:1000::/64'))
 
     def test_is_network(self):
         self.assertTrue(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/8'))
         self.assertTrue(vyos.template.compare_netmask('10.0.0.0/16', '20.0.0.0/16'))
         self.assertFalse(vyos.template.compare_netmask('10.0.0.0/8', '20.0.0.0/16'))
         self.assertFalse(vyos.template.compare_netmask('10.0.0.1', '20.0.0.0/16'))
 
         self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/48'))
         self.assertTrue(vyos.template.compare_netmask('2001:db8:1000::/64', '2001:db8:2000::/64'))
         self.assertFalse(vyos.template.compare_netmask('2001:db8:1000::/48', '2001:db8:2000::/64'))
 
     def test_cipher_to_string(self):
         ESP_DEFAULT = 'aes256gcm128-sha256-ecp256,aes128ccm64-sha256-ecp256'
         IKEv2_DEFAULT = 'aes256gcm128-sha256-ecp256,aes128ccm128-md5_128-modp1024'
 
         data = {
             'esp_group': {
                 'ESP_DEFAULT': {
                     'compression': 'disable',
                     'lifetime': '3600',
                     'mode': 'tunnel',
                     'pfs': 'dh-group19',
                     'proposal': {
                         '10': {
                             'encryption': 'aes256gcm128',
                             'hash': 'sha256',
                         },
                         '20': {
                             'encryption': 'aes128ccm64',
                             'hash': 'sha256',
                         }
                     }
                 }
             },
             'ike_group': {
                 'IKEv2_DEFAULT': {
                     'close_action': 'none',
                     'dead_peer_detection': {
                         'action': 'hold',
                         'interval': '30',
                         'timeout': '120'
                     },
                     'ikev2_reauth': 'no',
                     'key_exchange': 'ikev2',
                     'lifetime': '10800',
                     'mobike': 'disable',
                     'proposal': {
                         '10': {
                             'dh_group': '19',
                             'encryption': 'aes256gcm128',
                             'hash': 'sha256'
                         },
                         '20': {
                             'dh_group': '2',
                             'encryption': 'aes128ccm128',
                             'hash': 'md5_128'
                         },
                     }
                 }
             },
         }
 
         for group_name, group_config in data['esp_group'].items():
             ciphers = vyos.template.get_esp_ike_cipher(group_config)
             self.assertIn(ESP_DEFAULT, ','.join(ciphers))
 
         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))
diff --git a/src/tests/test_utils.py b/src/tests/test_utils.py
index 9ae329ced..7bfd2618e 100644
--- a/src/tests/test_utils.py
+++ b/src/tests/test_utils.py
@@ -1,28 +1,26 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 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
 # 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/>.
 
 from unittest import TestCase
 class TestVyOSUtils(TestCase):
     def test_key_mangling(self):
         from vyos.utils.dict import mangle_dict_keys
         data = {"foo-bar": {"baz-quux": None}}
         expected_data = {"foo_bar": {"baz_quux": None}}
         new_data = mangle_dict_keys(data, '-', '_')
         self.assertEqual(new_data, expected_data)
 
     def test_sysctl_read(self):
         from vyos.utils.system import sysctl_read
         self.assertEqual(sysctl_read('net.ipv4.conf.lo.forwarding'), '1')
diff --git a/src/tests/test_utils_network.py b/src/tests/test_utils_network.py
index 5a6dc2586..d68dec16f 100644
--- a/src/tests/test_utils_network.py
+++ b/src/tests/test_utils_network.py
@@ -1,50 +1,45 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2020-2023 VyOS maintainers and contributors
+# Copyright (C) 2020-2024 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
 # 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.utils.network
 from unittest import TestCase
 
 class TestVyOSUtilsNetwork(TestCase):
     def setUp(self):
         pass
 
     def test_is_addr_assigned(self):
         self.assertTrue(vyos.utils.network.is_addr_assigned('127.0.0.1'))
         self.assertTrue(vyos.utils.network.is_addr_assigned('::1'))
         self.assertFalse(vyos.utils.network.is_addr_assigned('127.251.255.123'))
 
     def test_is_ipv6_link_local(self):
         self.assertFalse(vyos.utils.network.is_ipv6_link_local('169.254.0.1'))
         self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::'))
         self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::affe:1'))
         self.assertTrue(vyos.utils.network.is_ipv6_link_local('fe80::affe:1%eth0'))
         self.assertFalse(vyos.utils.network.is_ipv6_link_local('2001:db8::'))
         self.assertFalse(vyos.utils.network.is_ipv6_link_local('2001:db8::%eth0'))
         self.assertFalse(vyos.utils.network.is_ipv6_link_local('VyOS'))
         self.assertFalse(vyos.utils.network.is_ipv6_link_local('::1'))
         self.assertFalse(vyos.utils.network.is_ipv6_link_local('::1%lo'))
 
     def test_is_ipv6_link_local(self):
         self.assertTrue(vyos.utils.network.is_loopback_addr('127.0.0.1'))
         self.assertTrue(vyos.utils.network.is_loopback_addr('127.0.1.1'))
         self.assertTrue(vyos.utils.network.is_loopback_addr('127.1.1.1'))
         self.assertTrue(vyos.utils.network.is_loopback_addr('::1'))
 
         self.assertFalse(vyos.utils.network.is_loopback_addr('::2'))
         self.assertFalse(vyos.utils.network.is_loopback_addr('192.0.2.1'))
-
-
-