~dobey/ubuntu/oneiric/ubuntuone-control-panel/fix-823648

« back to all changes in this revision

Viewing changes to ubuntuone/controlpanel/tests/test_backend.py

Tags: 0.9.5-0ubuntu1
releasing version 0.9.5-0ubuntu1

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
from ubuntuone.controlpanel import backend, replication_client
31
31
from ubuntuone.controlpanel.backend import (bool_str,
32
32
    ACCOUNT_API, DEVICES_API, DEVICE_REMOVE_API, QUOTA_API,
 
33
    DEVICE_TYPE_COMPUTER,
33
34
    FILE_SYNC_DISABLED,
34
35
    FILE_SYNC_DISCONNECTED,
35
36
    FILE_SYNC_ERROR,
41
42
    MSG_KEY, STATUS_KEY,
42
43
)
43
44
from ubuntuone.controlpanel.tests import (TestCase,
 
45
    EMPTY_DESCRIPTION_JSON,
44
46
    EXPECTED_ACCOUNT_INFO,
45
47
    EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN,
46
48
    EXPECTED_DEVICES_INFO,
 
49
    LOCAL_DEVICE,
47
50
    ROOT_PATH,
48
51
    SAMPLE_ACCOUNT_NO_CURRENT_PLAN,
49
52
    SAMPLE_ACCOUNT_WITH_CURRENT_PLAN,
56
59
    SHARES_PATH_LINK,
57
60
    TOKEN,
58
61
)
59
 
from ubuntuone.controlpanel.webclient import WebClientError
60
62
 
61
63
 
62
64
class MockWebClient(object):
70
72
 
71
73
    def call_api(self, method):
72
74
        """Get a given url from the webservice."""
73
 
        if self.failure:
74
 
            return defer.fail(WebClientError(self.failure))
 
75
        if self.failure == 401:
 
76
            return defer.fail(backend.UnauthorizedError(self.failure))
 
77
        elif self.failure:
 
78
            return defer.fail(backend.WebClientError(self.failure))
75
79
        else:
76
80
            result = simplejson.loads(self.results[method])
77
81
            return defer.succeed(result)
250
254
        self.patch(backend, "WebClient", MockWebClient)
251
255
        self.patch(backend, "dbus_client", MockDBusClient())
252
256
        self.patch(backend, "replication_client", MockReplicationClient())
253
 
        self.local_token = "Computer" + TOKEN["token"]
 
257
        self.local_token = DEVICE_TYPE_COMPUTER + TOKEN["token"]
254
258
        self.be = backend.ControlBackend()
255
259
 
256
260
        self.memento = MementoHandler()
278
282
        result = yield self.be.device_is_local(did)
279
283
        self.assertFalse(result)
280
284
 
 
285
    def test_shutdown_func(self):
 
286
        """A shutdown_func can be passed as creation parameter."""
 
287
        f = lambda: None
 
288
        be = backend.ControlBackend(shutdown_func=f)
 
289
        self.assertEqual(be.shutdown_func, f)
 
290
 
 
291
    def test_shutdown_func_is_called_on_shutdown(self):
 
292
        """The shutdown_func is called on shutdown."""
 
293
        self.be.shutdown_func = self._set_called
 
294
        self.be.shutdown()
 
295
        self.assertEqual(self._called, ((), {}))
 
296
 
 
297
    def test_shutdown_func_when_none(self):
 
298
        """The shutdown_func can be None."""
 
299
        self.be.shutdown_func = None
 
300
        self.be.shutdown()
 
301
        # nothing explodes
 
302
 
281
303
 
282
304
class BackendAccountTestCase(BackendBasicTestCase):
283
305
    """Account tests for the backend."""
305
327
        """The account_info method exercises its errback."""
306
328
        # pylint: disable=E1101
307
329
        self.be.wc.failure = 404
308
 
        yield self.assertFailure(self.be.account_info(), WebClientError)
 
330
        yield self.assertFailure(self.be.account_info(),
 
331
                                 backend.WebClientError)
 
332
 
 
333
    @inlineCallbacks
 
334
    def test_account_info_fails_with_unauthorized(self):
 
335
        """The account_info clears the credentials on unauthorized."""
 
336
        # pylint: disable=E1101
 
337
        self.be.wc.failure = 401
 
338
        d = defer.Deferred()
 
339
        self.patch(backend.dbus_client, 'clear_credentials',
 
340
                   lambda: d.callback('called'))
 
341
        yield self.assertFailure(self.be.account_info(),
 
342
                                 backend.UnauthorizedError)
 
343
        yield d
309
344
 
310
345
 
311
346
class BackendDevicesTestCase(BackendBasicTestCase):
322
357
    @inlineCallbacks
323
358
    def test_devices_info_fails(self):
324
359
        """The devices_info method exercises its errback."""
 
360
        def fail(*args, **kwargs):
 
361
            """Raise any error other than WebClientError."""
 
362
            raise ValueError(args)
 
363
 
 
364
        self.patch(self.be.wc, 'call_api', fail)
 
365
        yield self.assertFailure(self.be.devices_info(), ValueError)
 
366
 
 
367
    @inlineCallbacks
 
368
    def test_devices_info_with_webclient_error(self):
 
369
        """The devices_info returns local info if webclient error."""
325
370
        # pylint: disable=E1101
326
371
        self.be.wc.failure = 404
327
 
        yield self.assertFailure(self.be.devices_info(), WebClientError)
 
372
        result = yield self.be.devices_info()
 
373
 
 
374
        self.assertEqual(result, [LOCAL_DEVICE])
 
375
        self.assertTrue(self.memento.check_error('devices_info',
 
376
                                                 'web client failure'))
 
377
 
 
378
    @inlineCallbacks
 
379
    def test_devices_info_fails_with_unauthorized(self):
 
380
        """The devices_info clears the credentials on unauthorized."""
 
381
        # pylint: disable=E1101
 
382
        self.be.wc.failure = 401
 
383
        d = defer.Deferred()
 
384
        self.patch(backend.dbus_client, 'clear_credentials',
 
385
                   lambda: d.callback('called'))
 
386
        yield self.assertFailure(self.be.devices_info(),
 
387
                                 backend.UnauthorizedError)
 
388
        yield d
 
389
 
 
390
    @inlineCallbacks
 
391
    def test_devices_info_if_files_disable(self):
 
392
        """The devices_info returns device only info if files is disabled."""
 
393
        yield self.be.disable_files()
 
394
        status = yield self.be.file_sync_status()
 
395
        assert status['status'] == backend.FILE_SYNC_DISABLED, status
 
396
 
 
397
        # pylint: disable=E1101
 
398
        self.be.wc.results[DEVICES_API] = SAMPLE_DEVICES_JSON
 
399
        result = yield self.be.devices_info()
 
400
 
 
401
        expected = EXPECTED_DEVICES_INFO[:]
 
402
        for device in expected:
 
403
            device.pop('limit_bandwidth', None)
 
404
            device.pop('max_download_speed', None)
 
405
            device.pop('max_upload_speed', None)
 
406
            device.pop('show_all_notifications', None)
 
407
            device['configurable'] = ''
 
408
        self.assertEqual(result, expected)
 
409
 
 
410
    @inlineCallbacks
 
411
    def test_devices_info_when_token_name_is_empty(self):
 
412
        """The devices_info can handle empty token names."""
 
413
        # pylint: disable=E1101
 
414
        self.be.wc.results[DEVICES_API] = EMPTY_DESCRIPTION_JSON
 
415
        result = yield self.be.devices_info()
 
416
        expected = {'configurable': '',
 
417
                    'device_id': 'ComputerABCDEF01234token',
 
418
                    'is_local': '', 'name': 'None',
 
419
                    'type': DEVICE_TYPE_COMPUTER}
 
420
        self.assertEqual(result, [expected])
 
421
        self.assertTrue(self.memento.check_warning('name', 'None'))
 
422
 
 
423
    @inlineCallbacks
 
424
    def test_devices_info_does_not_log_device_id(self):
 
425
        """The devices_info does not log the device_id."""
 
426
        # pylint: disable=E1101
 
427
        self.be.wc.results[DEVICES_API] = SAMPLE_DEVICES_JSON
 
428
        yield self.be.devices_info()
 
429
 
 
430
        dids = (d['device_id'] for d in EXPECTED_DEVICES_INFO)
 
431
        device_id_logged = all(all(did not in r.getMessage()
 
432
                                   for r in self.memento.records)
 
433
                               for did in dids)
 
434
        self.assertTrue(device_id_logged)
328
435
 
329
436
    @inlineCallbacks
330
437
    def test_remove_device(self):
331
438
        """The remove_device method calls the right api."""
332
 
        dtype, did = "Computer", "SAMPLE-TOKEN"
 
439
        dtype, did = DEVICE_TYPE_COMPUTER, "SAMPLE-TOKEN"
333
440
        device_id = dtype + did
334
441
        apiurl = DEVICE_REMOVE_API % (dtype.lower(), did)
335
442
        # pylint: disable=E1101
354
461
        """The remove_device method fails as expected."""
355
462
        # pylint: disable=E1101
356
463
        self.be.wc.failure = 404
357
 
        yield self.assertFailure(self.be.devices_info(), WebClientError)
 
464
        yield self.assertFailure(self.be.remove_device(self.local_token),
 
465
                                 backend.WebClientError)
 
466
 
 
467
    @inlineCallbacks
 
468
    def test_remove_device_fails_with_unauthorized(self):
 
469
        """The remove_device clears the credentials on unauthorized."""
 
470
        # pylint: disable=E1101
 
471
        self.be.wc.failure = 401
 
472
        d = defer.Deferred()
 
473
        self.patch(backend.dbus_client, 'clear_credentials',
 
474
                   lambda: d.callback('called'))
 
475
        yield self.assertFailure(self.be.remove_device(self.local_token),
 
476
                                 backend.UnauthorizedError)
 
477
        yield d
 
478
 
 
479
    @inlineCallbacks
 
480
    def test_remove_device_does_not_log_device_id(self):
 
481
        """The remove_device does not log the device_id."""
 
482
        device_id = DEVICE_TYPE_COMPUTER + TOKEN['token']
 
483
        yield self.be.remove_device(device_id)
 
484
 
 
485
        device_id_logged = all(device_id not in r.getMessage()
 
486
                               for r in self.memento.records)
 
487
        self.assertTrue(device_id_logged)
358
488
 
359
489
    @inlineCallbacks
360
490
    def test_change_show_all_notifications(self):
411
541
        self.assertEqual(backend.dbus_client.limits["upload"], -1)
412
542
        self.assertEqual(backend.dbus_client.limits["download"], -1)
413
543
 
 
544
    @inlineCallbacks
 
545
    def test_changing_settings_does_not_log_device_id(self):
 
546
        """The change_device_settings does not log the device_id."""
 
547
        device_id = 'yadda-yadda'
 
548
        yield self.be.change_device_settings(device_id, {})
 
549
 
 
550
        device_id_logged = all(device_id not in r.getMessage()
 
551
                               for r in self.memento.records)
 
552
        self.assertTrue(device_id_logged)
 
553
 
414
554
 
415
555
class BackendVolumesTestCase(BackendBasicTestCase):
416
556
    """Volumes tests for the backend."""
581
721
class BackendSyncStatusTestCase(BackendBasicTestCase):
582
722
    """Syncdaemon state for the backend."""
583
723
 
 
724
    was_disabled = False
 
725
 
 
726
    def setUp(self):
 
727
        super(BackendSyncStatusTestCase, self).setUp()
 
728
        self.be.file_sync_disabled = self.was_disabled
 
729
 
584
730
    def _build_msg(self):
585
731
        """Build expected message regarding file sync status."""
586
732
        return '%s (%s)' % (MockDBusClient.status['description'],
599
745
        """The syncdaemon status is processed and emitted."""
600
746
        self.patch(MockDBusClient, 'file_sync', False)
601
747
        yield self.assert_correct_status(FILE_SYNC_DISABLED, msg='')
 
748
        self.assertTrue(self.be.file_sync_disabled)
602
749
 
603
750
    @inlineCallbacks
604
751
    def test_error(self):
610
757
            'description': 'auth failed',
611
758
        }
612
759
        yield self.assert_correct_status(FILE_SYNC_ERROR)
 
760
        # self.be.file_sync_disabled does not change
 
761
        self.assertEqual(self.was_disabled, self.be.file_sync_disabled)
613
762
 
614
763
    @inlineCallbacks
615
764
    def test_starting_when_init_not_user(self):
620
769
            'name': 'INIT', 'description': 'something new',
621
770
        }
622
771
        yield self.assert_correct_status(FILE_SYNC_STARTING)
 
772
        self.assertFalse(self.be.file_sync_disabled)
623
773
 
624
774
    @inlineCallbacks
625
775
    def test_starting_when_init_with_user(self):
630
780
            'name': 'INIT', 'description': 'something new',
631
781
        }
632
782
        yield self.assert_correct_status(FILE_SYNC_STARTING)
 
783
        self.assertFalse(self.be.file_sync_disabled)
633
784
 
634
785
    @inlineCallbacks
635
786
    def test_starting_when_local_rescan_not_user(self):
640
791
            'name': 'LOCAL_RESCAN', 'description': 'something new',
641
792
        }
642
793
        yield self.assert_correct_status(FILE_SYNC_STARTING)
 
794
        self.assertFalse(self.be.file_sync_disabled)
643
795
 
644
796
    @inlineCallbacks
645
797
    def test_starting_when_local_rescan_with_user(self):
650
802
            'name': 'LOCAL_RESCAN', 'description': 'something new',
651
803
        }
652
804
        yield self.assert_correct_status(FILE_SYNC_STARTING)
 
805
        self.assertFalse(self.be.file_sync_disabled)
653
806
 
654
807
    @inlineCallbacks
655
808
    def test_starting_when_ready_with_user(self):
660
813
            'name': 'READY', 'description': 'something nicer',
661
814
        }
662
815
        yield self.assert_correct_status(FILE_SYNC_STARTING)
 
816
        self.assertFalse(self.be.file_sync_disabled)
663
817
 
664
818
    @inlineCallbacks
665
819
    def test_disconnected(self):
672
826
        }
673
827
        yield self.assert_correct_status(FILE_SYNC_DISCONNECTED)
674
828
 
 
829
        # self.be.file_sync_disabled does not change
 
830
        self.assertEqual(self.was_disabled, self.be.file_sync_disabled)
 
831
 
675
832
    @inlineCallbacks
676
833
    def test_disconnected_when_waiting(self):
677
834
        """The syncdaemon status is processed and emitted."""
682
839
        }
683
840
        yield self.assert_correct_status(FILE_SYNC_DISCONNECTED)
684
841
 
 
842
        # self.be.file_sync_disabled does not change
 
843
        self.assertEqual(self.was_disabled, self.be.file_sync_disabled)
 
844
 
685
845
    @inlineCallbacks
686
846
    def test_syncing_if_online(self):
687
847
        """The syncdaemon status is processed and emitted."""
693
853
        }
694
854
        yield self.assert_correct_status(FILE_SYNC_SYNCING)
695
855
 
 
856
        # self.be.file_sync_disabled does not change
 
857
        self.assertEqual(self.was_disabled, self.be.file_sync_disabled)
 
858
 
696
859
    @inlineCallbacks
697
860
    def test_syncing_even_if_not_online(self):
698
861
        """The syncdaemon status is processed and emitted."""
704
867
        }
705
868
        yield self.assert_correct_status(FILE_SYNC_SYNCING)
706
869
 
 
870
        # self.be.file_sync_disabled does not change
 
871
        self.assertEqual(self.was_disabled, self.be.file_sync_disabled)
 
872
 
707
873
    @inlineCallbacks
708
874
    def test_idle(self):
709
875
        """The syncdaemon status is processed and emitted."""
715
881
        }
716
882
        yield self.assert_correct_status(FILE_SYNC_IDLE)
717
883
 
 
884
        # self.be.file_sync_disabled does not change
 
885
        self.assertEqual(self.was_disabled, self.be.file_sync_disabled)
 
886
 
718
887
    @inlineCallbacks
719
888
    def test_stopped(self):
720
889
        """The syncdaemon status is processed and emitted."""
726
895
        }
727
896
        yield self.assert_correct_status(FILE_SYNC_STOPPED)
728
897
 
 
898
        # self.be.file_sync_disabled does not change
 
899
        self.assertEqual(self.was_disabled, self.be.file_sync_disabled)
 
900
 
729
901
    @inlineCallbacks
730
902
    def test_unknown(self):
731
903
        """The syncdaemon status is processed and emitted."""
740
912
                                                 repr(MockDBusClient.status))
741
913
        self.assertTrue(has_warning)
742
914
 
 
915
        # self.be.file_sync_disabled does not change
 
916
        self.assertEqual(self.was_disabled, self.be.file_sync_disabled)
 
917
 
743
918
    def test_status_changed(self):
744
919
        """The file_sync_status is the status changed handler."""
745
920
        self.be.status_changed_handler = self._set_called
754
929
        self.assertEqual(self._called, ((expected_status,), {}))
755
930
 
756
931
 
 
932
class BackendSyncStatusIfDisabledTestCase(BackendSyncStatusTestCase):
 
933
    """Syncdaemon state for the backend when file sync is disabled."""
 
934
 
 
935
    was_disabled = True
 
936
 
 
937
    @inlineCallbacks
 
938
    def assert_correct_status(self, status, msg=None):
 
939
        """Check that the resulting status is correct."""
 
940
        sup = super(BackendSyncStatusIfDisabledTestCase, self)
 
941
        if status != FILE_SYNC_STARTING:
 
942
            yield sup.assert_correct_status(FILE_SYNC_DISABLED, msg='')
 
943
        else:
 
944
            yield sup.assert_correct_status(status, msg=msg)
 
945
 
 
946
 
757
947
class BackendFileSyncOpsTestCase(BackendBasicTestCase):
758
948
    """Syncdaemon operations for the backend."""
759
949
 
768
958
 
769
959
        yield self.be.enable_files()
770
960
        self.assertTrue(MockDBusClient.file_sync)
 
961
        self.assertFalse(self.be.file_sync_disabled)
771
962
 
772
963
    @inlineCallbacks
773
964
    def test_disable_files(self):
776
967
 
777
968
        yield self.be.disable_files()
778
969
        self.assertFalse(MockDBusClient.file_sync)
 
970
        self.assertTrue(self.be.file_sync_disabled)
779
971
 
780
972
    @inlineCallbacks
781
973
    def test_connect_files(self):
783
975
        yield self.be.connect_files()
784
976
 
785
977
        self.assertEqual(MockDBusClient.actions, ['connect'])
 
978
        self.assertFalse(self.be.file_sync_disabled)
786
979
 
787
980
    @inlineCallbacks
788
981
    def test_disconnect_files(self):
790
983
        yield self.be.disconnect_files()
791
984
 
792
985
        self.assertEqual(MockDBusClient.actions, ['disconnect'])
 
986
        self.assertFalse(self.be.file_sync_disabled)
793
987
 
794
988
    @inlineCallbacks
795
989
    def test_restart_files(self):
797
991
        yield self.be.restart_files()
798
992
 
799
993
        self.assertEqual(MockDBusClient.actions, ['stop', 'start'])
 
994
        self.assertFalse(self.be.file_sync_disabled)
800
995
 
801
996
    @inlineCallbacks
802
997
    def test_start_files(self):
804
999
        yield self.be.start_files()
805
1000
 
806
1001
        self.assertEqual(MockDBusClient.actions, ['start'])
 
1002
        self.assertFalse(self.be.file_sync_disabled)
807
1003
 
808
1004
    @inlineCallbacks
809
1005
    def test_stop_files(self):
811
1007
        yield self.be.stop_files()
812
1008
 
813
1009
        self.assertEqual(MockDBusClient.actions, ['stop'])
 
1010
        self.assertFalse(self.be.file_sync_disabled)
814
1011
 
815
1012
 
816
1013
class BackendReplicationsTestCase(BackendBasicTestCase):