~robru/friends/early-dbus-interface

« back to all changes in this revision

Viewing changes to friends/tests/test_account.py

  • Committer: Ken VanDine
  • Date: 2012-10-13 01:27:15 UTC
  • Revision ID: ken.vandine@canonical.com-20121013012715-gxfoi1oo30wdm8dv
initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# friends-service -- send & receive messages from any social network
 
2
# Copyright (C) 2012  Canonical Ltd
 
3
#
 
4
# This program is free software: you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation, version 3 of the License.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 
 
16
"""Test the Account class."""
 
17
 
 
18
__all__ = [
 
19
    'TestAccount',
 
20
    'TestAccountManager',
 
21
    ]
 
22
 
 
23
 
 
24
import json
 
25
import unittest
 
26
 
 
27
from friends.errors import UnsupportedProtocolError
 
28
from friends.protocols.flickr import Flickr
 
29
from friends.testing.helpers import FakeAccount
 
30
from friends.testing.mocks import SettingsIterMock
 
31
from friends.utils.account import Account, AccountManager
 
32
 
 
33
try:
 
34
    # Python 3.3
 
35
    from unittest import mock
 
36
except ImportError:
 
37
    import mock
 
38
 
 
39
 
 
40
class TestAccount(unittest.TestCase):
 
41
    """Test Account class."""
 
42
 
 
43
    def setUp(self):
 
44
        def connect_side_effect(signal, callback, account):
 
45
            # The account service provides a .connect method that connects a
 
46
            # signal to a callback.  We have to mock a side effect into the
 
47
            # connect() method to record this connection, which some of the
 
48
            # tests can then call.
 
49
            self._callback_signal = signal
 
50
            self._callback = callback
 
51
            self._callback_account = account
 
52
        # Set up the mock to return some useful values in the API expected by
 
53
        # the Account constructor.
 
54
        self.account_service = mock.Mock(**{
 
55
            'get_auth_data.return_value': mock.Mock(**{
 
56
                'get_credentials_id.return_value': 'fake credentials',
 
57
                'get_method.return_value': 'fake method',
 
58
                'get_mechanism.return_value': 'fake mechanism',
 
59
                'get_parameters.return_value': 'fake parameters',
 
60
                }),
 
61
            'get_account.return_value': mock.Mock(**{
 
62
                'get_settings_iter.return_value': SettingsIterMock(),
 
63
                'id': 'fake_id',
 
64
                'get_provider_name.return_value': 'flickr',
 
65
                }),
 
66
            'get_service.return_value': mock.Mock(**{
 
67
                'get_name.return_value': 'fake_service',
 
68
                }),
 
69
            'connect.side_effect': connect_side_effect,
 
70
            })
 
71
        self.account = Account(self.account_service)
 
72
 
 
73
    def test_account_auth(self):
 
74
        # Test that the constructor initializes the 'auth' attribute.
 
75
        auth = self.account.auth
 
76
        self.assertEqual(auth.id, 'fake credentials')
 
77
        self.assertEqual(auth.method, 'fake method')
 
78
        self.assertEqual(auth.mechanism, 'fake mechanism')
 
79
        self.assertEqual(auth.parameters, 'fake parameters')
 
80
 
 
81
    def test_account_id(self):
 
82
        # The 'id' key of the account gets the full service name.
 
83
        self.assertEqual(self.account.id, 'fake_id/flickr')
 
84
 
 
85
    def test_account_service(self):
 
86
        # The protocol attribute refers directly to the protocol used.
 
87
        self.assertIsInstance(self.account.protocol, Flickr)
 
88
 
 
89
    def test_account_unsupported(self):
 
90
        # Unsupported protocols raise exceptions in the Account constructor.
 
91
        mock = self.account_service.get_account()
 
92
        mock.get_provider_name.return_value = 'no service'
 
93
        with self.assertRaises(UnsupportedProtocolError) as cm:
 
94
            Account(self.account_service)
 
95
        self.assertEqual(cm.exception.protocol, 'no service')
 
96
 
 
97
    def test_on_account_changed(self):
 
98
        # Account.on_account_changed() gets called during the Account
 
99
        # constructor.  Test that it has the expected original key value.
 
100
        self.assertEqual(self.account.send_enabled, True)
 
101
 
 
102
    def test_iter_filter(self):
 
103
        # The get_settings_iter() filters everything that doesn't start with
 
104
        # 'friends/'
 
105
        self._callback_account.get_settings_iter.assert_called_with('friends/')
 
106
 
 
107
    def test_on_account_changed_signal(self):
 
108
        # Test that when the account changes, and a 'changed' signal is
 
109
        # received, the callback is called and the account is updated.
 
110
        #
 
111
        # Start by simulating a change in the account service.
 
112
        other_iter = SettingsIterMock()
 
113
        other_iter.items = [
 
114
            (True, 'send_enabled', False),
 
115
            (True, 'bee', 'two'),
 
116
            (True, 'cat', 'three'),
 
117
            ]
 
118
        iter = self.account_service.get_account().get_settings_iter
 
119
        iter.return_value = other_iter
 
120
        # Check that the signal has been connected.
 
121
        self.assertEqual(self._callback_signal, 'changed')
 
122
        # Check that the account is the object we expect it to be.
 
123
        self.assertEqual(self._callback_account,
 
124
                         self.account_service.get_account())
 
125
        # Simulate the signal.
 
126
        self._callback(self.account_service, self._callback_account)
 
127
        # Have the expected updates occurred?
 
128
        self.assertEqual(self.account.send_enabled, False)
 
129
        self.assertFalse(hasattr(self.account, 'bee'))
 
130
        self.assertFalse(hasattr(self.account, 'cat'))
 
131
 
 
132
    def test_enabled(self):
 
133
        # .enabled() just passes through from the account service.
 
134
        self.account_service.get_enabled.return_value = True
 
135
        self.assertTrue(self.account.enabled)
 
136
        self.account_service.get_enabled.return_value = False
 
137
        self.assertFalse(self.account.enabled)
 
138
 
 
139
    def test_equal(self):
 
140
        # Two accounts are equal if their account services are equal.
 
141
        other = Account(self.account_service)
 
142
        self.assertEqual(self.account, other)
 
143
 
 
144
    def test_unequal(self):
 
145
        # Two accounts are unequal if their account services are unequal.  The
 
146
        # other mock service has to at least support the basic required API.
 
147
        other = Account(mock.Mock(**{
 
148
            'get_account.return_value': mock.Mock(**{
 
149
                'get_settings_iter.return_value': SettingsIterMock(),
 
150
                    # It's okay if the provider names are the same; the test
 
151
                    # is for whether the account services are the same or not,
 
152
                    # and in this test, they'll be different mock instances.
 
153
                    'get_provider_name.return_value': 'flickr',
 
154
                }),
 
155
            }))
 
156
        self.assertNotEqual(self.account, other)
 
157
 
 
158
 
 
159
accounts_manager = mock.Mock()
 
160
accounts_manager.new_for_service_type(
 
161
    'microblogging').get_enabled_account_services.return_value = []
 
162
 
 
163
 
 
164
@mock.patch('gi.repository.Accounts.Manager', accounts_manager)
 
165
@mock.patch('friends.utils.account.Account', FakeAccount)
 
166
class TestAccountManager(unittest.TestCase):
 
167
    """Test the AccountManager API."""
 
168
 
 
169
    def setUp(self):
 
170
        self.account_service = mock.Mock()
 
171
 
 
172
    def test_account_manager(self):
 
173
        # Test that the AccountManager adds the expected accounts to its
 
174
        # internal mapping.
 
175
        def refresh():
 
176
            pass
 
177
        AccountManager(refresh)
 
178
 
 
179
    def test_account_manager_add_new_account(self):
 
180
        # Explicitly adding a new account puts the account's global_id into
 
181
        # the account manager's mapping.
 
182
        def refresh():
 
183
            pass
 
184
        manager = AccountManager(refresh)
 
185
        manager.add_new_account(self.account_service)
 
186
        self.assertIn('faker/than fake', manager._accounts)
 
187
 
 
188
    def test_account_manager_enabled_event(self):
 
189
        # Mimic a reaction to the enabled-event callback.
 
190
        refreshed = False
 
191
        def refresh():
 
192
            nonlocal refreshed
 
193
            refreshed = True
 
194
        accounts_manager.get_account().list_services.return_value = []
 
195
        manager = AccountManager(refresh)
 
196
        manager._on_enabled_event(accounts_manager, 'faker/than fake')
 
197
        self.assertTrue(refreshed)
 
198
 
 
199
    def test_account_manager_delete_account_no_account(self):
 
200
        # Deleting an account removes the global_id from the mapping.  But if
 
201
        # that global id is missing, then it does not cause an exception.
 
202
        # Also, the refresh callback is *not* called.
 
203
        refreshed = False
 
204
        def refresh():
 
205
            nonlocal refreshed
 
206
            refreshed = True
 
207
        manager = AccountManager(refresh)
 
208
        self.assertNotIn('faker/than fake', manager._accounts)
 
209
        manager._on_account_deleted(accounts_manager, 'faker/than fake')
 
210
        self.assertNotIn('faker/than fake', manager._accounts)
 
211
        self.assertFalse(refreshed)
 
212
 
 
213
    def test_account_manager_delete_account(self):
 
214
        # Deleting an account removes the id from the mapping. But if
 
215
        # that id is missing, then it does not cause an exception.
 
216
        refreshed = False
 
217
        def refresh():
 
218
            nonlocal refreshed
 
219
            refreshed = True
 
220
        manager = AccountManager(refresh)
 
221
        manager.add_new_account(self.account_service)
 
222
        manager._on_account_deleted(accounts_manager, 'faker/than fake')
 
223
        self.assertNotIn('faker/than fake', manager._accounts)
 
224
        self.assertTrue(refreshed)