1# Copyright 2007 Google Inc.
2#
3# This program is free software; you can redistribute it and/or
4# modify it under the terms of the GNU General Public License
5# as published by the Free Software Foundation; either version 2
6# of the License, or (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software Foundation,
15# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16"""Unit tests for passwd.py."""
17
18__author__ = 'vasilios@google.com (Vasilios Hoffman)'
19
20import time
21import unittest
22
23from nss_cache import error
24from nss_cache.maps import group
25from nss_cache.maps import passwd
26
27
28class TestPasswdMap(unittest.TestCase):
29    """Tests for the PasswdMap class."""
30
31    def setUp(self):
32        """Set some default avalible data for testing."""
33        self._good_entry = passwd.PasswdMapEntry()
34        self._good_entry.name = 'foo'
35        self._good_entry.passwd = 'x'
36        self._good_entry.uid = 10
37        self._good_entry.gid = 10
38        self._good_entry.gecos = 'How Now Brown Cow'
39        self._good_entry.dir = '/home/foo'
40        self._good_entry.shell = '/bin/bash'
41
42    def testInit(self):
43        """Construct an empty or seeded PasswdMap."""
44        self.assertEqual(passwd.PasswdMap,
45                         type(passwd.PasswdMap()),
46                         msg='failed to create emtpy PasswdMap')
47        pmap = passwd.PasswdMap([self._good_entry])
48        self.assertEqual(self._good_entry,
49                         pmap.PopItem(),
50                         msg='failed to seed PasswdMap with list')
51        self.assertRaises(TypeError, passwd.PasswdMap, ['string'])
52
53    def testAdd(self):
54        """Add raises exceptions for objects it can't add or verify."""
55        pmap = passwd.PasswdMap()
56        entry = self._good_entry
57        self.assertTrue(pmap.Add(entry), msg='failed to add new entry.')
58
59        self.assertEqual(1, len(pmap), msg='unexpected size for Map.')
60
61        ret_entry = pmap.PopItem()
62        self.assertEqual(ret_entry, entry, msg='failed to pop existing entry.')
63
64        gentry = group.GroupMapEntry()
65        gentry.name = 'foo'
66        gentry.gid = 10
67        self.assertRaises(TypeError, pmap.Add, gentry)
68
69    def testContains(self):
70        """Verify __contains__ works, and does a deep compare."""
71        pentry_good = self._good_entry
72        pentry_like_good = passwd.PasswdMapEntry()
73        pentry_like_good.name = 'foo'  # same Key(), but rest of attributes differ
74        pentry_bad = passwd.PasswdMapEntry()
75        pentry_bad.name = 'bar'
76
77        pmap = passwd.PasswdMap([pentry_good])
78
79        self.assertTrue(pentry_good in pmap, msg='expected entry to be in map')
80        self.assertFalse(pentry_bad in pmap,
81                         msg='did not expect entry to be in map')
82        self.assertFalse(pentry_like_good in pmap,
83                         msg='__contains__ not doing a deep compare')
84
85    def testIterate(self):
86        """Check that we can iterate over PasswdMap."""
87        pmap = passwd.PasswdMap()
88        pmap.Add(self._good_entry)
89        ret_entries = []
90        for entry in pmap:
91            ret_entries.append(entry)
92        self.assertEqual(len(ret_entries), 1, msg='iterated over wrong count')
93        self.assertEqual(ret_entries[0],
94                         self._good_entry,
95                         msg='got the wrong entry back')
96
97    def testLen(self):
98        """Verify we have correctly overridden __len__ in MapEntry."""
99        pmap = passwd.PasswdMap()
100        self.assertEqual(len(pmap), 0, msg='expected len(pmap) to be 0')
101        pmap.Add(self._good_entry)
102        self.assertEqual(len(pmap), 1, msg='expected len(pmap) to be 1')
103
104    def testExists(self):
105        """Verify Exists() checks for presence of MapEntry objects."""
106        pmap = passwd.PasswdMap()
107        entry = self._good_entry
108        self.assertFalse(pmap.Exists(entry))
109        pmap.Add(entry)
110        self.assertTrue(pmap.Exists(entry))
111
112    def testMerge(self):
113        """Verify Merge() throws the right exceptions and correctly merges."""
114
115        # Setup some MapEntry objects with distinct Key()s
116        pentry1 = self._good_entry
117        pentry2 = passwd.PasswdMapEntry()
118        pentry2.name = 'john'
119        pentry3 = passwd.PasswdMapEntry()
120        pentry3.name = 'jane'
121
122        # Setup some Map objects
123        pmap_big = passwd.PasswdMap([pentry1, pentry2])
124        pmap_small = passwd.PasswdMap([pentry3])
125
126        # Merge small into big
127        self.assertTrue(pmap_big.Merge(pmap_small),
128                        msg='Merging small into big failed!')
129        self.assertTrue(pmap_big.Exists(pentry1),
130                        msg='pentry1 not found in Map')
131        self.assertTrue(pmap_big.Exists(pentry2),
132                        msg='pentry1 not found in Map')
133        self.assertTrue(pmap_big.Exists(pentry3),
134                        msg='pentry1 not found in Map')
135
136        # A second merge should do nothing
137        self.assertFalse(pmap_big.Merge(pmap_small),
138                         msg='Re-merging small into big succeeded.')
139
140        # An empty merge should do nothing
141        self.assertFalse(pmap_big.Merge(passwd.PasswdMap()),
142                         msg='Empty Merge should have done nothing.')
143
144        # Merge a GroupMap should throw TypeError
145        gmap = group.GroupMap()
146        self.assertRaises(TypeError, pmap_big.Merge, gmap)
147
148        # Merge an older map should throw an UnsupportedMap
149        old_map = passwd.PasswdMap(modify_time=1)
150        new_map = passwd.PasswdMap(modify_time=2)
151        self.assertRaises(error.InvalidMerge, new_map.Merge, old_map)
152        old_map = passwd.PasswdMap(update_time=1)
153        new_map = passwd.PasswdMap(update_time=2)
154        self.assertRaises(error.InvalidMerge, new_map.Merge, old_map)
155
156    def testPopItem(self):
157        """Verify you can retrieve MapEntry with PopItem."""
158        pmap = passwd.PasswdMap([self._good_entry])
159        self.assertEqual(pmap.PopItem(), self._good_entry)
160
161    def testLastModificationTimestamp(self):
162        """Test setting/getting of timestamps on maps."""
163        m = passwd.PasswdMap()
164        # we only work in whole-second resolution
165        now = int(time.time())
166
167        m.SetModifyTimestamp(now)
168        self.assertEqual(now, m._last_modification_timestamp)
169
170        ts = m.GetModifyTimestamp()
171        self.assertEqual(now, ts)
172
173
174class TestPasswdMapEntry(unittest.TestCase):
175    """Tests for the PasswdMapEntry class."""
176
177    def testInit(self):
178        """Construct empty and seeded PasswdMapEntry."""
179        entry = passwd.PasswdMapEntry()
180        self.assertEqual(type(entry),
181                         passwd.PasswdMapEntry,
182                         msg='Could not create empty PasswdMapEntry')
183        seed = {
184            'name': 'foo',
185            'passwd': 'x',
186            'uid': 10,
187            'gid': 10,
188            'gecos': '',
189            'dir': '',
190            'shell': ''
191        }
192        entry = passwd.PasswdMapEntry(seed)
193        self.assertTrue(entry.Verify(),
194                        msg='Could not verify seeded PasswdMapEntry')
195        self.assertEqual(entry.name,
196                         'foo',
197                         msg='Entry returned wrong value for name')
198        self.assertEqual(entry.passwd,
199                         'x',
200                         msg='Entry returned wrong value for passwd')
201        self.assertEqual(entry.uid,
202                         10,
203                         msg='Entry returned wrong value for uid')
204        self.assertEqual(entry.gid,
205                         10,
206                         msg='Entry returned wrong value for gid')
207        self.assertEqual(entry.gecos,
208                         '',
209                         msg='Entry returned wrong value for gecos')
210        self.assertEqual(entry.dir,
211                         '',
212                         msg='Entry returned wrong value for dir')
213        self.assertEqual(entry.shell,
214                         '',
215                         msg='Entry returned wrong value for shell')
216
217    def testAttributes(self):
218        """Test that we can get and set all expected attributes."""
219        entry = passwd.PasswdMapEntry()
220        entry.name = 'foo'
221        self.assertEqual(entry.name, 'foo', msg='Could not set attribute: name')
222        entry.passwd = 'x'
223        self.assertEqual(entry.passwd,
224                         'x',
225                         msg='Could not set attribute: passwd')
226        entry.uid = 10
227        self.assertEqual(entry.uid, 10, msg='Could not set attribute: uid')
228        entry.gid = 10
229        self.assertEqual(entry.gid, 10, msg='Could not set attribute: gid')
230        entry.gecos = 'How Now Brown Cow'
231        self.assertEqual(entry.gecos,
232                         'How Now Brown Cow',
233                         msg='Could not set attribute: gecos')
234        entry.dir = '/home/foo'
235        self.assertEqual(entry.dir,
236                         '/home/foo',
237                         msg='Could not set attribute: dir')
238        entry.shell = '/bin/bash'
239        self.assertEqual(entry.shell,
240                         '/bin/bash',
241                         msg='Could not set attribute: shell')
242
243    def testEq(self):
244        """Verify we are doing a deep compare in __eq__."""
245
246        # Setup some things to compare
247        entry_good = passwd.PasswdMapEntry({
248            'name': 'foo',
249            'uid': 10,
250            'gid': 10
251        })
252        entry_same_as_good = passwd.PasswdMapEntry({
253            'name': 'foo',
254            'uid': 10,
255            'gid': 10
256        })
257        entry_like_good = passwd.PasswdMapEntry()
258        entry_like_good.name = 'foo'  # same Key(), but rest of attributes differ
259        entry_bad = passwd.PasswdMapEntry()
260        entry_bad.name = 'bar'
261
262        self.assertEqual(entry_good,
263                         entry_good,
264                         msg='entry_good not equal to itself')
265        self.assertEqual(entry_good,
266                         entry_same_as_good,
267                         msg='__eq__ not doing deep compare')
268        self.assertNotEqual(entry_good,
269                            entry_like_good,
270                            msg='__eq__ not doing deep compare')
271        self.assertNotEqual(entry_good, entry_bad, msg='unexpected equality')
272
273    def testVerify(self):
274        """Test that the object can verify it's attributes and itself."""
275        entry = passwd.PasswdMapEntry()
276
277        # by leaving _KEY unset, we should bomb.
278        self.assertFalse(entry.Verify())
279
280    def testKey(self):
281        """Key() should return the value of the 'name' attribute."""
282        entry = passwd.PasswdMapEntry()
283        entry.name = 'foo'
284        self.assertEqual(entry.Key(), entry.name)
285
286
287if __name__ == '__main__':
288    unittest.main()
289