~ahasenack/landscape-client/landscape-client-1.5.5-0ubuntu0.9.04.0

« back to all changes in this revision

Viewing changes to landscape/manager/tests/test_usermanager.py

  • Committer: Bazaar Package Importer
  • Author(s): Rick Clark
  • Date: 2008-09-08 16:35:57 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080908163557-l3ixzj5dxz37wnw2
Tags: 1.0.18-0ubuntu1
New upstream release 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from landscape.lib.dbus_util import get_object
 
2
 
 
3
from landscape.lib.persist import Persist
 
4
 
 
5
from landscape.manager.manager import SUCCEEDED, FAILED
 
6
from landscape.monitor.monitor import MonitorPluginRegistry
 
7
from landscape.monitor.usermonitor import UserMonitor
 
8
from landscape.manager.usermanager import UserManager, UserManagerDBusObject
 
9
from landscape.manager.manager import ManagerPluginRegistry
 
10
from landscape.user.tests.helpers import FakeUserProvider, FakeUserManagement
 
11
from landscape.tests.helpers import LandscapeIsolatedTest
 
12
from landscape.user.provider import UserManagementError
 
13
from landscape.tests.helpers import RemoteBrokerHelper
 
14
 
 
15
 
 
16
class ManagerServiceTest(LandscapeIsolatedTest):
 
17
 
 
18
    helpers = [RemoteBrokerHelper]
 
19
 
 
20
    def setUp(self):
 
21
        super(ManagerServiceTest, self).setUp()
 
22
 
 
23
    def test_remote_locked_usernames(self):
 
24
 
 
25
        def check_result(result):
 
26
            self.assertEquals(result, ["psmith"])
 
27
 
 
28
        self.shadow_file = self.make_path("""\
 
29
jdoe:$1$xFlQvTqe$cBtrNEDOIKMy/BuJoUdeG0:13348:0:99999:7:::
 
30
psmith:!:13348:0:99999:7:::
 
31
sbarnes:$1$q7sz09uw$q.A3526M/SHu8vUb.Jo1A/:13349:0:99999:7:::
 
32
""")
 
33
        UserManagerDBusObject(self.broker_service.bus,
 
34
                              shadow_file=self.shadow_file)
 
35
        remote_service = get_object(self.broker_service.bus,
 
36
            UserManagerDBusObject.bus_name, UserManagerDBusObject.object_path)
 
37
 
 
38
        result = remote_service.get_locked_usernames()
 
39
        result.addCallback(check_result)
 
40
        return result
 
41
 
 
42
    def test_remote_empty_shadow_file(self):
 
43
 
 
44
        def check_result(result):
 
45
            self.assertEquals(result, [])
 
46
 
 
47
        self.shadow_file = self.make_path("\n")
 
48
        UserManagerDBusObject(self.broker_service.bus,
 
49
                              shadow_file=self.shadow_file)
 
50
        remote_service = get_object(self.broker_service.bus,
 
51
            UserManagerDBusObject.bus_name, UserManagerDBusObject.object_path)
 
52
 
 
53
        result = remote_service.get_locked_usernames()
 
54
        result.addCallback(check_result)
 
55
        return result
 
56
 
 
57
class UserGroupTestBase(LandscapeIsolatedTest):
 
58
 
 
59
    helpers = [RemoteBrokerHelper]
 
60
 
 
61
    def setUp(self):
 
62
        super(UserGroupTestBase, self).setUp()
 
63
        self.persist = Persist()
 
64
        self.monitor = MonitorPluginRegistry(
 
65
            self.broker_service.reactor, self.remote,
 
66
            self.broker_service.config, self.broker_service.bus,
 
67
            self.persist)
 
68
 
 
69
        self.shadow_file = self.make_path("""\
 
70
jdoe:$1$xFlQvTqe$cBtrNEDOIKMy/BuJoUdeG0:13348:0:99999:7:::
 
71
psmith:!:13348:0:99999:7:::
 
72
sbarnes:$1$q7sz09uw$q.A3526M/SHu8vUb.Jo1A/:13349:0:99999:7:::
 
73
""")
 
74
        accepted_types = ["operation-result", "users"]
 
75
        self.broker_service.message_store.set_accepted_types(accepted_types)
 
76
        self.manager = ManagerPluginRegistry(
 
77
            self.broker_service.reactor, self.remote,
 
78
            self.broker_service.config, self.broker_service.bus)
 
79
 
 
80
    def setup_environment(self, users, groups, shadow_file):
 
81
        provider = FakeUserProvider(users=users, groups=groups,
 
82
                                    shadow_file=shadow_file)
 
83
        plugin = UserMonitor(provider=provider)
 
84
        self.monitor.add(plugin)
 
85
        manager = UserManager(management=FakeUserManagement(provider=provider),
 
86
                              shadow_file=shadow_file)
 
87
        self.manager.add(manager)
 
88
        return plugin
 
89
 
 
90
 
 
91
class UserOperationsMessagingTest(UserGroupTestBase):
 
92
 
 
93
    def test_add_user_event(self):
 
94
        """
 
95
        When an C{add-user} event is received the user should be
 
96
        added.  Two messages should be generated: a C{users} message
 
97
        with details about the change and an C{operation-result} with
 
98
        details of the outcome of the operation.
 
99
        """
 
100
        def handle_callback(result):
 
101
            messages = self.broker_service.message_store.get_pending_messages()
 
102
            self.assertMessages(messages,
 
103
                                [{"type": "operation-result",
 
104
                                  "status": SUCCEEDED,
 
105
                                  "operation-id": 123, "timestamp": 0,
 
106
                                  "result-text": "add_user succeeded"},
 
107
                                  {"timestamp": 0, "type": "users",
 
108
                                  "operation-id": 123,
 
109
                                  "create-users": [{"home-phone": None,
 
110
                                                    "username": "jdoe",
 
111
                                                    "uid": 1000,
 
112
                                                    "enabled": True,
 
113
                                                    "location": "Room 101",
 
114
                                                    "work-phone": "+12345",
 
115
                                                    "name": u"John Doe",
 
116
                                                    "primary-gid": 1000}]}])
 
117
 
 
118
        self.setup_environment([], [], None)
 
119
 
 
120
        result = self.manager.dispatch_message(
 
121
            {"username": "jdoe", "name": "John Doe", "password": "password",
 
122
             "operation-id": 123, "require-password-reset": False,
 
123
             "primary-group-name": None, "location": "Room 101",
 
124
             "work-number": "+12345", "home-number": None,
 
125
             "type": "add-user"})
 
126
 
 
127
        result.addCallback(handle_callback)
 
128
        return result
 
129
 
 
130
    def test_failing_add_user_event(self):
 
131
        """
 
132
        When an C{add-user} event is received the user should be
 
133
        added. If not enough information is provided, we expect a single error,
 
134
        containing details of the failure.
 
135
        """
 
136
        self.log_helper.ignore_errors(KeyError)
 
137
        def handle_callback(result):
 
138
            messages = self.broker_service.message_store.get_pending_messages()
 
139
            self.assertMessages(messages,
 
140
                                [{"type": "operation-result", "status": FAILED,
 
141
                                  "operation-id": 123, "timestamp": 0,
 
142
                                  "result-text": "KeyError: 'username'"}])
 
143
 
 
144
        self.setup_environment([], [], None)
 
145
 
 
146
        result = self.manager.dispatch_message(
 
147
            {"name": "John Doe", "password": "password", "operation-id": 123,
 
148
             "require-password-reset": False, "type": "add-user"})
 
149
        result.addCallback(handle_callback)
 
150
        return result
 
151
 
 
152
    def test_add_user_event_in_sync(self):
 
153
        """
 
154
        The client and server should be in sync after an C{add-user}
 
155
        event is received and processed.  In other words, a snapshot
 
156
        should have been taken after the operation was handled.
 
157
        """
 
158
        def handle_callback1(result):
 
159
            message_store = self.broker_service.message_store
 
160
            messages = message_store.get_pending_messages()
 
161
            self.assertTrue(messages)
 
162
            result = plugin.run()
 
163
            result.addCallback(handle_callback2, messages)
 
164
            return result
 
165
 
 
166
        def handle_callback2(result, messages):
 
167
            message_store = self.broker_service.message_store
 
168
            new_messages = message_store.get_pending_messages()
 
169
            self.assertEquals(messages, new_messages)
 
170
            return result
 
171
 
 
172
        plugin = self.setup_environment([], [], None)
 
173
        result = self.manager.dispatch_message(
 
174
            {"username": "jdoe", "name": "John Doe", "password": "password",
 
175
             "operation-id": 123, "require-password-reset": False,
 
176
             "primary-group-name": None, "type": "add-user",
 
177
             "location": None, "home-number": "+123456", "work-number": None})
 
178
 
 
179
        result.addCallback(handle_callback1)
 
180
        return result
 
181
 
 
182
    def test_add_user_event_with_external_changes(self):
 
183
        """
 
184
        If external user changes have been made but not detected by
 
185
        the client before an C{add-user} event is received, the client
 
186
        should first detect changes and then perform the operation.
 
187
        The results should be reported in separate messages.
 
188
        """
 
189
        def handle_callback(result):
 
190
            messages = self.broker_service.message_store.get_pending_messages()
 
191
            self.assertEquals(len(messages), 3)
 
192
            messages = [messages[0], messages[2]]
 
193
            self.assertMessages(messages,
 
194
                                [{"type": "users",
 
195
                                  "create-users": [{"home-phone": None,
 
196
                                                    "name": "Bo",
 
197
                                                    "username": "bo",
 
198
                                                    "uid": 1000,
 
199
                                                    "enabled": True,
 
200
                                                    "location": None,
 
201
                                                    "primary-gid": 1000,
 
202
                                                    "work-phone": None}]},
 
203
                                 {"type": "users", "operation-id": 123,
 
204
                                  "create-users": [{"home-phone": "+123456",
 
205
                                                    "username": "jdoe",
 
206
                                                    "uid": 1001,
 
207
                                                    "enabled": True,
 
208
                                                    "location": None,
 
209
                                                    "work-phone": None,
 
210
                                                    "name": "John Doe",
 
211
                                                    "primary-gid": 1001}]}])
 
212
 
 
213
        users = [("bo", "x", 1000, 1000, "Bo,,,,", "/home/bo", "/bin/zsh")]
 
214
        self.setup_environment(users, [], None)
 
215
        result = self.manager.dispatch_message(
 
216
            {"username": "jdoe", "name": "John Doe", "password": "password",
 
217
             "operation-id": 123, "require-password-reset": False,
 
218
             "type": "add-user", "primary-group-name": None,
 
219
             "location": None, "work-number": None, "home-number": "+123456"})
 
220
        result.addCallback(handle_callback)
 
221
        return result
 
222
 
 
223
    def test_edit_user_event(self):
 
224
        """
 
225
        When a C{edit-user} message is received the user should be
 
226
        updated.  Two messages should be generated: a C{users} message
 
227
        with details about the change and an C{operation-result} with
 
228
        details of the outcome of the operation.
 
229
        """
 
230
        def handle_callback(result):
 
231
            messages = self.broker_service.message_store.get_pending_messages()
 
232
            self.assertEquals(len(messages), 3)
 
233
            # Ignore the message created by plugin.run.
 
234
            self.assertMessages(messages[1:],
 
235
                                [{"type": "operation-result",
 
236
                                  "status": SUCCEEDED,
 
237
                                  "operation-id": 99, "timestamp": 0,
 
238
                                  "result-text": "set_user_details succeeded"},
 
239
                                 {"update-users": [{"username": "jdoe",
 
240
                                                    "uid": 1001,
 
241
                                                    "enabled": True,
 
242
                                                    "work-phone": "789WORK",
 
243
                                                    "home-phone": "123HOME",
 
244
                                                    "location": "Everywhere",
 
245
                                                    "name": "John Doe",
 
246
                                                    "primary-gid": 1001}],
 
247
                                    "timestamp": 0, "type": "users",
 
248
                                  "operation-id": 99},])
 
249
 
 
250
        users = [("jdoe", "x", 1001, 1000, "John Doe,,,,", "/home/bo", "/bin/zsh")]
 
251
        groups = [("users", "x", 1001, [])]
 
252
        self.setup_environment(users, groups, None)
 
253
        result = self.manager.dispatch_message(
 
254
            {"uid": 1001, "username": "jdoe", "password": "password",
 
255
             "name": "John Doe", "location": "Everywhere",
 
256
             "work-number": "789WORK", "home-number": "123HOME",
 
257
             "operation-id": 99, "primary-group-name": u"users",
 
258
             "type": "edit-user"})
 
259
        result.addCallback(handle_callback)
 
260
        return result
 
261
 
 
262
 
 
263
    def test_edit_user_event_in_sync(self):
 
264
        """
 
265
        The client and server should be in sync after a C{edit-user}
 
266
        event is received and processed.  In other words, a snapshot
 
267
        should have been taken after the operation was handled.
 
268
        """
 
269
        def handle_callback1(result):
 
270
            messages = self.broker_service.message_store.get_pending_messages()
 
271
            self.assertTrue(messages)
 
272
            result = plugin.run()
 
273
            result.addCallback(handle_callback2, messages)
 
274
            return result
 
275
 
 
276
        def handle_callback2(result, messages):
 
277
            new_messages = self.broker_service.message_store.get_pending_messages()
 
278
            self.assertEquals(messages, new_messages)
 
279
            return result
 
280
 
 
281
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/home/bo", "/bin/zsh")]
 
282
        plugin = self.setup_environment(users, [], None)
 
283
        result = self.manager.dispatch_message(
 
284
            {"username": "jdoe", "password": "password", "name": "John Doe",
 
285
             "location": "Everywhere", "work-number": "789WORK",
 
286
             "home-number": "123HOME", "primary-group-name": None,
 
287
             "type": "edit-user", "operation-id": 99})
 
288
        result.addCallback(handle_callback1)
 
289
        return result
 
290
 
 
291
    def test_edit_user_event_with_external_changes(self):
 
292
        """
 
293
        If external user changes have been made but not detected by
 
294
        the client before a C{edit-user} event is received, the client
 
295
        should first detect changes and then perform the operation.
 
296
        The results should be reported in separate messages.
 
297
        """
 
298
        def handle_callback(result):
 
299
            messages = self.broker_service.message_store.get_pending_messages()
 
300
            self.assertEquals(len(messages), 3)
 
301
            self.assertMessages([messages[0], messages[2]],
 
302
                                [{"type": "users",
 
303
                                  "create-group-members": {u"users": [u"jdoe"]},
 
304
                                  "create-groups": [{"gid": 1001,
 
305
                                                     "name": u"users"}],
 
306
                                  "create-users": [{"home-phone": None,
 
307
                                                    "work-phone": None,
 
308
                                                    "username": "jdoe",
 
309
                                                    "uid": 1000,
 
310
                                                    "enabled": True,
 
311
                                                    "location": None,
 
312
                                                    "name": "John Doe",
 
313
                                                    "primary-gid": 1000}]},
 
314
                                 {"type": "users", "operation-id": 99,
 
315
                                  "update-users": [{"username": "jdoe",
 
316
                                                    "uid": 1000,
 
317
                                                    "enabled": True,
 
318
                                                    "work-phone": "789WORK",
 
319
                                                    "home-phone": "123HOME",
 
320
                                                    "location": "Everywhere",
 
321
                                                    "primary-gid": 1001,
 
322
                                                    "name": "John Doe"}]}])
 
323
 
 
324
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/home/bo",
 
325
                  "/bin/zsh")]
 
326
        groups = [("users", "x", 1001, ["jdoe"])]
 
327
        self.setup_environment(users, groups, None)
 
328
        result = self.manager.dispatch_message(
 
329
            {"username": "jdoe", "password": "password", "name": "John Doe",
 
330
             "location": "Everywhere", "work-number": "789WORK",
 
331
             "home-number": "123HOME", "primary-group-name": u"users",
 
332
             "type": "edit-user", "operation-id": 99})
 
333
        result.addCallback(handle_callback)
 
334
        return result
 
335
 
 
336
    def test_remove_user_event(self):
 
337
        """
 
338
        When a C{remove-user} event is received, with the
 
339
        C{delete-home} parameter set to C{True}, the user and her home
 
340
        directory should be removed.  Two messages should be
 
341
        generated: a C{users} message with details about the change
 
342
        and an C{operation-result} with details of the outcome of the
 
343
        operation.
 
344
        """
 
345
        def handle_callback(result):
 
346
            messages = self.broker_service.message_store.get_pending_messages()
 
347
            self.assertEquals(len(messages), 3)
 
348
            # Ignore the message created by plugin.run.
 
349
            self.assertMessages([messages[2], messages[1]],
 
350
                                [{"timestamp": 0, "delete-users": ["jdoe"],
 
351
                                  "type": "users", "operation-id": 39},
 
352
                                 {"type": "operation-result",
 
353
                                  "status": SUCCEEDED,
 
354
                                  "operation-id": 39, "timestamp": 0,
 
355
                                  "result-text": "remove_user succeeded"}])
 
356
 
 
357
 
 
358
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/home/bo",
 
359
                  "/bin/zsh")]
 
360
        self.setup_environment(users, [], None)
 
361
        result = self.manager.dispatch_message(
 
362
            {"username": "jdoe",
 
363
             "delete-home": True,
 
364
             "type": "remove-user",
 
365
             "operation-id": 39})
 
366
        result.addCallback(handle_callback)
 
367
        return result
 
368
 
 
369
    def test_failing_remove_user_event(self):
 
370
        """
 
371
        When a C{remove-user} event is received, and the user doesn't exist, we
 
372
        expect a single message with the failure message.
 
373
        """
 
374
        self.log_helper.ignore_errors(UserManagementError)
 
375
 
 
376
        def handle_callback(result):
 
377
            messages = self.broker_service.message_store.get_pending_messages()
 
378
            self.assertEquals(len(messages), 1)
 
379
            failure_string = "UserManagementError: remove_user failed"
 
380
            self.assertMessages(messages,
 
381
                                [{"type": "operation-result", "status": FAILED,
 
382
                                  "operation-id": 39, "timestamp": 0,
 
383
                                  "result-text": failure_string}
 
384
                                ])
 
385
 
 
386
        self.setup_environment([], [], None)
 
387
        result = self.manager.dispatch_message(
 
388
            {"username": "jdoe",
 
389
             "delete-home": True,
 
390
             "type": "remove-user",
 
391
             "operation-id": 39})
 
392
        result.addCallback(handle_callback)
 
393
        return result
 
394
 
 
395
 
 
396
    def test_remove_user_event_leave_home(self):
 
397
        """
 
398
        When a C{remove-user} event is received, with the
 
399
        C{delete-home} parameter set to C{False}, the user should be
 
400
        removed without deleting the user's home directory.  Two
 
401
        messages should be generated: a C{users} message with details
 
402
        about the change and an C{operation-result} with details of
 
403
        the outcome of the operation.
 
404
        """
 
405
        def handle_callback(result):
 
406
                messages = self.broker_service.message_store.get_pending_messages()
 
407
                self.assertEquals(len(messages), 3)
 
408
                # Ignore the message created by plugin.run.
 
409
                self.assertMessages([messages[2], messages[1]],
 
410
                                    [{"timestamp": 0, "delete-users": ["jdoe"],
 
411
                                      "type": "users", "operation-id": 39},
 
412
                                     {"type": "operation-result",
 
413
                                      "status": SUCCEEDED,
 
414
                                      "operation-id": 39, "timestamp": 0,
 
415
                                      "result-text": "remove_user succeeded"}])
 
416
 
 
417
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/home/bo", "/bin/zsh")]
 
418
        self.setup_environment(users, [], None)
 
419
        result = self.manager.dispatch_message(
 
420
            {"username": "jdoe",
 
421
             "delete-home": False,
 
422
             "type": "remove-user",
 
423
             "operation-id": 39})
 
424
        result.addCallback(handle_callback)
 
425
        return result
 
426
 
 
427
    def test_remove_user_event_in_sync(self):
 
428
        """
 
429
        The client and server should be in sync after a C{remove-user}
 
430
        event is received and processed.  In other words, a snapshot
 
431
        should have been taken after the operation was handled.
 
432
        """
 
433
        def handle_callback1(result):
 
434
            message_store = self.broker_service.message_store
 
435
            messages = message_store.get_pending_messages()
 
436
            self.assertTrue(messages)
 
437
            result = plugin.run()
 
438
            result.addCallback(handle_callback2, messages)
 
439
            return result
 
440
 
 
441
        def handle_callback2(result, messages):
 
442
            message_store = self.broker_service.message_store
 
443
            new_messages = message_store.get_pending_messages()
 
444
            self.assertEquals(messages, new_messages)
 
445
 
 
446
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/home/bo",
 
447
                  "/bin/zsh")]
 
448
        plugin = self.setup_environment(users, [], self.shadow_file)
 
449
        result = self.manager.dispatch_message(
 
450
            {"username": "jdoe",
 
451
             "delete-home": True,
 
452
             "type": "remove-user",
 
453
             "operation-id": 39})
 
454
        result.addCallback(handle_callback1)
 
455
        return result
 
456
 
 
457
    def test_remove_user_event_with_external_changes(self):
 
458
        """
 
459
        If external user changes have been made but not detected by
 
460
        the client before a C{remove-user} event is received, the
 
461
        client should first detect changes and then perform the
 
462
        operation.  The results should be reported in separate
 
463
        messages.
 
464
        """
 
465
        def handle_callback(result):
 
466
            messages = self.broker_service.message_store.get_pending_messages()
 
467
            self.assertEquals(len(messages), 3)
 
468
            self.assertMessages([messages[0], messages[2]],
 
469
                                [{"type": "users",
 
470
                                  "create-users": [{"home-phone": None,
 
471
                                                    "username": "jdoe",
 
472
                                                    "uid": 1000,
 
473
                                                    "enabled": True,
 
474
                                                    "location": None,
 
475
                                                    "work-phone": None,
 
476
                                                    "primary-gid": 1000,
 
477
                                                    "name": "John Doe"}]},
 
478
                                 {"type": "users",
 
479
                                  "delete-users": ["jdoe"],
 
480
                                  "operation-id": 39}])
 
481
 
 
482
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/home/bo",
 
483
                  "/bin/zsh")]
 
484
        self.setup_environment(users, [], None)
 
485
        result = self.manager.dispatch_message(
 
486
            {"username": "jdoe",
 
487
             "delete-home": True,
 
488
             "type": "remove-user",
 
489
             "operation-id": 39})
 
490
        result.addCallback(handle_callback)
 
491
        return result
 
492
 
 
493
    def test_lock_user_event(self):
 
494
        """
 
495
        When a C{lock-user} event is received the user should be
 
496
        locked out.  Two messages should be generated: a C{users}
 
497
        message with details about the change and an
 
498
        C{operation-result} with details of the outcome of the
 
499
        operation.
 
500
        """
 
501
        def handle_callback(result):
 
502
           messages = self.broker_service.message_store.get_pending_messages()
 
503
           self.assertEquals(len(messages), 3, messages)
 
504
           # Ignore the message created by plugin.run.
 
505
           self.assertMessages([messages[2], messages[1]],
 
506
                               [{"timestamp": 0, "type": "users", "operation-id": 99,
 
507
                                 "update-users": [{"home-phone": None,
 
508
                                                   "username": "jdoe",
 
509
                                                   "uid": 1000,
 
510
                                                   "enabled": False,
 
511
                                                   "location": None,
 
512
                                                   "work-phone": None,
 
513
                                                   "primary-gid": 1000,
 
514
                                                   "name": u"John Doe"}]},
 
515
                                {"type": "operation-result",
 
516
                                 "status": SUCCEEDED,
 
517
                                 "operation-id": 99, "timestamp": 0,
 
518
                                 "result-text": "lock_user succeeded"}])
 
519
 
 
520
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/home/bo", "/bin/zsh")]
 
521
        self.setup_environment(users, [], self.shadow_file)
 
522
        result = self.manager.dispatch_message(
 
523
            {"username": "jdoe",
 
524
             "operation-id": 99,
 
525
             "type": "lock-user"})
 
526
        result.addCallback(handle_callback)
 
527
        return result
 
528
 
 
529
    def test_failing_lock_user_event(self):
 
530
        """
 
531
        When a C{lock-user} event is received the user should be
 
532
        locked out.  However, if the user doesn't exist in the user database,
 
533
        we expect only a single failure message to be generated.
 
534
        """
 
535
        self.log_helper.ignore_errors(UserManagementError)
 
536
        def handle_callback(result):
 
537
            messages = self.broker_service.message_store.get_pending_messages()
 
538
            self.assertEquals(len(messages), 1)
 
539
            failure_string = "UserManagementError: lock_user failed"
 
540
            self.assertMessages(messages,
 
541
                                [{"type": "operation-result",
 
542
                                  "status": FAILED,
 
543
                                  "operation-id": 99, "timestamp": 0,
 
544
                                  "result-text": failure_string}])
 
545
 
 
546
        self.setup_environment([], [], None)
 
547
        result = self.manager.dispatch_message(
 
548
            {"username": "jdoe",
 
549
             "operation-id": 99,
 
550
             "type": "lock-user"})
 
551
        result.addCallback(handle_callback)
 
552
        return result
 
553
 
 
554
    def test_lock_user_event_in_sync(self):
 
555
        """
 
556
        The client and server should be in sync after a C{lock-user}
 
557
        event is received and processed.  In other words, a snapshot
 
558
        should have been taken after the operation was handled.
 
559
        """
 
560
        def handle_callback1(result):
 
561
            message_store = self.broker_service.message_store
 
562
            messages = message_store.get_pending_messages()
 
563
            self.assertTrue(messages)
 
564
            result = plugin.run()
 
565
            result.addCallback(handle_callback2, messages)
 
566
            return result
 
567
 
 
568
        def handle_callback2(result, messages):
 
569
            message_store = self.broker_service.message_store
 
570
            new_messages = message_store.get_pending_messages()
 
571
            self.assertEquals(messages, new_messages)
 
572
 
 
573
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/home/bo",
 
574
                  "/bin/zsh")]
 
575
        plugin = self.setup_environment(users, [], self.shadow_file)
 
576
        result = self.manager.dispatch_message(
 
577
            {"username": "jdoe",
 
578
             "type": "lock-user",
 
579
             "operation-id": 99})
 
580
        result.addCallback(handle_callback1)
 
581
        return result
 
582
 
 
583
    def test_lock_user_event_with_external_changes(self):
 
584
        """
 
585
        If external user changes have been made but not detected by
 
586
        the client before a C{lock-user} event is received, the client
 
587
        should first detect changes and then perform the operation.
 
588
        The results should be reported in separate messages.
 
589
        """
 
590
        def handle_callback(result):
 
591
            messages = self.broker_service.message_store.get_pending_messages()
 
592
            self.assertEquals(len(messages), 3)
 
593
            self.assertMessages([messages[0], messages[2]],
 
594
                                [{"type": "users",
 
595
                                  "create-users": [{"home-phone": None,
 
596
                                                    "username": "jdoe",
 
597
                                                    "uid": 1000,
 
598
                                                    "enabled": True,
 
599
                                                    "location": None,
 
600
                                                    "work-phone": None,
 
601
                                                    "primary-gid": 1000,
 
602
                                                    "name": "John Doe"}]},
 
603
                                 {"type": "users", "operation-id": 99,
 
604
                                  "update-users": [{"home-phone": None,
 
605
                                                    "username": "jdoe",
 
606
                                                    "uid": 1000,
 
607
                                                    "enabled": False,
 
608
                                                    "location": None,
 
609
                                                    "work-phone": None,
 
610
                                                    "primary-gid": 1000,
 
611
                                                    "name": "John Doe"}]}])
 
612
 
 
613
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/home/bo",
 
614
                  "/bin/zsh")]
 
615
        self.setup_environment(users, [], self.shadow_file)
 
616
        result = self.manager.dispatch_message(
 
617
            {"username": "jdoe",
 
618
             "type": "lock-user",
 
619
             "operation-id": 99})
 
620
        result.addCallback(handle_callback)
 
621
        return result
 
622
 
 
623
    def test_unlock_user_event(self):
 
624
        """
 
625
        When an C{unlock-user} event is received the user should be
 
626
        enabled.  Two messages should be generated: a C{users} message
 
627
        with details about the change and an C{operation-result} with
 
628
        details of the outcome of the operation.
 
629
        """
 
630
        def handle_callback(result):
 
631
            messages = self.broker_service.message_store.get_pending_messages()
 
632
            self.assertEquals(len(messages), 3)
 
633
            # Ignore the message created by plugin.run.
 
634
            self.assertMessages([messages[2], messages[1]],
 
635
                                [{"timestamp": 0, "type": "users", "operation-id": 99,
 
636
                                  "update-users": [{"home-phone": None,
 
637
                                                    "username": "psmith",
 
638
                                                    "uid": 1000,
 
639
                                                    "enabled": True,
 
640
                                                    "location": None,
 
641
                                                    "work-phone": None,
 
642
                                                    "primary-gid": 1000,
 
643
                                                    "name": u"Paul Smith"}]},
 
644
                                 {"type": "operation-result",
 
645
                                  "status": SUCCEEDED,
 
646
                                  "operation-id": 99, "timestamp": 0,
 
647
                                  "result-text": "unlock_user succeeded"}])
 
648
 
 
649
        users = [("psmith", "x", 1000, 1000, "Paul Smith,,,,", "/home/psmith",
 
650
                  "/bin/zsh")]
 
651
        self.setup_environment(users, [], self.shadow_file)
 
652
 
 
653
        result = self.manager.dispatch_message(
 
654
            {"username": "psmith",
 
655
             "type": "unlock-user",
 
656
             "operation-id": 99})
 
657
        result.addCallback(handle_callback)
 
658
        return result
 
659
 
 
660
    def test_failing_unlock_user_event(self):
 
661
        """
 
662
        When an C{unlock-user} event is received the user should be
 
663
        enabled.  However, when the user doesn't exist in the user database, an
 
664
        error should be generated.
 
665
        """
 
666
        self.log_helper.ignore_errors(UserManagementError)
 
667
        def handle_callback(result):
 
668
            messages = self.broker_service.message_store.get_pending_messages()
 
669
            self.assertEquals(len(messages), 1)
 
670
            failure_string = "UserManagementError: unlock_user failed"
 
671
            self.assertMessages(messages,
 
672
                                [{"type": "operation-result",
 
673
                                  "status": FAILED,
 
674
                                  "operation-id": 99, "timestamp": 0,
 
675
                                  "result-text": failure_string}])
 
676
 
 
677
        self.setup_environment([], [], None)
 
678
        result = self.manager.dispatch_message(
 
679
            {"username": "jdoe",
 
680
             "operation-id": 99,
 
681
             "type": "unlock-user"})
 
682
        result.addCallback(handle_callback)
 
683
        return result
 
684
 
 
685
    def test_unlock_user_event_in_sync(self):
 
686
        """
 
687
        The client and server should be in sync after an
 
688
        C{unlock-user} event is received and processed.  In other
 
689
        words, a snapshot should have been taken after the operation
 
690
        was handled.
 
691
        """
 
692
        def handle_callback(result):
 
693
            message_store = self.broker_service.message_store
 
694
            messages = message_store.get_pending_messages()
 
695
            self.assertTrue(messages)
 
696
            result = plugin.run()
 
697
            result.addCallback(handle_callback2, messages)
 
698
            return result
 
699
 
 
700
        def handle_callback2(result, messages):
 
701
            message_store = self.broker_service.message_store
 
702
            new_messages = message_store.get_pending_messages()
 
703
            self.assertEquals(messages, new_messages)
 
704
 
 
705
        users = [("psmith", "x", 1000, 1000, "Paul Smith,,,,", "/home/psmith",
 
706
                  "/bin/zsh")]
 
707
        plugin = self.setup_environment(users, [], self.shadow_file)
 
708
 
 
709
        result = self.manager.dispatch_message(
 
710
            {"username": "psmith",
 
711
             "operation-id": 99,
 
712
             "type": "unlock-user"})
 
713
        result.addCallback(handle_callback)
 
714
        return result
 
715
 
 
716
    def test_unlock_user_event_with_external_changes(self):
 
717
        """
 
718
        If external user changes have been made but not detected by
 
719
        the client before a C{unlock-user} event is received, the
 
720
        client should first detect changes and then perform the
 
721
        operation.  The results should be reported in separate
 
722
        messages.
 
723
        """
 
724
        def handle_callback(result):
 
725
            messages = self.broker_service.message_store.get_pending_messages()
 
726
            self.assertEquals(len(messages), 3)
 
727
            self.assertMessages([messages[0], messages[2]],
 
728
                                [{"type": "users",
 
729
                                  "create-users": [{"home-phone": None,
 
730
                                                    "username": "psmith",
 
731
                                                    "uid": 1000,
 
732
                                                    "enabled": False,
 
733
                                                    "location": None,
 
734
                                                    "work-phone": None,
 
735
                                                    "primary-gid": 1000,
 
736
                                                    "name": "Paul Smith"}]},
 
737
                                 {"type": "users", "operation-id": 99,
 
738
                                  "update-users": [{"home-phone": None,
 
739
                                                    "username": "psmith",
 
740
                                                    "uid": 1000,
 
741
                                                    "enabled": True,
 
742
                                                    "location": None,
 
743
                                                    "work-phone": None,
 
744
                                                    "primary-gid": 1000,
 
745
                                                    "name": "Paul Smith"}]}])
 
746
 
 
747
        users = [("psmith", "x", 1000, 1000, "Paul Smith,,,,", "/home/psmith",
 
748
                  "/bin/zsh")]
 
749
        plugin = self.setup_environment(users, [], self.shadow_file)
 
750
 
 
751
        result = self.manager.dispatch_message(
 
752
            {"username": "psmith",
 
753
             "operation-id": 99,
 
754
             "type": "unlock-user"})
 
755
        result.addCallback(handle_callback)
 
756
        return result
 
757
 
 
758
 
 
759
class GroupOperationsMessagingTest(UserGroupTestBase):
 
760
 
 
761
    def test_add_group_event(self):
 
762
        """
 
763
        When an C{add-group} message is received the group should be
 
764
        created.  Two messages should be generated: a C{users} message
 
765
        with details about the change and an C{operation-result} with
 
766
        details of the outcome of the operation.
 
767
        """
 
768
        def handle_callback(result):
 
769
            messages = self.broker_service.message_store.get_pending_messages()
 
770
            self.assertEquals(len(messages), 2)
 
771
            # Ignore the message created by plugin.run.
 
772
            self.assertMessages([messages[1], messages[0]],
 
773
                                [{"type": "users", "timestamp": 0,
 
774
                                  "operation-id": 123,
 
775
                                  "create-groups": [{"gid": 1000,
 
776
                                                     "name": "bizdev"}]},
 
777
                                 {"type": "operation-result",
 
778
                                  "status": SUCCEEDED,
 
779
                                  "operation-id": 123, "timestamp": 0,
 
780
                                  "result-text": "add_group succeeded"}])
 
781
 
 
782
        self.setup_environment([], [], None)
 
783
        result = self.manager.dispatch_message(
 
784
            {"groupname": "bizdev",
 
785
             "type": "add-group",
 
786
             "operation-id": 123})
 
787
        result.addCallback(handle_callback)
 
788
        return result
 
789
 
 
790
    def test_add_group_event_in_sync(self):
 
791
        """
 
792
        The client and server should be in sync after an C{add-group}
 
793
        event is received and processed.  In other words, a snapshot
 
794
        should have been taken after the operation was handled.
 
795
        """
 
796
        def handle_callback1(result):
 
797
            message_store = self.broker_service.message_store
 
798
            messages = message_store.get_pending_messages()
 
799
            self.assertTrue(messages)
 
800
            result = plugin.run()
 
801
            result.addCallback(handle_callback2, messages)
 
802
            return result
 
803
 
 
804
        def handle_callback2(result, messages):
 
805
            message_store = self.broker_service.message_store
 
806
            new_messages = message_store.get_pending_messages()
 
807
            self.assertEquals(messages, new_messages)
 
808
 
 
809
        plugin = self.setup_environment([], [], None)
 
810
        result = self.manager.dispatch_message(
 
811
            {"groupname": "bizdev",
 
812
             "operation-id": 123,
 
813
             "type": "add-group"})
 
814
        result.addCallback(handle_callback1)
 
815
        return result
 
816
 
 
817
 
 
818
    def test_add_group_event_with_external_changes(self):
 
819
        """
 
820
        If external user changes have been made but not detected by
 
821
        the client before an C{add-group} event is received, the client
 
822
        should first detect changes and then perform the operation.
 
823
        The results should be reported in separate messages.
 
824
        """
 
825
        def handle_callback(result):
 
826
            messages = self.broker_service.message_store.get_pending_messages()
 
827
            self.assertEquals(len(messages), 3)
 
828
            # We skip the operation-result message.
 
829
            self.assertMessages([messages[0], messages[2]],
 
830
                                [{"type": "users",
 
831
                                  "create-groups": [{"gid": 1001,
 
832
                                                     "name": "sales"}]},
 
833
                                 {"type": "users", "operation-id": 123,
 
834
                                  "create-groups": [{"gid": 1002,
 
835
                                                     "name": "bizdev"}]}])
 
836
 
 
837
        groups = [("sales", "x", 1001, [])]
 
838
        self.setup_environment([], groups, None)
 
839
        result = self.manager.dispatch_message(
 
840
            {"groupname": "bizdev",
 
841
             "type": "add-group",
 
842
             "operation-id": 123})
 
843
        result.addCallback(handle_callback)
 
844
        return result
 
845
 
 
846
 
 
847
    def test_edit_group_event(self):
 
848
        """
 
849
        When an C{edit-group} message is received the specified group
 
850
        should be edited. This causes the originally named group to be
 
851
        removed and replaced with a newly named group with the new name.
 
852
        This generates C{users} message with details about the change
 
853
        and an C{operation-result} with details of the outcome of the
 
854
        operation.
 
855
        """
 
856
        def handle_callback(result):
 
857
            messages = self.broker_service.message_store.get_pending_messages()
 
858
            self.assertEquals(len(messages), 3)
 
859
            # Ignore the message created when the initial snapshot was
 
860
            # taken before the operation was performed.
 
861
            self.assertMessages(messages,
 
862
                                [{"create-groups": [{"gid": 50,
 
863
                                                     "name": "sales"}],
 
864
                                  "timestamp": 0,
 
865
                                  "type": "users"},
 
866
                                 {"type": "operation-result",
 
867
                                  "status": SUCCEEDED,
 
868
                                  "operation-id": 123, "timestamp": 0,
 
869
                                  "result-text": "set_group_details succeeded"},
 
870
                                 {"delete-groups": ["sales"],
 
871
                                  "create-groups": [{"gid": 50,
 
872
                                                     "name": "bizdev"}],
 
873
                                  "timestamp": 0,
 
874
                                  "operation-id": 123,
 
875
                                  "type": "users"},])
 
876
 
 
877
 
 
878
        groups = [("sales", "x", 50, [])]
 
879
        self.setup_environment([], groups, None)
 
880
        result = self.manager.dispatch_message(
 
881
            {"groupname": "sales",
 
882
             "new-name": "bizdev",
 
883
             "type": "edit-group",
 
884
             "operation-id": 123})
 
885
        result.addCallback(handle_callback)
 
886
        return result
 
887
 
 
888
    def test_edit_group_event_in_sync(self):
 
889
        """
 
890
        The client and server should be in sync after an C{edit-group}
 
891
        event is received and processed.  In other words, a snapshot
 
892
        should have been taken after the operation was handled.
 
893
        """
 
894
        def handle_callback1(result):
 
895
            message_store = self.broker_service.message_store
 
896
            messages = message_store.get_pending_messages()
 
897
            self.assertTrue(messages)
 
898
            result = plugin.run()
 
899
            result.addCallback(handle_callback2, messages)
 
900
            return result
 
901
 
 
902
        def handle_callback2(result, messages):
 
903
            message_store = self.broker_service.message_store
 
904
            new_messages = message_store.get_pending_messages()
 
905
            self.assertEquals(messages, new_messages)
 
906
 
 
907
        groups = [("sales", "x", 50, [])]
 
908
        plugin = self.setup_environment([], groups, None)
 
909
        result = self.manager.dispatch_message(
 
910
            {"gid": 50,
 
911
             "groupname": "sales",
 
912
             "new-name": "bizdev",
 
913
             "operation-id": 123,
 
914
             "type": "edit-group"})
 
915
        result.addCallback(handle_callback1)
 
916
        return result
 
917
 
 
918
    def test_edit_group_event_with_external_changes(self):
 
919
        """
 
920
        If external user changes have been made but not detected by
 
921
        the client before an C{edit-group} event is received, the
 
922
        client should first detect changes and then perform the
 
923
        operation.  The results should be reported in separate
 
924
        messages.
 
925
        """
 
926
        def handle_callback1(result):
 
927
            result = self.manager.dispatch_message(
 
928
                {"groupname": "sales", "new-name": "webdev",
 
929
                 "operation-id": 123, "type": "edit-group"})
 
930
 
 
931
            result.addCallback(handle_callback2)
 
932
            return result
 
933
 
 
934
        def handle_callback2(result):
 
935
            message_store = self.broker_service.message_store
 
936
            messages = message_store.get_pending_messages()
 
937
            self.assertEquals(len(messages), 3)
 
938
            self.assertMessages([messages[0], messages[2]],
 
939
                                [{"type": "users",
 
940
                                  "create-groups": [{"gid": 1001,
 
941
                                                     "name": "sales"}]},
 
942
                                 {"type": "users",
 
943
                                  "operation-id": 123,
 
944
                                  "delete-groups": ["sales"],
 
945
                                  "create-groups": [{"gid": 1001,
 
946
                                                     "name": "webdev"}]
 
947
                                                    }])
 
948
 
 
949
 
 
950
        groups = [("sales", "x", 1001, [])]
 
951
        plugin = self.setup_environment([], groups, None)
 
952
        result = plugin.run()
 
953
        result.addCallback(handle_callback1)
 
954
        return result
 
955
 
 
956
 
 
957
    def test_add_group_member_event(self):
 
958
        """
 
959
        When an C{add-group-member} message is received the new user
 
960
        should be added to the group.  Two messages should be
 
961
        generated: a C{users} message with details about the change
 
962
        and an C{operation-result} with details of the outcome of the
 
963
        operation.
 
964
        """
 
965
        def handle_callback(result):
 
966
            messages = self.broker_service.message_store.get_pending_messages()
 
967
            self.assertEquals(len(messages), 3)
 
968
            # Ignore the message created when the initial snapshot was
 
969
            # taken before the operation was performed.
 
970
            self.assertMessages([messages[2], messages[1]],
 
971
                                [{"type": "users", "timestamp": 0,
 
972
                                  "operation-id": 123,
 
973
                                  "create-group-members": {"bizdev": ["jdoe"]}},
 
974
                                 {"type": "operation-result",
 
975
                                  "timestamp": 0,
 
976
                                  "status": SUCCEEDED,
 
977
                                  "operation-id": 123,
 
978
                                  "result-text": "add_group_member succeeded"}])
 
979
 
 
980
 
 
981
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/bin/sh",
 
982
                  "/home/jdoe")]
 
983
        groups = [("bizdev", "x", 1001, [])]
 
984
        self.setup_environment(users, groups, None)
 
985
        result = self.manager.dispatch_message(
 
986
            {"username": "jdoe",
 
987
             "groupname": "bizdev",
 
988
             "operation-id": 123,
 
989
             "type": "add-group-member"})
 
990
        result.addCallback(handle_callback)
 
991
        return result
 
992
 
 
993
 
 
994
    def test_add_group_member_with_username_and_groupname_event(self):
 
995
        """
 
996
        When an C{add-group-member} message is received with a
 
997
        username and group name, instead of a UID and GID, the new
 
998
        user should be added to the group.  Two messages should be
 
999
        generated: a C{users} message with details about the change
 
1000
        and an C{operation-result} with details of the outcome of the
 
1001
        operation.
 
1002
        """
 
1003
        def handle_callback(result):
 
1004
            messages = self.broker_service.message_store.get_pending_messages()
 
1005
            self.assertEquals(len(messages), 3)
 
1006
            # Ignore the message created when the initial snapshot was
 
1007
            # taken before the operation was performed.
 
1008
            self.assertMessages([messages[2], messages[1]],
 
1009
                               [{"type": "users", "timestamp": 0,
 
1010
                                  "operation-id": 123,
 
1011
                                  "create-group-members": {"bizdev": ["jdoe"]}},
 
1012
                                 {"type": "operation-result", "timestamp": 0,
 
1013
                                  "status": SUCCEEDED, "operation-id": 123,
 
1014
                                 "result-text": "add_group_member succeeded"}
 
1015
                                ])
 
1016
 
 
1017
 
 
1018
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/bin/sh",
 
1019
                  "/home/jdoe")]
 
1020
        groups = [("bizdev", "x", 1001, [])]
 
1021
        self.setup_environment(users, groups, None)
 
1022
        result = self.manager.dispatch_message(
 
1023
            {"username": "jdoe",
 
1024
             "groupname": "bizdev",
 
1025
             "type": "add-group-member",
 
1026
             "operation-id": 123})
 
1027
        result.addCallback(handle_callback)
 
1028
        return result
 
1029
 
 
1030
    def test_add_group_member_event_in_sync(self):
 
1031
        """
 
1032
        The client and server should be in sync after an
 
1033
        C{add-group-member} event is received and processed.  In other
 
1034
        words, a snapshot should have been taken after the operation
 
1035
        was handled.
 
1036
        """
 
1037
        def handle_callback(result):
 
1038
            message_store = self.broker_service.message_store
 
1039
            messages = message_store.get_pending_messages()
 
1040
            self.assertTrue(messages)
 
1041
            result = plugin.run()
 
1042
            result.addCallback(handle_callback2, messages)
 
1043
            return result
 
1044
 
 
1045
        def handle_callback2(result, messages):
 
1046
            message_store = self.broker_service.message_store
 
1047
            new_messages = message_store.get_pending_messages()
 
1048
            self.assertEquals(messages, new_messages)
 
1049
 
 
1050
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/bin/sh",
 
1051
                  "/home/jdoe")]
 
1052
        groups = [("bizdev", "x", 1001, ["jdoe"])]
 
1053
        plugin = self.setup_environment(users, groups, None)
 
1054
        result = self.manager.dispatch_message(
 
1055
            {"username": u"jdoe",
 
1056
             "groupname": u"bizdev",
 
1057
             "type": "add-group-member",
 
1058
             "operation-id": 123})
 
1059
        result.addCallback(handle_callback)
 
1060
        return result
 
1061
 
 
1062
    def test_add_group_member_event_with_external_changes(self):
 
1063
        """
 
1064
        If external user changes have been made but not detected by
 
1065
        the client before an C{add-group-member} event is received,
 
1066
        the client should first detect changes and then perform the
 
1067
        operation.  The results should be reported in separate
 
1068
        messages.
 
1069
        """
 
1070
        def handle_callback(result):
 
1071
            messages = self.broker_service.message_store.get_pending_messages()
 
1072
            self.assertEquals(len(messages), 3)
 
1073
            self.assertMessages([messages[0], messages[2]],
 
1074
                                [{"type": "users",
 
1075
                                  "create-users": [{"home-phone": None,
 
1076
                                                    "username": "jdoe",
 
1077
                                                    "uid": 1000,
 
1078
                                                    "enabled": True,
 
1079
                                                    "location": None,
 
1080
                                                    "work-phone": None,
 
1081
                                                    "primary-gid": 1000,
 
1082
                                                    "name": "John Doe"}],
 
1083
                                  "create-groups": [{"gid": 1001,
 
1084
                                                     "name": "bizdev"}]},
 
1085
                                 {"type": "users", "operation-id": 123,
 
1086
                                  "create-group-members": {"bizdev": ["jdoe"]}}])
 
1087
 
 
1088
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/bin/sh",
 
1089
                  "/home/jdoe")]
 
1090
        groups = [("bizdev", "x", 1001, [])]
 
1091
        plugin = self.setup_environment(users, groups, None)
 
1092
        result = self.manager.dispatch_message(
 
1093
            {"username": "jdoe",
 
1094
             "groupname": "bizdev",
 
1095
             "type": "add-group-member",
 
1096
             "operation-id": 123})
 
1097
        result.addCallback(handle_callback)
 
1098
        return result
 
1099
 
 
1100
    def test_remove_group_member_event(self):
 
1101
        """
 
1102
        When an C{add-group-member} message is received the user
 
1103
        should be removed from the group.  Two messages should be
 
1104
        generated: a C{users} message with details about the change
 
1105
        and an C{operation-result} with details of the outcome of the
 
1106
        operation.
 
1107
        """
 
1108
        def handle_callback(result):
 
1109
            messages = self.broker_service.message_store.get_pending_messages()
 
1110
            self.assertEquals(len(messages), 3)
 
1111
            # Ignore the message created by plugin.run.
 
1112
            self.assertMessages([messages[2], messages[1]],
 
1113
                [{"type": "users", "timestamp": 0,
 
1114
                  "operation-id": 123,
 
1115
                  "delete-group-members": {"bizdev": ["jdoe"]}},
 
1116
                 {"type": "operation-result",
 
1117
                  "status": SUCCEEDED,
 
1118
                  "operation-id": 123, "timestamp": 0,
 
1119
                  "result-text": "remove_group_member succeeded"}])
 
1120
 
 
1121
 
 
1122
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/bin/sh",
 
1123
                  "/home/jdoe")]
 
1124
        groups = [("bizdev", "x", 1001, ["jdoe"])]
 
1125
        self.setup_environment(users, groups, None)
 
1126
        result = self.manager.dispatch_message(
 
1127
            {"username": "jdoe", "groupname": "bizdev",
 
1128
             "type": "remove-group-member", "operation-id": 123})
 
1129
        result.addCallback(handle_callback)
 
1130
        return result
 
1131
 
 
1132
    def test_remove_group_member_event_in_sync(self):
 
1133
        """
 
1134
        The client and server should be in sync after an
 
1135
        C{remove-group-member} event is received and processed.  In
 
1136
        other words, a snapshot should have been taken after the
 
1137
        operation was handled.
 
1138
        """
 
1139
        def handle_callback1(result):
 
1140
            message_store = self.broker_service.message_store
 
1141
            messages = self.broker_service.message_store.get_pending_messages()
 
1142
            self.assertTrue(messages)
 
1143
            result = plugin.run()
 
1144
            result.addCallback(handle_callback2, messages)
 
1145
            return result
 
1146
 
 
1147
        def handle_callback2(result, messages):
 
1148
            message_store = self.broker_service.message_store
 
1149
            new_messages = message_store.get_pending_messages()
 
1150
            self.assertEquals(messages, new_messages)
 
1151
 
 
1152
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/bin/sh",
 
1153
                  "/home/jdoe")]
 
1154
        groups = [("bizdev", "x", 1001, ["jdoe"])]
 
1155
        plugin = self.setup_environment(users, groups, None)
 
1156
        result = self.manager.dispatch_message(
 
1157
            {"username": "jdoe", "groupname": "bizdev",
 
1158
             "type": "remove-group-member",
 
1159
             "operation-id": 123})
 
1160
        result.addCallback(handle_callback1)
 
1161
        return result
 
1162
 
 
1163
    def test_remove_group_member_event_with_external_changes(self):
 
1164
        """
 
1165
        If external user changes have been made but not detected by
 
1166
        the client before a C{remove-group-member} event is received,
 
1167
        the client should first detect changes and then perform the
 
1168
        operation.  The results should be reported in separate
 
1169
        messages.
 
1170
        """
 
1171
        def handle_callback(result):
 
1172
            messages = self.broker_service.message_store.get_pending_messages()
 
1173
            self.assertEquals(len(messages), 3)
 
1174
            self.assertMessages([messages[0], messages[2]],
 
1175
                                [{"timestamp": 0, "type": "users",
 
1176
                                  "create-users": [{"home-phone": None,
 
1177
                                                    "username": "jdoe",
 
1178
                                                    "uid": 1000,
 
1179
                                                    "enabled": True,
 
1180
                                                    "location": None,
 
1181
                                                    "work-phone": None,
 
1182
                                                    "primary-gid": 1000,
 
1183
                                                    "name": "John Doe"}],
 
1184
                                  "create-groups": [{"gid": 1001,
 
1185
                                                     "name": "bizdev"}],
 
1186
                                  "create-group-members": {"bizdev": ["jdoe"]}},
 
1187
                                 {"type": "users", "operation-id": 123,
 
1188
                                  "delete-group-members": {"bizdev": ["jdoe"]}}])
 
1189
 
 
1190
        users = [("jdoe", "x", 1000, 1000, "John Doe,,,,", "/bin/sh",
 
1191
                  "/home/jdoe")]
 
1192
        groups = [("bizdev", "x", 1001, ["jdoe"])]
 
1193
        self.setup_environment(users, groups, None)
 
1194
        result = self.manager.dispatch_message(
 
1195
            {"groupname": "bizdev",
 
1196
             "username": "jdoe",
 
1197
             "type": "remove-group-member",
 
1198
             "operation-id": 123})
 
1199
        result.addCallback(handle_callback)
 
1200
        return result
 
1201
 
 
1202
    def test_remove_group_event(self):
 
1203
        """
 
1204
        When a C{remove-group} message is received the specified group
 
1205
        should be removeed.  Two messages should be generated: a
 
1206
        C{users} message with details about the change and an
 
1207
        C{operation-result} with details of the outcome of the
 
1208
        operation.
 
1209
        """
 
1210
        def handle_callback1(result):
 
1211
 
 
1212
            result = self.manager.dispatch_message(
 
1213
                {"groupname": "sales", "type": "remove-group",
 
1214
                 "operation-id": 123})
 
1215
 
 
1216
            result.addCallback(handle_callback2)
 
1217
            return result
 
1218
 
 
1219
        def handle_callback2(result):
 
1220
            message_store = self.broker_service.message_store
 
1221
            messages = message_store.get_pending_messages()
 
1222
            self.assertEquals(len(messages), 3)
 
1223
            # Ignore the message created when the initial snapshot was
 
1224
            # taken before the operation was performed.
 
1225
            self.assertMessages([messages[2], messages[1]],
 
1226
                                [{"type": "users", "timestamp": 0,
 
1227
                                  "operation-id": 123,
 
1228
                                  "delete-groups": ["sales"]},
 
1229
                                 {"type": "operation-result",
 
1230
                                  "status": SUCCEEDED,
 
1231
                                  "operation-id": 123, "timestamp": 0,
 
1232
                                  "result-text": "remove_group succeeded"}])
 
1233
 
 
1234
        groups = [("sales", "x", 1001, ["jdoe"])]
 
1235
        plugin = self.setup_environment([], groups, None)
 
1236
        result = plugin.run()
 
1237
        result.addCallback(handle_callback1)
 
1238
        return result
 
1239
 
 
1240
    def test_remove_group_event_in_sync(self):
 
1241
        """
 
1242
        The client and server should be in sync after a
 
1243
        C{remove-group} event is received and processed.  In other
 
1244
        words, a snapshot should have been taken after the operation
 
1245
        was handled.
 
1246
        """
 
1247
        def handle_callback1(result):
 
1248
            message_store = self.broker_service.message_store
 
1249
            messages = message_store.get_pending_messages()
 
1250
            self.assertTrue(messages)
 
1251
            result = plugin.run()
 
1252
            result.addCallback(handle_callback2, messages)
 
1253
            return result
 
1254
 
 
1255
        def handle_callback2(result, messages):
 
1256
            message_store = self.broker_service.message_store
 
1257
            new_messages = message_store.get_pending_messages()
 
1258
            self.assertEquals(messages, new_messages)
 
1259
 
 
1260
        groups = [("sales", "x", 50, [])]
 
1261
        plugin = self.setup_environment([], groups, None)
 
1262
        result = self.manager.dispatch_message(
 
1263
            {"groupname": "sales",
 
1264
             "operation-id": 123,
 
1265
             "type": "remove-group"})
 
1266
        result.addCallback(handle_callback1)
 
1267
        return result
 
1268
 
 
1269
    def test_remove_group_event_with_external_changes(self):
 
1270
        """
 
1271
        If external user changes have been made but not detected by
 
1272
        the client before a C{remove-group} event is received, the
 
1273
        client should first detect changes and then perform the
 
1274
        operation.  The results should be reported in separate
 
1275
        messages.
 
1276
        """
 
1277
        def handle_callback1(result):
 
1278
            result = self.manager.dispatch_message(
 
1279
                {"groupname": "sales", "operation-id": 123,
 
1280
                 "type": "remove-group"})
 
1281
            result.addCallback(handle_callback2)
 
1282
            return result
 
1283
 
 
1284
        def handle_callback2(result):
 
1285
            message_store = self.broker_service.message_store
 
1286
            messages = message_store.get_pending_messages()
 
1287
            self.assertEquals(len(messages), 3)
 
1288
            self.assertMessages([messages[0], messages[2]],
 
1289
                                [{"type": "users",
 
1290
                                  "create-groups": [{"gid": 1001,
 
1291
                                                     "name": "sales"}]},
 
1292
                                 {"type": "users",
 
1293
                                  "delete-groups": ["sales"],
 
1294
                                  "operation-id": 123}])
 
1295
 
 
1296
        groups = [("sales", "x", 1001, [])]
 
1297
        plugin = self.setup_environment([], groups, None)
 
1298
        result = plugin.run()
 
1299
        result.addCallback(handle_callback1)
 
1300
        return result