# The software in this package is distributed under the GNU General # Public License version 2 (with a special exception described below). # # A copy of GNU General Public License (GPL) is included in this distribution, # in the file COPYING.GPL. # # As a special exception, if other files instantiate templates or use macros # or inline functions from this file, or you compile this file and link it # with other works to produce a work based on this file, this file # does not by itself cause the resulting work to be covered # by the GNU General Public License. # # However the source code for this file must still be made available # in accordance with section (3) of the GNU General Public License. # # This exception does not invalidate any other reasons why a work based # on this file might be covered by the GNU General Public License. # # Copyright (c) 2016-2022 Intra2net AG """ test_model.py: unit tests for cnfvar/model.py. Tests classes and functions in cnfvar/model.py For help see :py:mod:`unittest` """ import unittest import json from textwrap import dedent from tempfile import NamedTemporaryFile from copy import deepcopy from src.cnfvar import CnfList, CnfDiff CNF_TEST_DATA = dedent("""\ 1 USER,1: "jake" 2 (1) USER_DISABLED,0: "0" 3 (1) USER_FULLNAME,0: "Jake" 4 (1) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe" 5 (1) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte" 6 (1) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente" 7 (1) USER_GROUP_MEMBER_REF,0: "100" 8 (1) USER_GROUP_MEMBER_REF,1: "2" 9 (1) USER_PASSWORD,0: "test1234" 11 USER,2: "jill" 12 (11) USER_DISABLED,0: "0" 13 (11) USER_FULLNAME,0: "Jill" 14 (11) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe" 15 (11) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte" 16 (11) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente" 17 (11) USER_GROUP_MEMBER_REF,0: "100" 18 (11) USER_GROUP_MEMBER_REF,1: "2" 19 (11) USER_PASSWORD,0: "test1234" 74 EMAILFILTER_BAN_FILTERLIST,0: "Vordefiniert: Alles verboten" 75 (74) EMAILFILTER_BAN_FILTERLIST_ENCRYPTED,0: "BLOCK" 76 (74) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS,0: "" 77 (74) EMAILFILTER_BAN_FILTERLIST_FILTER_OFFICE_FILES,0: "BY_FILTERLIST" 78 (74) EMAILFILTER_BAN_FILTERLIST_MIMETYPES,0: "" 79 (78) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,0: "text/plain" 80 (78) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,1: "text/html" 81 (74) EMAILFILTER_BAN_FILTERLIST_MODE,0: "ALLOW" 82 (74) EMAILFILTER_BAN_FILTERLIST_PREDEFINED_ID,0: "1" 1196 UPDATE_EXPIRED,0: "0" 1197 UPDATE_URL_BASE,0: "https://update.intra2net.com/" 1198 UPDATE_VALIDATION_GROUP,0: "normal" 1199 UPS_LOCAL_KILLPOWER_ENABLE,0: "1" """) EXPECTED_CNF_DATA = dedent("""\ 1 USER,1: "jake" 2 (1) USER_DISABLED,0: "0" 3 (1) USER_FULLNAME,0: "Jake" 4 (1) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe" 5 (1) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte" 6 (1) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente" 7 (1) USER_GROUP_MEMBER_REF,0: "100" 8 (1) USER_GROUP_MEMBER_REF,1: "2" 9 (1) USER_PASSWORD,0: "test1234" 10 USER,2: "jane" 11 (10) USER_DISABLED,0: "0" 12 (10) USER_FULLNAME,0: "Jane" 13 (10) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe" 14 (10) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Objekte" 15 (10) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente" 16 (10) USER_GROUP_MEMBER_REF,0: "200" 17 (10) USER_GROUP_MEMBER_REF,1: "2" 18 (10) USER_PASSWORD,0: "test1234" 19 (10) USER_GROUP_MEMBER_REF,2: "5" 20 EMAILFILTER_BAN_FILTERLIST,0: "Vordefiniert: Alles verboten" 21 (20) EMAILFILTER_BAN_FILTERLIST_ENCRYPTED,0: "BLOCK" 22 (20) EMAILFILTER_BAN_FILTERLIST_EXTENSIONS,0: "" 23 (20) EMAILFILTER_BAN_FILTERLIST_FILTER_OFFICE_FILES,0: "BY_FILTERLIST" 24 (20) EMAILFILTER_BAN_FILTERLIST_MIMETYPES,0: "" # hey this is a comment 25 (24) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,0: "text/plain" 26 (24) EMAILFILTER_BAN_FILTERLIST_MIMETYPES_NAME,1: "text/html" 27 (20) EMAILFILTER_BAN_FILTERLIST_MODE,0: "ALLOW" 28 (20) EMAILFILTER_BAN_FILTERLIST_PREDEFINED_ID,0: "1" 29 UPDATE_EXPIRED,0: "1" 30 UPDATE_URL_BASE,0: "https://update.intra2net.com/" 31 UPDATE_VALIDATION_GROUP,0: "normal" 32 UPS_LOCAL_KILLPOWER_ENABLE,0: "1" """) class TestModel(unittest.TestCase): """Test the multiple capabilities of the CNF modules.""" def test_querying_and_serialization(self): """Test deserializing, querying and serializing a CnfList.""" cnfs = CnfList.from_cnf_string(CNF_TEST_DATA) self._modify(cnfs) with NamedTemporaryFile() as tmpfile: cnfs.to_cnf_file(tmpfile.name) with open(tmpfile.name, "r", encoding="latin1") as f: contents = f.read() self.assertEqual(contents.splitlines(), EXPECTED_CNF_DATA.splitlines()) # make sure the result can be parsed again CnfList.from_cnf_string(str(contents)) def test_querying_and_serialization_json(self): """Test deserializing, querying and serializing a CnfList as JSON.""" # first serialize to JSON without renumbering to keep the structure cnfs = CnfList.from_cnf_string(CNF_TEST_DATA) with NamedTemporaryFile() as tmpfile: cnfs.to_json_file(tmpfile.name, renumber=False) with open(tmpfile.name, "r") as f: contents = f.read() # make sure the JSON structure is sane json.loads(contents) # now asserts that reading from JSON works cnfs_2 = CnfList.from_json_string(contents) self._modify(cnfs_2) cnfs_2.renumber() self.assertEqual(str(cnfs_2).splitlines(), EXPECTED_CNF_DATA.splitlines()) # make sure the result can be parsed again CnfList.from_cnf_string(str(cnfs_2)) def _modify(self, cnfs): """ Make test modifications on the list. :param cnfs: list of cnfvars to modify :type cnfs: :py:class:`CnfList` .. note:: we unify this into a method so we can make sure that the test using cnfvar strings and the test using JSON do the exactly same modifications. """ self.assertFalse(cnfs.single_with_name("update_expired").is_enabled()) cnfs.single_with_name("update_expired") \ .enable() self.assertTrue(cnfs.single_with_name("update_expired").is_enabled()) user_cnf = cnfs.first_with_value("jill") user_cnf.value = "jane" user_cnf.children[1].value = "Jane" user_cnf.children.first_with_name("user_group_member_ref").value = "200" user_cnf.add_children(("USER_GROUP_MEMBER_REF", 5, -1)) # check correct types and equality self.assertEqual(user_cnf.instance, 2) self.assertEqual(user_cnf.lineno, 11) other_cnf = cnfs.single_with_value("Vordefiniert: Alles verboten") other_cnf.children \ .where(lambda c: c.name == "emailfilter_ban_filterlist_mimetypes") \ .single() \ .comment = "hey this is a comment" class TestCompare(unittest.TestCase): """Test compare() function of BaseCnfList.""" DATA_STR = dedent("""\ 01 FRANCHISE,0: "Star Trek" 02 (01) SERIES,0: "The Original Series" 03 (02) SHIP,0: "Enterprise (original)" 04 (03) CAPTAIN,0: "Pike" 05 (03) CAPTAIN,1: "Kirk" 06 (01) SERIES,1: "The Animated Series" 07 (01) SERIES,1: "The Next Generation" 08 (07) SHIP,0: "Enterprise D" 09 (08) CAPTAIN,0: "Picard" 11 (08) CAPTAIN,1: "Riker" 12 (01) SERIES,2: "Deep Space Nine" 13 (01) SERIES,3: "Voyager" 14 (13) SHIP,0: "Voyager" 15 (01) SERIES,4: "Enterprise" 16 (15) SHIP,0: "Enterprise" 17 (16) CAPTAIN,0: "Archer" 18 FRANCHISE,1: "Star Wars" 19 (18) MOVIE,0: "A New Hope" 21 (19) EPISODE,0: "4" 22 (18) MOVIE,1: "The Empire Strikes Back" 23 (22) EPISODE,0: "5" 24 (18) MOVIE,2: "Return of the Jedi" 25 (24) EPISODE,0: "6" 26 (18) MOVIE,3: "The Phantom Menace" 27 (26) EPISODE,0: "1" 28 (18) MOVIE,4: "Attack of the Clones" 29 (28) EPISODE,0: "2" 31 (18) MOVIE,5: "Revenge of the Sith" 32 (31) EPISODE,0: "3" 33 FRANCHISE,2: "Battlestar Galactica" 34 FRANCHISE,3: "Firefly" 35 FRANCHISE,4: "Babylon 5" 36 FRANCHISE,5: "Star Gate" """) DATA: CnfList # initialized in setUpClass @classmethod def setUpClass(cls) -> None: """Called once before first test, converts above string to CnfList.""" cls.DATA = CnfList.from_cnf_string(cls.DATA_STR) def test_equal(self): """Compare data to itself.""" self.assertEqual(CnfDiff(), self.DATA.compare(self.DATA)) def test_remove_child(self): """Remove child entry from data, check it is found.""" other = deepcopy(self.DATA) removed = other.single_with_value("Star Trek").children\ .single_with_value("The Original Series").children\ .single_with_value("Enterprise (original)").children \ .remove_where(lambda cnf: cnf.value == "Pike") self.assertEqual(1, len(removed)) diff = self.DATA.compare(other) self.assertEqual(1, len(diff)) diff_text = [] diff.print(output_func=lambda s: diff_text.append(s)) self.assertEqual(1, len(diff_text)) expect = "cnf diff: - captain (0) = 'Pike' " + \ "in franchise=Star Trek > series=The Original Series > ship=Enterprise (original)" self.assertEqual(expect, diff_text[0]) def test_remove_root(self): """Remove entry from data at root level, check it is found and properly textified""" other = deepcopy(self.DATA) removed = other.remove_where(lambda cnf: cnf.value == "Babylon 5") self.assertEqual(1, len(removed)) diff = self.DATA.compare(other) self.assertEqual(1, len(diff)) diff_text = [] diff.print(output_func=lambda s: diff_text.append(s)) self.assertEqual(1, len(diff_text)) expect = "cnf diff: - franchise (4) = 'Babylon 5' " self.assertEqual(expect, diff_text[0]) def test_add(self): """Add entry to data, check it is found.""" other = deepcopy(self.DATA) other.single_with_value("Star Wars").children.single_with_value("The Phantom Menace")\ .add_child("CHARACTER", "Jar Jar Binks") diff = self.DATA.compare(other) self.assertEqual(1, len(diff)) diff_text = [] diff.print(output_func=lambda s: diff_text.append(s)) self.assertEqual(1, len(diff_text)) expect = "cnf diff: + CHARACTER (0) = 'Jar Jar Binks' " + \ "in franchise=Star Wars > movie=The Phantom Menace" self.assertEqual(expect, diff_text[0]) def test_change(self): """Change entry in data, check it is found.""" other = deepcopy(self.DATA) other.single_with_value("Star Trek").children \ .single_with_value("The Next Generation").children \ .single_with_value("Enterprise D").children \ .single_with_value("Picard").value = "Jean-Luc" diff = self.DATA.compare(other) self.assertEqual(2, len(diff)) diff_text = [] diff.print(output_func=lambda s: diff_text.append(s)) self.assertEqual(2, len(diff_text)) expect = [ "cnf diff: - captain (0) = 'Picard' in franchise=Star Trek > " "series=The Next Generation > ship=Enterprise D", "cnf diff: + captain (0) = 'Jean-Luc' in franchise=Star Trek > " "series=The Next Generation > ship=Enterprise D", ] self.assertEqual(expect, diff_text) def test_ignore(self): """Check that changes on ignore_list are ignored.""" other = deepcopy(self.DATA) # same change as in test_change other.single_with_value("Star Trek").children \ .single_with_value("The Next Generation").children \ .single_with_value("Enterprise D").children \ .single_with_value("Picard").value = "Jean-Luc" diff = self.DATA.compare(other, ignore_list=["CAPTAIN",]) self.assertEqual(0, len(diff)) if __name__ == '__main__': unittest.main()