Merge branch 'cnfline-templates' into cnfvar-deprecations
[pyi2ncommon] / test / test_simple_cnf.py
1 #!/usr/bin/env python
2 # This Python file uses the following encoding: utf-8
3
4 # The software in this package is distributed under the GNU General
5 # Public License version 2 (with a special exception described below).
6 #
7 # A copy of GNU General Public License (GPL) is included in this distribution,
8 # in the file COPYING.GPL.
9 #
10 # As a special exception, if other files instantiate templates or use macros
11 # or inline functions from this file, or you compile this file and link it
12 # with other works to produce a work based on this file, this file
13 # does not by itself cause the resulting work to be covered
14 # by the GNU General Public License.
15 #
16 # However the source code for this file must still be made available
17 # in accordance with section (3) of the GNU General Public License.
18 #
19 # This exception does not invalidate any other reasons why a work based
20 # on this file might be covered by the GNU General Public License.
21 #
22 # Copyright (c) 2016-2018 Intra2net AG <info@intra2net.com>
23
24 import unittest
25 from traceback import print_exc
26 from tempfile import mkstemp
27 import os
28 import sys
29
30 from src.simple_cnf import SimpleCnf
31 from src.simple_cnf import InvalidCnf
32
33 TEST_SET = """
34 1  USER,1: "admin"
35 2     (1) USER_DISABLED,0: "0"
36 3     (1) USER_FULLNAME,0: "Administrator"
37 4     (1) USER_GROUPWARE_FOLDER_CALENDAR,0: "INBOX/Kalender"
38 5     (1) USER_GROUPWARE_FOLDER_CONTACTS,0: "INBOX/Kontakte"
39 6     (1) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
40 7     (1) USER_GROUPWARE_FOLDER_NOTES,0: "INBOX/Notizen"
41 8     (1) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Elemente"
42 9     (1) USER_GROUPWARE_FOLDER_TASKS,0: "INBOX/Aufgaben"
43 10    (1) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
44 11    (1) USER_GROUP_MEMBER_REF,0: "1"
45 12    (1) USER_GROUP_MEMBER_REF,1: "2"
46 13    (1) USER_LOCALE,0: ""
47 14    (1) USER_PASSWORD,0: "test1234"
48 15    (1) USER_TRASH_DELETEDAYS,0: "30"
49 16 USER,2: "test"
50 17    (16) USER_DISABLED,0: "0"
51 18    (16) USER_EMAIL_VACATION,0: "ON"
52 19    (16) USER_EMAIL_VACATION_AUTOMATIC_END,0: ""
53 20    (16) USER_EMAIL_VACATION_AUTOMATIC_START,0: ""
54 21    (16) USER_EMAIL_VACATION_AUTOMATIC_STATE,0: "UNKNOWN"
55 22    (16) USER_EMAIL_VACATION_REPLYDAYS,0: "1"
56 23    (16) USER_EMAIL_VACATION_TEXT,0: "Bin im Urlaub"
57 24    (16) USER_FULLNAME,0: "testnutzer"
58 25    (16) USER_GROUPWARE_FOLDER_CALENDAR,0: "INBOX/Kalender"
59 26    (16) USER_GROUPWARE_FOLDER_CONTACTS,0: "INBOX/Kontakte"
60 27    (16) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
61 28    (16) USER_GROUPWARE_FOLDER_NOTES,0: "INBOX/Notizen"
62 29    (16) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Elemente"
63 30    (16) USER_GROUPWARE_FOLDER_TASKS,0: "INBOX/Aufgaben"
64 31    (16) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
65 32    (16) USER_GROUP_MEMBER_REF,0: "2"
66 33    (16) USER_GROUP_MEMBER_REF,1: "3"
67 34    (16) USER_LOCALE,0: ""
68 35    (16) USER_PASSWORD,0: "test1234"
69 36    (16) USER_TRASH_DELETEDAYS,0: "30"
70 37    (16) USER_WEBMAIL_MESSAGES_PER_PAGE,0: "25"
71 38    (16) USER_WEBMAIL_SIGNATURE,0: ""
72 39 USER,3: "mueller"
73 40    (39) USER_DISABLED,0: "0"
74 41    (39) USER_FULLNAME,0: "Kärößü"
75 42    (39) USER_GROUPWARE_FOLDER_CALENDAR,0: "INBOX/Kalender"
76 43    (39) USER_GROUPWARE_FOLDER_CONTACTS,0: "INBOX/Kontakte"
77 44    (39) USER_GROUPWARE_FOLDER_DRAFTS,0: "INBOX/Entwürfe"
78 45    (39) USER_GROUPWARE_FOLDER_NOTES,0: "INBOX/Notizen"
79 46    (39) USER_GROUPWARE_FOLDER_OUTBOX,0: "INBOX/Gesendete Elemente"
80 47    (39) USER_GROUPWARE_FOLDER_TASKS,0: "INBOX/Aufgaben"
81 48    (39) USER_GROUPWARE_FOLDER_TRASH,0: "INBOX/Gelöschte Elemente"
82 49    (39) USER_GROUP_MEMBER_REF,0: "2"
83 50    (39) USER_GROUP_MEMBER_REF,1: "3"
84 51    (39) USER_LOCALE,0: ""
85 52    (39) USER_PASSWORD,0: "grmpfl"
86 53    (39) USER_TRASH_DELETEDAYS,0: "30"
87 54    (39) USER_WEBMAIL_MESSAGES_PER_PAGE,0: "25"
88 55    (39) USER_WEBMAIL_SIGNATURE,0: ""
89 56 BACKUP_COMPRESS_ENABLE,0: "1"
90 57 BACKUP_CRON,0: "0123456"
91 58    (57) BACKUP_CRON_BEGIN,0: "7200"
92 59 BACKUP_ENCRYPT_ENABLE,0: "0"
93 60 BACKUP_ENCRYPT_PASSWORD,0: ""
94 61 TESTVAR,0: "test"
95 """
96
97 #: encoding for text files, set in :py:func:`setUpModule`
98 ENCODING = None
99
100
101 def setUpModule():
102     """Called once before all tests; determines encoding
103
104     This test might run in very limited build environments without locale, so
105     auto-selection of temp-file text encoding may return 'ASCII'.
106     Therefore, do the encoding "manually".
107     """
108     global ENCODING
109     ENCODING = sys.getfilesystemencoding()
110     if ENCODING.lower() in ('ascii', 'ansi_x3.4-1968'):
111         ENCODING = 'utf8'
112         print('\nWARNING: Using fallback encoding {} for temp file creation'
113               .format(ENCODING))
114
115
116 class SimpleCnfTest(unittest.TestCase):
117     """ The one and only test case in this module `-->` see module doc """
118
119     # setup and cleanup
120
121     import_file = None
122     cnf = None
123
124     @classmethod
125     def _import_cnf(cls):
126         """ import conf var data from temp file """
127         cls.cnf = SimpleCnf()
128         cls.cnf.append_file(cls.import_file)
129
130     @classmethod
131     def setUpClass(cls):
132         """ before running tests: write conf var string to temp file """
133         try:
134             sys_file_descriptor, cls.import_file = mkstemp()
135             os.close(sys_file_descriptor)
136             with open(cls.import_file, 'wb') as file_handle:
137                 file_handle.write(TEST_SET.encode(ENCODING))
138         except Exception:
139             print('\nWARNING: exception creating temp file:')
140             print_exc()
141
142             # clean up
143             cls.tearDownClass()
144
145             # re-raise
146             raise
147
148         cls._import_cnf()
149
150     @classmethod
151     def tearDownClass(cls):
152         """ after all tests have run, delete temp file """
153         if cls.import_file is not None:
154             try:
155                 os.unlink(cls.import_file)
156             except Exception:
157                 print('\nWARNING: exception deleting temp file:')
158                 print_exc()
159
160     # tests
161
162     def test_ctor_ok (self):
163         """
164         Test initializer :py:meth:`SimpleCnf.__init__` must succeed on sane
165         inputs.
166         """
167         self.assertNotEqual (SimpleCnf ({"cnf": []}), None)
168         self.assertNotEqual (SimpleCnf ([]), None)
169         self.assertNotEqual (SimpleCnf (), None)
170
171     def test_ctor_fail (self):
172         """
173         Test initializer :py:meth:`SimpleCnf.__init__` must reject inputs that
174         ``cnfvar.py`` does not handle.
175         """
176         with self.assertRaises (InvalidCnf):
177             _void = SimpleCnf (b"junk")
178         with self.assertRaises (InvalidCnf):
179             _void = SimpleCnf (42)
180         with self.assertRaises (InvalidCnf):
181             _void = SimpleCnf (tuple ([1701, 1337]))
182         with self.assertRaises (InvalidCnf):
183             _void = SimpleCnf (set (["one", "two"]))
184
185     def test_eq(self):
186         """ test method :py:meth:`SimpleCnf.__eq__` """
187         self.assertEqual(self.cnf[61], self.cnf[61])
188         self.assertEqual(self.cnf['testvar'], self.cnf[61])
189         self.assertEqual(self.cnf, self.cnf)
190         self.assertNotEqual(self.cnf[56], self.cnf[57])
191         self.assertNotEqual(SimpleCnf ({"cnf": []}), None)
192         self.assertNotEqual(SimpleCnf ({"cnf": []}), 42)
193         self.assertNotEqual(SimpleCnf ({"cnf": []}), "got wood?")
194         self.assertEqual(SimpleCnf (), SimpleCnf ({"cnf": []}))
195
196     def test_len(self):
197         """ test method :py:meth:`SimpleCnf.__len__` """
198         self.assertEqual(len(self.cnf), 8)
199
200     def test_getitem(self):
201         """ test method :py:meth:`SimpleCnf.__item__` """
202         self.assertEqual(len(self.cnf['user']), 3)
203         self.assertEqual(self.cnf['USER'], self.cnf['user'])
204         self.assertEqual(len(self.cnf['backup_encrypt_password']), 1)
205         self.assertEqual(len(self.cnf[12232]), 0)
206         self.assertEqual(len(self.cnf[55]), 0)
207         self.assertEqual(len(self.cnf[61]), 1)
208         self.assertEqual(len(self.cnf['user_webmail_signature']), 0)
209
210     def test_get(self):
211         """ test method :py:meth:`SimpleCnf.get` """
212         self.assertEqual(len(self.cnf.get()), 8)
213         self.assertEqual(len(self.cnf.get(name='user')), 3)
214
215     def test_get_value(self):
216         """ test method :py:meth:`SimpleCnf.get_value` """
217
218         with self.assertRaises(ValueError):
219             self.cnf.get_value()
220
221         self.assertEqual(self.cnf[56].get_value(), '1')
222         self.assertEqual(self.cnf[61].get_value(), 'test')
223
224     def test_get_children(self):
225         """ test method :py:meth:`SimpleCnf.get_children` """
226         with self.assertRaises(ValueError):
227             self.cnf.get_children()
228         self.assertEqual(len(self.cnf.get(name='user', value='mueller')
229                              .get_children()), 16)
230         self.assertEqual(self.cnf.get(name='user'),
231                          self.cnf.get(name='USER'))
232         self.assertEqual(self.cnf.get(name='user', value='mueller'),
233                          self.cnf.get(name='USER', value='mueller'))
234         self.assertEqual(len(self.cnf[57].get_children()), 1)
235         self.assertEqual(self.cnf[57].get_children().get_value(), '7200')
236
237     def test_add_alone(self):
238         """ test method :py:meth:`SimpleCnf.add` on empty conf """
239         # do not use self.cnf since that would void other test methods
240         cnf = SimpleCnf()
241         self.assertEqual(len(cnf), 0)
242         cnf.add('new_var', 'new_value')
243         self.assertEqual(len(cnf), 1)
244         cnf_var = cnf.get(name='new_var').get_single_dict()
245         self.assertEqual(cnf_var['data'], 'new_value')
246         self.assertIsInstance(cnf_var['data'], str)
247         self.assertEqual(cnf_var['number'], 1)
248
249     def test_add_on_top(self):
250         """ test method :py:meth:`SimpleCnf.add` on regular conf """
251         cnf = SimpleCnf()
252         self.assertEqual(len(cnf), 0)
253         cnf.append_file(self.import_file)
254         self.assertEqual(len(cnf), 8)
255
256         cnf.add('new_var', 'new_value')
257         self.assertEqual(len(cnf), 9)
258         cnf_var = cnf.get(name='new_var').get_single_dict()
259         self.assertEqual(cnf_var['data'], 'new_value')
260         self.assertIsInstance(cnf_var['data'], str)
261         self.assertEqual(cnf_var['number'], 62)
262
263     def test_add_with_children(self):
264         """ test method :py:meth:`SimpleCnf.add` by adding var with children"""
265         # load config
266         cnf = SimpleCnf()
267         cnf.append_file(self.import_file)
268         self.assertEqual(len(cnf['user']), 3)
269
270         # get a certain user with all its sub config
271         user_cnf = cnf.get(name='user', value='admin')
272         self.assertEqual(len(user_cnf), 1)
273
274         # copy as new user with different name but same children
275         cnf.add('user', 'admin2', children=user_cnf.get_children())
276         self.assertEqual(len(cnf['user']), 4)
277         self.assertEqual(len(cnf.get(name='user', value='admin2')), 1)
278         self.assertEqual(len(cnf.get(name='user', value='admin2').get_children()), 14)
279
280
281 if __name__ == '__main__':
282     unittest.main()