Merge lp:~nataliabidart/ubuntuone-control-panel/replication-to-the-backend into lp:ubuntuone-control-panel
- replication-to-the-backend
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Natalia Bidart |
Approved revision: | 45 |
Merged at revision: | 44 |
Proposed branch: | lp:~nataliabidart/ubuntuone-control-panel/replication-to-the-backend |
Merge into: | lp:ubuntuone-control-panel |
Diff against target: |
1056 lines (+677/-172) 7 files modified
ubuntuone/controlpanel/backend.py (+41/-1) ubuntuone/controlpanel/dbus_service.py (+41/-0) ubuntuone/controlpanel/integrationtests/test_dbus_service.py (+55/-7) ubuntuone/controlpanel/replication_client.py (+115/-0) ubuntuone/controlpanel/tests/__init__.py (+149/-1) ubuntuone/controlpanel/tests/test_backend.py (+116/-163) ubuntuone/controlpanel/tests/test_replication_client.py (+160/-0) |
To merge this branch: | bzr merge lp:~nataliabidart/ubuntuone-control-panel/replication-to-the-backend |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Eric Casteleijn (community) | Approve | ||
Review via email: mp+45439@code.launchpad.net |
Commit message
Desktopcouch replication service is managed in the backend and exposed in the DBus service (LP: #696782).
Description of the change
There is no easy way to test this except by running the backen with:
DEBUG=True PYTHONPATH=. ./bin/ubuntuone
and using d-feet to play with the replication methods. The merge proposal:
https:/
provides the GUI that accesses this service directly.
- 45. By Natalia Bidart
-
Forgot some files!
Eric Casteleijn (thisfred) wrote : | # |
Eric Casteleijn (thisfred) wrote : | # |
I'm not sure I understand the 'dependency' field in the replication record. Bookmarks and contacts should be replicated, or at least replicatable, regardless of what is installed. Am I missing the point?
Eric Casteleijn (thisfred) wrote : | # |
Looks good otherwise.
Roberto Alsina (ralsina) wrote : | # |
Approved. I agree with Eric and Natalia (on IRC) that the spec needs some improvements because what dbs you replicate shouldn't depend on installed packages, but this is according to the current spec.
Preview Diff
1 | === modified file 'ubuntuone/controlpanel/backend.py' |
2 | --- ubuntuone/controlpanel/backend.py 2010-12-23 18:20:56 +0000 |
3 | +++ ubuntuone/controlpanel/backend.py 2011-01-06 20:44:10 +0000 |
4 | @@ -22,6 +22,7 @@ |
5 | from twisted.internet.defer import inlineCallbacks, returnValue |
6 | |
7 | from ubuntuone.controlpanel import dbus_client |
8 | +from ubuntuone.controlpanel import replication_client |
9 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
10 | from ubuntuone.controlpanel.webclient import WebClient |
11 | |
12 | @@ -48,6 +49,9 @@ |
13 | MSG_KEY = 'message' |
14 | STATUS_KEY = 'status' |
15 | |
16 | +BOOKMARKS_PKG = 'xul-ext-bindwood' |
17 | +CONTACTS_PKG = 'evolution-couchdb' |
18 | + |
19 | |
20 | def bool_str(value): |
21 | """Return the string representation of a bool (dbus-compatible).""" |
22 | @@ -300,7 +304,7 @@ |
23 | |
24 | """ |
25 | if 'subscribed' in settings: |
26 | - subscribed = settings['subscribed'] |
27 | + subscribed = bool(settings['subscribed']) |
28 | if subscribed: |
29 | yield self.subscribe_volume(volume_id) |
30 | else: |
31 | @@ -321,6 +325,42 @@ |
32 | yield dbus_client.unsubscribe_folder(volume_id) |
33 | |
34 | @log_call(logger.debug) |
35 | + @inlineCallbacks |
36 | + def replications_info(self): |
37 | + """Get the user replications info.""" |
38 | + replications = yield replication_client.get_replications() |
39 | + exclusions = yield replication_client.get_exclusions() |
40 | + |
41 | + result = [] |
42 | + for rep in replications: |
43 | + dependency = '' |
44 | + if rep == replication_client.BOOKMARKS: |
45 | + dependency = BOOKMARKS_PKG |
46 | + elif rep == replication_client.CONTACTS: |
47 | + dependency = CONTACTS_PKG |
48 | + |
49 | + repd = { |
50 | + "replication_id": rep, |
51 | + "name": rep, # this may change to be more user friendly |
52 | + "enabled": bool_str(rep not in exclusions), |
53 | + "dependency": dependency, |
54 | + } |
55 | + result.append(repd) |
56 | + |
57 | + returnValue(result) |
58 | + |
59 | + @log_call(logger.info) |
60 | + @inlineCallbacks |
61 | + def change_replication_settings(self, replication_id, settings): |
62 | + """Change the settings for the given replication.""" |
63 | + if 'enabled' in settings: |
64 | + if bool(settings['enabled']): |
65 | + yield replication_client.replicate(replication_id) |
66 | + else: |
67 | + yield replication_client.exclude(replication_id) |
68 | + returnValue(replication_id) |
69 | + |
70 | + @log_call(logger.debug) |
71 | def query_bookmark_extension(self): |
72 | """True if the bookmark extension has been installed.""" |
73 | # still pending (LP: #673672) |
74 | |
75 | === modified file 'ubuntuone/controlpanel/dbus_service.py' |
76 | --- ubuntuone/controlpanel/dbus_service.py 2010-12-23 18:20:56 +0000 |
77 | +++ ubuntuone/controlpanel/dbus_service.py 2011-01-06 20:44:10 +0000 |
78 | @@ -339,6 +339,47 @@ |
79 | |
80 | @log_call(logger.debug) |
81 | @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="") |
82 | + def replications_info(self): |
83 | + """Return the replications info.""" |
84 | + d = self.backend.replications_info() |
85 | + d.addCallback(self.ReplicationsInfoReady) |
86 | + d.addErrback(transform_failure(self.ReplicationsInfoError)) |
87 | + |
88 | + @log_call(logger.debug) |
89 | + @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}") |
90 | + def ReplicationsInfoReady(self, info): |
91 | + """The replications info is ready.""" |
92 | + |
93 | + @log_call(logger.error) |
94 | + @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}") |
95 | + def ReplicationsInfoError(self, error): |
96 | + """Problem getting the replications info.""" |
97 | + |
98 | + #--- |
99 | + |
100 | + @log_call(logger.info) |
101 | + @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="sa{ss}") |
102 | + def change_replication_settings(self, replication_id, settings): |
103 | + """Configure a given replication.""" |
104 | + d = self.backend.change_replication_settings(replication_id, settings) |
105 | + d.addCallback(self.ReplicationSettingsChanged) |
106 | + d.addErrback(transform_failure(self.ReplicationSettingsChangeError), |
107 | + replication_id) |
108 | + |
109 | + @log_call(logger.info) |
110 | + @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") |
111 | + def ReplicationSettingsChanged(self, replication_id): |
112 | + """The settings for the replication were changed.""" |
113 | + |
114 | + @log_call(logger.error) |
115 | + @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="sa{ss}") |
116 | + def ReplicationSettingsChangeError(self, replication_id, error): |
117 | + """Problem changing settings for the replication.""" |
118 | + |
119 | + #--- |
120 | + |
121 | + @log_call(logger.debug) |
122 | + @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="") |
123 | def query_bookmark_extension(self): |
124 | """Check if the extension to sync bookmarks is installed.""" |
125 | d = self.backend.query_bookmark_extension() |
126 | |
127 | === modified file 'ubuntuone/controlpanel/integrationtests/test_dbus_service.py' |
128 | --- ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2010-12-23 18:20:56 +0000 |
129 | +++ ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2011-01-06 20:44:10 +0000 |
130 | @@ -84,6 +84,11 @@ |
131 | }, |
132 | ] |
133 | |
134 | +SAMPLE_REPLICATIONS_INFO = [ |
135 | + {'replication_id': 'yadda', 'wait for it': 'awesome'}, |
136 | + {'replication_id': 'yoda', 'something else': 'awesome'}, |
137 | +] |
138 | + |
139 | |
140 | class DBusServiceMainTestCase(mocker.MockerTestCase): |
141 | """Tests for the main function.""" |
142 | @@ -166,6 +171,19 @@ |
143 | """Configure a given volume.""" |
144 | return self._process(volume_id) |
145 | |
146 | + def replications_info(self): |
147 | + """Start the replication exclusion service if needed. |
148 | + |
149 | + Return the replication info, which is a dictionary of (replication |
150 | + name, enabled). |
151 | + |
152 | + """ |
153 | + return self._process(SAMPLE_REPLICATIONS_INFO) |
154 | + |
155 | + def change_replication_settings(self, replication_id, settings): |
156 | + """Configure a given replication.""" |
157 | + return self._process(replication_id) |
158 | + |
159 | def query_bookmark_extension(self): |
160 | """True if the bookmark extension has been installed.""" |
161 | return self._process(False) |
162 | @@ -258,13 +276,13 @@ |
163 | self.assertEqual(expected, result) |
164 | |
165 | |
166 | -class OperationsTestCase(TestCase): |
167 | - """Test for the DBus service operations.""" |
168 | +class BaseTestCase(TestCase): |
169 | + """Base test case for the DBus service.""" |
170 | |
171 | timeout = 3 |
172 | |
173 | def setUp(self): |
174 | - super(OperationsTestCase, self).setUp() |
175 | + super(BaseTestCase, self).setUp() |
176 | dbus_service.init_mainloop() |
177 | be = dbus_service.publish_backend(MockBackend()) |
178 | self.addCleanup(be.remove_from_connection) |
179 | @@ -279,7 +297,7 @@ |
180 | def tearDown(self): |
181 | self.backend = None |
182 | self.deferred = None |
183 | - super(OperationsTestCase, self).tearDown() |
184 | + super(BaseTestCase, self).tearDown() |
185 | |
186 | def got_error(self, *a): |
187 | """Some error happened in the DBus call.""" |
188 | @@ -322,6 +340,10 @@ |
189 | |
190 | return self.deferred |
191 | |
192 | + |
193 | +class OperationsTestCase(BaseTestCase): |
194 | + """Test for the DBus service operations.""" |
195 | + |
196 | def test_account_info_returned(self): |
197 | """The account info is successfully returned.""" |
198 | |
199 | @@ -416,9 +438,9 @@ |
200 | def test_volumes_info(self): |
201 | """The volumes info is reported.""" |
202 | |
203 | - def got_signal(volumes_dict): |
204 | + def got_signal(volumes): |
205 | """The correct info was received.""" |
206 | - self.assertEqual(volumes_dict, SAMPLE_VOLUMES_INFO) |
207 | + self.assertEqual(volumes, SAMPLE_VOLUMES_INFO) |
208 | self.deferred.callback("success") |
209 | |
210 | args = ("VolumesInfoReady", "VolumesInfoError", got_signal, |
211 | @@ -439,6 +461,32 @@ |
212 | expected_volume_id, {'subscribed': ''}) |
213 | return self.assert_correct_method_call(*args) |
214 | |
215 | + def test_replications_info(self): |
216 | + """The replications info is reported.""" |
217 | + |
218 | + def got_signal(replications): |
219 | + """The correct info was received.""" |
220 | + self.assertEqual(replications, SAMPLE_REPLICATIONS_INFO) |
221 | + self.deferred.callback("success") |
222 | + |
223 | + args = ("ReplicationsInfoReady", "ReplicationsInfoError", got_signal, |
224 | + self.backend.replications_info) |
225 | + return self.assert_correct_method_call(*args) |
226 | + |
227 | + def test_change_replication_settings(self): |
228 | + """The replication settings are successfully changed.""" |
229 | + expected_replication_id = SAMPLE_REPLICATIONS_INFO[0]['replication_id'] |
230 | + |
231 | + def got_signal(replication_id): |
232 | + """The correct replication was changed.""" |
233 | + self.assertEqual(replication_id, expected_replication_id) |
234 | + self.deferred.callback("success") |
235 | + |
236 | + args = ("ReplicationSettingsChanged", "ReplicationSettingsChangeError", |
237 | + got_signal, self.backend.change_replication_settings, |
238 | + expected_replication_id, {'enabled': ''}) |
239 | + return self.assert_correct_method_call(*args) |
240 | + |
241 | def test_query_bookmarks_extension(self): |
242 | """The bookmarks extension is queried.""" |
243 | |
244 | @@ -495,7 +543,7 @@ |
245 | error_sig, success_sig, got_error_signal, method, *args) |
246 | |
247 | |
248 | -class FileSyncTestCase(OperationsTestCase): |
249 | +class FileSyncTestCase(BaseTestCase): |
250 | """Test for the DBus service when requesting file sync status.""" |
251 | |
252 | def assert_correct_status_signal(self, status, sync_signal, |
253 | |
254 | === added file 'ubuntuone/controlpanel/replication_client.py' |
255 | --- ubuntuone/controlpanel/replication_client.py 1970-01-01 00:00:00 +0000 |
256 | +++ ubuntuone/controlpanel/replication_client.py 2011-01-06 20:44:10 +0000 |
257 | @@ -0,0 +1,115 @@ |
258 | +# -*- coding: utf-8 -*- |
259 | + |
260 | +# Authors: Natalia B. Bidart <nataliabidart@canonical.com> |
261 | +# |
262 | +# Copyright 2010 Canonical Ltd. |
263 | +# |
264 | +# This program is free software: you can redistribute it and/or modify it |
265 | +# under the terms of the GNU General Public License version 3, as published |
266 | +# by the Free Software Foundation. |
267 | +# |
268 | +# This program is distributed in the hope that it will be useful, but |
269 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
270 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
271 | +# PURPOSE. See the GNU General Public License for more details. |
272 | +# |
273 | +# You should have received a copy of the GNU General Public License along |
274 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
275 | + |
276 | +"""Client to use replication services.""" |
277 | + |
278 | +from twisted.internet.defer import Deferred, inlineCallbacks, returnValue |
279 | + |
280 | +from ubuntuone.controlpanel.logger import setup_logging |
281 | + |
282 | + |
283 | +logger = setup_logging('replication_client') |
284 | + |
285 | +BOOKMARKS = 'bookmarks' |
286 | +CONTACTS = 'contacts' |
287 | +# we should get this list from somewhere else |
288 | +REPLICATIONS = set([BOOKMARKS, CONTACTS]) |
289 | + |
290 | + |
291 | +class ReplicationError(Exception): |
292 | + """A replication error.""" |
293 | + |
294 | + |
295 | +class NoPairingRecord(ReplicationError): |
296 | + """There is no pairing record.""" |
297 | + |
298 | + |
299 | +class InvalidIdError(ReplicationError): |
300 | + """The replication id is not valid.""" |
301 | + |
302 | + |
303 | +class NotExcludedError(ReplicationError): |
304 | + """The replication can not be replicated since is not excluded.""" |
305 | + |
306 | + |
307 | +class AlreadyExcludedError(ReplicationError): |
308 | + """The replication can not be excluded since is already excluded.""" |
309 | + |
310 | + |
311 | +def get_replication_proxy(replication_module=None): |
312 | + """Return a proxy to the replication client.""" |
313 | + d = Deferred() |
314 | + if replication_module is None: |
315 | + # delay import in case DC is not installed at module import time |
316 | + # Unable to import 'desktopcouch.application.replication_services' |
317 | + # pylint: disable=F0401 |
318 | + from desktopcouch.application.replication_services \ |
319 | + import ubuntuone as replication_module |
320 | + try: |
321 | + result = replication_module.ReplicationExclusion() |
322 | + except ValueError: |
323 | + d.errback(NoPairingRecord()) |
324 | + else: |
325 | + d.callback(result) |
326 | + |
327 | + return d |
328 | + |
329 | + |
330 | +@inlineCallbacks |
331 | +def get_replications(): |
332 | + """Retrieve the list of replications.""" |
333 | + yield get_replication_proxy() |
334 | + returnValue(REPLICATIONS) |
335 | + |
336 | + |
337 | +@inlineCallbacks |
338 | +def get_exclusions(): |
339 | + """Retrieve the list of exclusions.""" |
340 | + proxy = yield get_replication_proxy() |
341 | + result = proxy.all_exclusions() |
342 | + returnValue(result) |
343 | + |
344 | + |
345 | +@inlineCallbacks |
346 | +def replicate(replication_id): |
347 | + """Remove replication_id from the exclusions list.""" |
348 | + replications = yield get_replications() |
349 | + if replication_id not in replications: |
350 | + raise InvalidIdError(replication_id) |
351 | + |
352 | + exclusions = yield get_exclusions() |
353 | + if replication_id not in exclusions: |
354 | + raise NotExcludedError(replication_id) |
355 | + |
356 | + proxy = yield get_replication_proxy() |
357 | + yield proxy.replicate(replication_id) |
358 | + |
359 | + |
360 | +@inlineCallbacks |
361 | +def exclude(replication_id): |
362 | + """Add replication_id to the exclusions list.""" |
363 | + replications = yield get_replications() |
364 | + if replication_id not in replications: |
365 | + raise InvalidIdError(replication_id) |
366 | + |
367 | + exclusions = yield get_exclusions() |
368 | + if replication_id in exclusions: |
369 | + raise AlreadyExcludedError(replication_id) |
370 | + |
371 | + proxy = yield get_replication_proxy() |
372 | + yield proxy.exclude(replication_id) |
373 | |
374 | === modified file 'ubuntuone/controlpanel/tests/__init__.py' |
375 | --- ubuntuone/controlpanel/tests/__init__.py 2010-12-20 16:11:13 +0000 |
376 | +++ ubuntuone/controlpanel/tests/__init__.py 2011-01-06 20:44:10 +0000 |
377 | @@ -24,9 +24,157 @@ |
378 | TOKEN = {u'consumer_key': u'xQ7xDAz', |
379 | u'consumer_secret': u'KzCJWCTNbbntwfyCKKjomJDzlgqxLy', |
380 | u'token_name': u'test', |
381 | - u'token': u'GkInOfSMGwTXAUoVQwLUoPxElEEUdhsLVNTPhxHJDUIeHCPNEo', |
382 | + u'token': u'ABCDEF01234-localtoken', |
383 | u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'} |
384 | |
385 | +SAMPLE_ACCOUNT_JSON = """ |
386 | +{ |
387 | + "username": "andrewpz", |
388 | + "openid": "https://login.launchpad.net/+id/abcdefg", |
389 | + "first_name": "Andrew P.", |
390 | + "last_name": "Zoilo", |
391 | + "couchdb": { |
392 | + "host": "https://couchdb.one.ubuntu.com", |
393 | + "root": "https://couchdb.one.ubuntu.com/u/abc/def/12345", |
394 | + "dbpath": "u/abc/def/12345" |
395 | + }, |
396 | + "couchdb_root": "https://couchdb.one.ubuntu.com/u/abc/def/12345", |
397 | + "email": "andrewpz@protocultura.net",%s |
398 | + "nickname": "Andrew P. Zoilo", |
399 | + "id": 12345, |
400 | + "subscription": { |
401 | + "upgrade_available": false, |
402 | + "description": "Paid Plan, 50 GB of storage", |
403 | + "trial": false, |
404 | + "started": "2010-03-24T18:38:38Z", |
405 | + "is_paid": true, |
406 | + "expires": null, |
407 | + "qty": 1, |
408 | + "price": 0.0, |
409 | + "currency": null, |
410 | + "id": 654321, |
411 | + "name": "50 GB" |
412 | + } |
413 | +} |
414 | +""" |
415 | + |
416 | +CURRENT_PLAN = "Ubuntu One Basic (2 GB) + 1 x 20-Pack with 20 GB (monthly)" |
417 | +SAMPLE_CURRENT_PLAN = '\n "current_plan": "%s",' % CURRENT_PLAN |
418 | + |
419 | +SAMPLE_ACCOUNT_NO_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % '' |
420 | +SAMPLE_ACCOUNT_WITH_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % SAMPLE_CURRENT_PLAN |
421 | + |
422 | + |
423 | +SAMPLE_QUOTA_JSON = """ |
424 | +{ |
425 | + "total": 53687091200, |
426 | + "used": 2350345156 |
427 | +} |
428 | +""" |
429 | + |
430 | +EXPECTED_ACCOUNT_INFO = { |
431 | + "quota_used": "2350345156", |
432 | + "quota_total": "53687091200", |
433 | + "type": "Paid Plan, 50 GB of storage", |
434 | + "name": "Andrew P. Zoilo", |
435 | + "email": "andrewpz@protocultura.net", |
436 | +} |
437 | + |
438 | +EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN = { |
439 | + "quota_used": "2350345156", |
440 | + "quota_total": "53687091200", |
441 | + "type": CURRENT_PLAN, |
442 | + "name": "Andrew P. Zoilo", |
443 | + "email": "andrewpz@protocultura.net", |
444 | +} |
445 | + |
446 | +SAMPLE_DEVICES_JSON = """ |
447 | +[ |
448 | + { |
449 | + "token": "ABCDEF01234token", |
450 | + "description": "Ubuntu One @ darkstar", |
451 | + "kind": "Computer" |
452 | + }, |
453 | + { |
454 | + "token": "ABCDEF01234-localtoken", |
455 | + "description": "Ubuntu One @ localhost", |
456 | + "kind": "Computer" |
457 | + }, |
458 | + { |
459 | + "kind": "Phone", |
460 | + "description": "Nokia E65", |
461 | + "id": 1000 |
462 | + } |
463 | +] |
464 | +""" |
465 | + |
466 | +EXPECTED_DEVICES_INFO = [ |
467 | + { |
468 | + "device_id": "ComputerABCDEF01234token", |
469 | + "name": "Ubuntu One @ darkstar", |
470 | + "type": "Computer", |
471 | + "is_local": '', |
472 | + "configurable": '', |
473 | + }, |
474 | + { |
475 | + 'is_local': 'True', |
476 | + 'configurable': 'True', |
477 | + 'device_id': 'ComputerABCDEF01234-localtoken', |
478 | + 'limit_bandwidth': '', |
479 | + 'max_download_speed': '-1', |
480 | + 'max_upload_speed': '-1', |
481 | + 'name': 'Ubuntu One @ localhost', |
482 | + 'type': 'Computer' |
483 | + }, |
484 | + { |
485 | + "device_id": "Phone1000", |
486 | + "name": "Nokia E65", |
487 | + "type": "Phone", |
488 | + "configurable": '', |
489 | + "is_local": '', |
490 | + }, |
491 | +] |
492 | + |
493 | +SAMPLE_FOLDERS = [ |
494 | + {u'generation': u'2', u'node_id': u'341da068-81d8-437a-8f75-5bb9d86455ba', |
495 | + u'path': u'/home/tester/Public', u'subscribed': u'True', |
496 | + u'suggested_path': u'~/Public', |
497 | + u'type': u'UDF', u'volume_id': u'9ea892f8-15fa-4201-bdbf-8de99fa5f588'}, |
498 | + {u'generation': u'', u'node_id': u'11fbc86c-0d7a-49f5-ae83-8402caf66c6a', |
499 | + u'path': u'/home/tester/Documents', u'subscribed': u'', |
500 | + u'suggested_path': u'~/Documents', |
501 | + u'type': u'UDF', u'volume_id': u'2db262f5-a151-4c19-969c-bb5ced753c61'}, |
502 | + {u'generation': u'24', u'node_id': u'9ee0e130-a7c7-4d76-a5e3-5df506221b48', |
503 | + u'path': u'/home/tester/Pictures/Photos', u'subscribed': u'True', |
504 | + u'suggested_path': u'~/Pictures/Photos', |
505 | + u'type': u'UDF', u'volume_id': u'1deb2874-3d28-46ae-9999-d5f48de9f460'}, |
506 | +] |
507 | + |
508 | +SAMPLE_SHARES = [ |
509 | + {u'accepted': u'True', u'access_level': u'View', |
510 | + u'free_bytes': u'39892622746', u'generation': u'2704', |
511 | + u'name': u're', u'node_id': u'c483f419-ed28-490a-825d-a8c074e2d795', |
512 | + u'other_username': u'otheruser', u'other_visible_name': u'Other User', |
513 | + u'path': u'/home/tester/.local/share/ubuntuone/shares/re from Other User', |
514 | + u'type': u'Share', u'volume_id': u'4a1b263b-a2b3-4f66-9e66-4cd18050810d'}, |
515 | + {u'accepted': u'True', u'access_level': u'Modify', |
516 | + u'free_bytes': u'39892622746', u'generation': u'2704', |
517 | + u'name': u'do', u'node_id': u'84544ea4-aefe-4f91-9bb9-ed7b0a805baf', |
518 | + u'other_username': u'otheruser', u'other_visible_name': u'Other User', |
519 | + u'path': u'/home/tester/.local/share/ubuntuone/shares/do from Other User', |
520 | + u'type': u'Share', u'volume_id': u'7d130dfe-98b2-4bd5-8708-9eeba9838ac0'}, |
521 | +] |
522 | + |
523 | +SAMPLE_SHARED = [ |
524 | + {u'accepted': u'True', u'access_level': u'View', |
525 | + u'free_bytes': u'', u'generation': u'', |
526 | + u'name': u'bar', u'node_id': u'31e47530-9448-4f03-b4dc-4154fdf35225', |
527 | + u'other_username': u'otheruser', u'other_visible_name': u'Other User', |
528 | + u'path': u'/home/tester/Ubuntu One/bar', |
529 | + u'type': u'Shared', |
530 | + u'volume_id': u'79584900-517f-4dff-b2f3-20e8c1e79365'}, |
531 | +] |
532 | + |
533 | |
534 | class TestCase(BaseTestCase): |
535 | """Basics for testing.""" |
536 | |
537 | === modified file 'ubuntuone/controlpanel/tests/test_backend.py' |
538 | --- ubuntuone/controlpanel/tests/test_backend.py 2010-12-23 18:20:56 +0000 |
539 | +++ ubuntuone/controlpanel/tests/test_backend.py 2011-01-06 20:44:10 +0000 |
540 | @@ -25,9 +25,9 @@ |
541 | from twisted.internet.defer import inlineCallbacks |
542 | from ubuntuone.devtools.handlers import MementoHandler |
543 | |
544 | -from ubuntuone.controlpanel import backend |
545 | -from ubuntuone.controlpanel.backend import (ACCOUNT_API, |
546 | - DEVICES_API, DEVICE_REMOVE_API, QUOTA_API, |
547 | +from ubuntuone.controlpanel import backend, replication_client |
548 | +from ubuntuone.controlpanel.backend import (bool_str, |
549 | + ACCOUNT_API, DEVICES_API, DEVICE_REMOVE_API, QUOTA_API, |
550 | FILE_SYNC_DISABLED, |
551 | FILE_SYNC_DISCONNECTED, |
552 | FILE_SYNC_ERROR, |
553 | @@ -37,160 +37,21 @@ |
554 | FILE_SYNC_UNKNOWN, |
555 | MSG_KEY, STATUS_KEY, |
556 | ) |
557 | - |
558 | -from ubuntuone.controlpanel.tests import TestCase |
559 | +from ubuntuone.controlpanel.tests import (TestCase, |
560 | + EXPECTED_ACCOUNT_INFO, |
561 | + EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN, |
562 | + EXPECTED_DEVICES_INFO, |
563 | + SAMPLE_ACCOUNT_NO_CURRENT_PLAN, |
564 | + SAMPLE_ACCOUNT_WITH_CURRENT_PLAN, |
565 | + SAMPLE_DEVICES_JSON, |
566 | + SAMPLE_FOLDERS, |
567 | + SAMPLE_QUOTA_JSON, |
568 | + SAMPLE_SHARED, |
569 | + SAMPLE_SHARES, |
570 | + TOKEN, |
571 | +) |
572 | from ubuntuone.controlpanel.webclient import WebClientError |
573 | |
574 | -SAMPLE_CREDENTIALS = {"token": "ABC1234DEF"} |
575 | - |
576 | -SAMPLE_ACCOUNT_JSON = """ |
577 | -{ |
578 | - "username": "andrewpz", |
579 | - "openid": "https://login.launchpad.net/+id/abcdefg", |
580 | - "first_name": "Andrew P.", |
581 | - "last_name": "Zoilo", |
582 | - "couchdb": { |
583 | - "host": "https://couchdb.one.ubuntu.com", |
584 | - "root": "https://couchdb.one.ubuntu.com/u/abc/def/12345", |
585 | - "dbpath": "u/abc/def/12345" |
586 | - }, |
587 | - "couchdb_root": "https://couchdb.one.ubuntu.com/u/abc/def/12345", |
588 | - "email": "andrewpz@protocultura.net",%s |
589 | - "nickname": "Andrew P. Zoilo", |
590 | - "id": 12345, |
591 | - "subscription": { |
592 | - "upgrade_available": false, |
593 | - "description": "Paid Plan, 50 GB of storage", |
594 | - "trial": false, |
595 | - "started": "2010-03-24T18:38:38Z", |
596 | - "is_paid": true, |
597 | - "expires": null, |
598 | - "qty": 1, |
599 | - "price": 0.0, |
600 | - "currency": null, |
601 | - "id": 654321, |
602 | - "name": "50 GB" |
603 | - } |
604 | -} |
605 | -""" |
606 | - |
607 | -CURRENT_PLAN = "Ubuntu One Basic (2 GB) + 1 x 20-Pack with 20 GB (monthly)" |
608 | -SAMPLE_CURRENT_PLAN = '\n "current_plan": "%s",' % CURRENT_PLAN |
609 | - |
610 | -SAMPLE_ACCOUNT_NO_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % '' |
611 | -SAMPLE_ACCOUNT_WITH_CURRENT_PLAN = SAMPLE_ACCOUNT_JSON % SAMPLE_CURRENT_PLAN |
612 | - |
613 | - |
614 | -SAMPLE_QUOTA_JSON = """ |
615 | -{ |
616 | - "total": 53687091200, |
617 | - "used": 2350345156 |
618 | -} |
619 | -""" |
620 | - |
621 | -EXPECTED_ACCOUNT_INFO = { |
622 | - "quota_used": "2350345156", |
623 | - "quota_total": "53687091200", |
624 | - "type": "Paid Plan, 50 GB of storage", |
625 | - "name": "Andrew P. Zoilo", |
626 | - "email": "andrewpz@protocultura.net", |
627 | -} |
628 | - |
629 | -EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN = { |
630 | - "quota_used": "2350345156", |
631 | - "quota_total": "53687091200", |
632 | - "type": CURRENT_PLAN, |
633 | - "name": "Andrew P. Zoilo", |
634 | - "email": "andrewpz@protocultura.net", |
635 | -} |
636 | - |
637 | -SAMPLE_DEVICES_JSON = """ |
638 | -[ |
639 | - { |
640 | - "token": "ABCDEF01234token", |
641 | - "description": "Ubuntu One @ darkstar", |
642 | - "kind": "Computer" |
643 | - }, |
644 | - { |
645 | - "token": "ABC1234DEF", |
646 | - "description": "Ubuntu One @ localhost", |
647 | - "kind": "Computer" |
648 | - }, |
649 | - { |
650 | - "kind": "Phone", |
651 | - "description": "Nokia E65", |
652 | - "id": 1000 |
653 | - } |
654 | -] |
655 | -""" |
656 | - |
657 | -EXPECTED_DEVICES_INFO = [ |
658 | - { |
659 | - "device_id": "ComputerABCDEF01234token", |
660 | - "name": "Ubuntu One @ darkstar", |
661 | - "type": "Computer", |
662 | - "is_local": '', |
663 | - "configurable": '', |
664 | - }, |
665 | - { |
666 | - 'is_local': 'True', |
667 | - 'configurable': 'True', |
668 | - 'device_id': 'ComputerABC1234DEF', |
669 | - 'limit_bandwidth': '', |
670 | - 'max_download_speed': '-1', |
671 | - 'max_upload_speed': '-1', |
672 | - 'name': 'Ubuntu One @ localhost', |
673 | - 'type': 'Computer' |
674 | - }, |
675 | - { |
676 | - "device_id": "Phone1000", |
677 | - "name": "Nokia E65", |
678 | - "type": "Phone", |
679 | - "configurable": '', |
680 | - "is_local": '', |
681 | - }, |
682 | -] |
683 | - |
684 | -SAMPLE_FOLDERS = [ |
685 | - {u'generation': u'2', u'node_id': u'341da068-81d8-437a-8f75-5bb9d86455ba', |
686 | - u'path': u'/home/tester/Public', u'subscribed': u'True', |
687 | - u'suggested_path': u'~/Public', |
688 | - u'type': u'UDF', u'volume_id': u'9ea892f8-15fa-4201-bdbf-8de99fa5f588'}, |
689 | - {u'generation': u'', u'node_id': u'11fbc86c-0d7a-49f5-ae83-8402caf66c6a', |
690 | - u'path': u'/home/tester/Documents', u'subscribed': u'', |
691 | - u'suggested_path': u'~/Documents', |
692 | - u'type': u'UDF', u'volume_id': u'2db262f5-a151-4c19-969c-bb5ced753c61'}, |
693 | - {u'generation': u'24', u'node_id': u'9ee0e130-a7c7-4d76-a5e3-5df506221b48', |
694 | - u'path': u'/home/tester/Pictures/Photos', u'subscribed': u'True', |
695 | - u'suggested_path': u'~/Pictures/Photos', |
696 | - u'type': u'UDF', u'volume_id': u'1deb2874-3d28-46ae-9999-d5f48de9f460'}, |
697 | -] |
698 | - |
699 | -SAMPLE_SHARES = [ |
700 | - {u'accepted': u'True', u'access_level': u'View', |
701 | - u'free_bytes': u'39892622746', u'generation': u'2704', |
702 | - u'name': u're', u'node_id': u'c483f419-ed28-490a-825d-a8c074e2d795', |
703 | - u'other_username': u'otheruser', u'other_visible_name': u'Other User', |
704 | - u'path': u'/home/tester/.local/share/ubuntuone/shares/re from Other User', |
705 | - u'type': u'Share', u'volume_id': u'4a1b263b-a2b3-4f66-9e66-4cd18050810d'}, |
706 | - {u'accepted': u'True', u'access_level': u'Modify', |
707 | - u'free_bytes': u'39892622746', u'generation': u'2704', |
708 | - u'name': u'do', u'node_id': u'84544ea4-aefe-4f91-9bb9-ed7b0a805baf', |
709 | - u'other_username': u'otheruser', u'other_visible_name': u'Other User', |
710 | - u'path': u'/home/tester/.local/share/ubuntuone/shares/do from Other User', |
711 | - u'type': u'Share', u'volume_id': u'7d130dfe-98b2-4bd5-8708-9eeba9838ac0'}, |
712 | -] |
713 | - |
714 | -SAMPLE_SHARED = [ |
715 | - {u'accepted': u'True', u'access_level': u'View', |
716 | - u'free_bytes': u'', u'generation': u'', |
717 | - u'name': u'bar', u'node_id': u'31e47530-9448-4f03-b4dc-4154fdf35225', |
718 | - u'other_username': u'otheruser', u'other_visible_name': u'Other User', |
719 | - u'path': u'/home/tester/Ubuntu One/bar', |
720 | - u'type': u'Shared', |
721 | - u'volume_id': u'79584900-517f-4dff-b2f3-20e8c1e79365'}, |
722 | -] |
723 | - |
724 | |
725 | class MockWebClient(object): |
726 | """A mock webclient.""" |
727 | @@ -213,7 +74,7 @@ |
728 | class MockDBusClient(object): |
729 | """A mock dbus_client module.""" |
730 | |
731 | - creds = SAMPLE_CREDENTIALS |
732 | + creds = TOKEN |
733 | throttling = False |
734 | limits = {"download": -1, "upload": -1} |
735 | file_sync = True |
736 | @@ -292,6 +153,36 @@ |
737 | return SAMPLE_SHARED |
738 | |
739 | |
740 | +class MockReplicationClient(object): |
741 | + """A mock replication_client module.""" |
742 | + |
743 | + BOOKMARKS = 'awesome' |
744 | + CONTACTS = 'legendary' |
745 | + |
746 | + replications = set([BOOKMARKS, CONTACTS, 'other']) |
747 | + exclusions = set([CONTACTS]) |
748 | + |
749 | + def get_replications(self): |
750 | + """Grab the list of replications in this machine.""" |
751 | + return MockReplicationClient.replications |
752 | + |
753 | + def get_exclusions(self): |
754 | + """Grab the list of exclusions in this machine.""" |
755 | + return MockReplicationClient.exclusions |
756 | + |
757 | + def replicate(self, replication_id): |
758 | + """Remove replication_id from the exclusions list.""" |
759 | + if replication_id not in MockReplicationClient.replications: |
760 | + raise replication_client.ReplicationError(replication_id) |
761 | + MockReplicationClient.exclusions.remove(replication_id) |
762 | + |
763 | + def exclude(self, replication_id): |
764 | + """Add replication_id to the exclusions list.""" |
765 | + if replication_id not in MockReplicationClient.replications: |
766 | + raise replication_client.ReplicationError(replication_id) |
767 | + MockReplicationClient.exclusions.add(replication_id) |
768 | + |
769 | + |
770 | class BackendBasicTestCase(TestCase): |
771 | """Simple tests for the backend.""" |
772 | |
773 | @@ -301,13 +192,14 @@ |
774 | super(BackendBasicTestCase, self).setUp() |
775 | self.patch(backend, "WebClient", MockWebClient) |
776 | self.patch(backend, "dbus_client", MockDBusClient()) |
777 | - self.local_token = "Computer" + SAMPLE_CREDENTIALS["token"] |
778 | + self.patch(backend, "replication_client", MockReplicationClient()) |
779 | + self.local_token = "Computer" + TOKEN["token"] |
780 | self.be = backend.ControlBackend() |
781 | |
782 | self.memento = MementoHandler() |
783 | backend.logger.addHandler(self.memento) |
784 | |
785 | - MockDBusClient.creds = SAMPLE_CREDENTIALS |
786 | + MockDBusClient.creds = TOKEN |
787 | |
788 | def test_backend_creation(self): |
789 | """The backend instance is successfully created.""" |
790 | @@ -317,7 +209,7 @@ |
791 | def test_get_token(self): |
792 | """The get_token method returns the right token.""" |
793 | token = yield self.be.get_token() |
794 | - self.assertEqual(token, SAMPLE_CREDENTIALS["token"]) |
795 | + self.assertEqual(token, TOKEN["token"]) |
796 | |
797 | @inlineCallbacks |
798 | def test_device_is_local(self): |
799 | @@ -388,12 +280,12 @@ |
800 | result = yield self.be.remove_device(device_id) |
801 | self.assertEqual(result, device_id) |
802 | # credentials were not cleared |
803 | - self.assertEqual(MockDBusClient.creds, SAMPLE_CREDENTIALS) |
804 | + self.assertEqual(MockDBusClient.creds, TOKEN) |
805 | |
806 | @inlineCallbacks |
807 | def test_remove_device_clear_credentials_if_local_device(self): |
808 | """The remove_device method clears the credentials if is local.""" |
809 | - apiurl = DEVICE_REMOVE_API % ('computer', SAMPLE_CREDENTIALS['token']) |
810 | + apiurl = DEVICE_REMOVE_API % ('computer', TOKEN['token']) |
811 | # pylint: disable=E1101 |
812 | self.be.wc.results[apiurl] = SAMPLE_DEVICES_JSON |
813 | yield self.be.remove_device(self.local_token) |
814 | @@ -487,10 +379,10 @@ |
815 | """The volume settings can be changed.""" |
816 | fid = '0123-4567' |
817 | |
818 | - yield self.be.change_volume_settings(fid, {'subscribed': True}) |
819 | + yield self.be.change_volume_settings(fid, {'subscribed': 'True'}) |
820 | self.assertEqual(MockDBusClient.subscribed_folders, [fid]) |
821 | |
822 | - yield self.be.change_volume_settings(fid, {'subscribed': False}) |
823 | + yield self.be.change_volume_settings(fid, {'subscribed': ''}) |
824 | self.assertEqual(MockDBusClient.subscribed_folders, []) |
825 | |
826 | @inlineCallbacks |
827 | @@ -671,3 +563,64 @@ |
828 | |
829 | self.be.disable_files() |
830 | self.assertFalse(MockDBusClient.file_sync) |
831 | + |
832 | + |
833 | +class BackendReplicationsTestCase(BackendBasicTestCase): |
834 | + """Replications tests for the backend.""" |
835 | + |
836 | + @inlineCallbacks |
837 | + def test_replications_info(self): |
838 | + """The replications_info method exercises its callback.""" |
839 | + result = yield self.be.replications_info() |
840 | + |
841 | + # replications_info will use exclusions information |
842 | + expected = [] |
843 | + for name in MockReplicationClient.replications: |
844 | + enabled = bool_str(name not in MockReplicationClient.exclusions) |
845 | + dependency = '' |
846 | + if name == MockReplicationClient.BOOKMARKS: |
847 | + dependency = backend.BOOKMARKS_PKG |
848 | + elif name == MockReplicationClient.CONTACTS: |
849 | + dependency = backend.CONTACTS_PKG |
850 | + |
851 | + item = {'replication_id': name, 'name': name, |
852 | + 'enabled': enabled, 'dependency': dependency} |
853 | + expected.append(item) |
854 | + self.assertEqual(sorted(expected), sorted(result)) |
855 | + |
856 | + @inlineCallbacks |
857 | + def test_change_replication_settings(self): |
858 | + """The replication settings can be changed.""" |
859 | + rid = '0123-4567' |
860 | + MockReplicationClient.replications.add(rid) |
861 | + self.addCleanup(lambda: MockReplicationClient.replications.remove(rid)) |
862 | + |
863 | + yield self.be.change_replication_settings(rid, {'enabled': ''}) |
864 | + self.assertIn(rid, MockReplicationClient.exclusions) |
865 | + |
866 | + yield self.be.change_replication_settings(rid, {'enabled': 'True'}) |
867 | + self.assertNotIn(rid, MockReplicationClient.exclusions) |
868 | + |
869 | + @inlineCallbacks |
870 | + def test_change_replication_settings_not_in_replications(self): |
871 | + """The settings can not be changed for an item not in replications.""" |
872 | + rid = '0123-4567' |
873 | + assert rid not in MockReplicationClient.replications |
874 | + |
875 | + d = self.be.change_replication_settings(rid, {'enabled': 'True'}) |
876 | + yield self.assertFailure(d, replication_client.ReplicationError) |
877 | + |
878 | + d = self.be.change_replication_settings(rid, {'enabled': ''}) |
879 | + yield self.assertFailure(d, replication_client.ReplicationError) |
880 | + |
881 | + @inlineCallbacks |
882 | + def test_change_replication_settings_no_setting(self): |
883 | + """The change replication settings does not fail on empty settings.""" |
884 | + rid = '0123-4567' |
885 | + MockReplicationClient.replications.add(rid) |
886 | + self.addCleanup(lambda: MockReplicationClient.replications.remove(rid)) |
887 | + |
888 | + prior = MockReplicationClient.exclusions.copy() |
889 | + yield self.be.change_replication_settings(rid, {}) |
890 | + |
891 | + self.assertEqual(MockReplicationClient.exclusions, prior) |
892 | |
893 | === added file 'ubuntuone/controlpanel/tests/test_replication_client.py' |
894 | --- ubuntuone/controlpanel/tests/test_replication_client.py 1970-01-01 00:00:00 +0000 |
895 | +++ ubuntuone/controlpanel/tests/test_replication_client.py 2011-01-06 20:44:10 +0000 |
896 | @@ -0,0 +1,160 @@ |
897 | +# -*- coding: utf-8 -*- |
898 | + |
899 | +# Authors: Natalia B. Bidart <natalia.bidart@canonical.com> |
900 | +# |
901 | +# Copyright 2010 Canonical Ltd. |
902 | +# |
903 | +# This program is free software: you can redistribute it and/or modify it |
904 | +# under the terms of the GNU General Public License version 3, as published |
905 | +# by the Free Software Foundation. |
906 | +# |
907 | +# This program is distributed in the hope that it will be useful, but |
908 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
909 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
910 | +# PURPOSE. See the GNU General Public License for more details. |
911 | +# |
912 | +# You should have received a copy of the GNU General Public License along |
913 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
914 | + |
915 | +"""Tests for the DBus service when accessing desktopcouch replications.""" |
916 | + |
917 | +from twisted.internet.defer import inlineCallbacks |
918 | + |
919 | +from ubuntuone.controlpanel import replication_client |
920 | +from ubuntuone.controlpanel.tests import TestCase |
921 | + |
922 | +EXCLUSIONS = set() |
923 | + |
924 | + |
925 | +class FakedReplication(object): |
926 | + """Faked a DC replication exclusion.""" |
927 | + |
928 | + def __init__(self): |
929 | + self.all_exclusions = lambda: EXCLUSIONS |
930 | + self.replicate = EXCLUSIONS.remove |
931 | + self.exclude = EXCLUSIONS.add |
932 | + |
933 | + |
934 | +class FakedReplicationModule(object): |
935 | + """Faked a DC replication module.""" |
936 | + |
937 | + ReplicationExclusion = FakedReplication |
938 | + |
939 | + |
940 | +class ReplicationsTestCase(TestCase): |
941 | + """Test for the replications client methods.""" |
942 | + |
943 | + def setUp(self): |
944 | + super(ReplicationsTestCase, self).setUp() |
945 | + |
946 | + orig_get_proxy = replication_client.get_replication_proxy |
947 | + |
948 | + def get_proxy(): |
949 | + """Fake the proxy getter.""" |
950 | + return orig_get_proxy(replication_module=FakedReplicationModule()) |
951 | + |
952 | + self.patch(replication_client, 'get_replication_proxy', get_proxy) |
953 | + |
954 | + def tearDown(self): |
955 | + EXCLUSIONS.clear() |
956 | + super(ReplicationsTestCase, self).tearDown() |
957 | + |
958 | + @inlineCallbacks |
959 | + def test_no_pairing_record(self): |
960 | + """Handle ValueError from replication layer.""" |
961 | + |
962 | + def no_pairing_record(*args, **kwargs): |
963 | + """Fail with ValueError.""" |
964 | + raise ValueError('No pairing record.') |
965 | + |
966 | + self.patch(FakedReplicationModule, 'ReplicationExclusion', |
967 | + no_pairing_record) |
968 | + |
969 | + yield self.assertFailure(replication_client.get_replications(), |
970 | + replication_client.NoPairingRecord) |
971 | + |
972 | + @inlineCallbacks |
973 | + def test_get_replications(self): |
974 | + """Replications are correctly retrieved.""" |
975 | + result = yield replication_client.get_replications() |
976 | + self.assertEqual(result, replication_client.REPLICATIONS) |
977 | + |
978 | + @inlineCallbacks |
979 | + def test_get_exclusions(self): |
980 | + """Exclusions are correctly retrieved.""" |
981 | + replications = yield replication_client.get_replications() |
982 | + for rep in replications: |
983 | + yield replication_client.exclude(rep) |
984 | + |
985 | + result = yield replication_client.get_exclusions() |
986 | + self.assertEqual(result, replications) |
987 | + |
988 | + @inlineCallbacks |
989 | + def test_replicate(self): |
990 | + """Replicate a service is correct.""" |
991 | + replications = yield replication_client.get_replications() |
992 | + rid = list(replications)[0] |
993 | + yield replication_client.exclude(rid) |
994 | + |
995 | + yield replication_client.replicate(rid) |
996 | + exclusions = yield replication_client.get_exclusions() |
997 | + self.assertNotIn(rid, exclusions) |
998 | + |
999 | + @inlineCallbacks |
1000 | + def test_replicate_name_not_in_replications(self): |
1001 | + """Replicate a service fails if not in replications.""" |
1002 | + replications = yield replication_client.get_replications() |
1003 | + rid = 'not in replications' |
1004 | + assert rid not in replications |
1005 | + |
1006 | + yield self.assertFailure(replication_client.replicate(rid), |
1007 | + replication_client.InvalidIdError) |
1008 | + |
1009 | + @inlineCallbacks |
1010 | + def test_replicate_name_not_in_exclusions(self): |
1011 | + """Replicate a service fails if not in exclusions.""" |
1012 | + replications = yield replication_client.get_replications() |
1013 | + rid = list(replications)[0] |
1014 | + assert rid in replications |
1015 | + |
1016 | + exclusions = yield replication_client.get_exclusions() |
1017 | + assert rid not in exclusions |
1018 | + |
1019 | + yield self.assertFailure(replication_client.replicate(rid), |
1020 | + replication_client.NotExcludedError) |
1021 | + |
1022 | + @inlineCallbacks |
1023 | + def test_exclude(self): |
1024 | + """Excluding a service is correct.""" |
1025 | + replications = yield replication_client.get_replications() |
1026 | + rid = list(replications)[0] |
1027 | + yield replication_client.exclude(rid) |
1028 | + yield replication_client.replicate(rid) |
1029 | + |
1030 | + yield replication_client.exclude(rid) |
1031 | + exclusions = yield replication_client.get_exclusions() |
1032 | + self.assertIn(rid, exclusions) |
1033 | + |
1034 | + @inlineCallbacks |
1035 | + def test_exclude_name_not_in_replications(self): |
1036 | + """Excluding a service fails if not in replications.""" |
1037 | + replications = yield replication_client.get_replications() |
1038 | + rid = 'not in replications' |
1039 | + assert rid not in replications |
1040 | + |
1041 | + yield self.assertFailure(replication_client.exclude(rid), |
1042 | + replication_client.InvalidIdError) |
1043 | + |
1044 | + @inlineCallbacks |
1045 | + def test_exclude_name_in_exclusions(self): |
1046 | + """Excluding a service fails if already on exclusions.""" |
1047 | + replications = yield replication_client.get_replications() |
1048 | + rid = list(replications)[0] |
1049 | + assert rid in replications |
1050 | + |
1051 | + yield replication_client.exclude(rid) |
1052 | + exclusions = yield replication_client.get_exclusions() |
1053 | + assert rid in exclusions |
1054 | + |
1055 | + yield self.assertFailure(replication_client.exclude(rid), |
1056 | + replication_client.AlreadyExcludedError) |
Minor point: I don't like exclude/replicate as complementary method names, I'd prefer exclude/include or maybe even include_ in_replication/ exclude_ from_replicatio n for clarity.