1
# friends-service -- send & receive messages from any social network
2
# Copyright (C) 2012 Canonical Ltd
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.
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.
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/>.
16
"""Test the Account class."""
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
35
from unittest import mock
40
class TestAccount(unittest.TestCase):
41
"""Test Account class."""
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',
61
'get_account.return_value': mock.Mock(**{
62
'get_settings_iter.return_value': SettingsIterMock(),
64
'get_provider_name.return_value': 'flickr',
66
'get_service.return_value': mock.Mock(**{
67
'get_name.return_value': 'fake_service',
69
'connect.side_effect': connect_side_effect,
71
self.account = Account(self.account_service)
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')
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')
85
def test_account_service(self):
86
# The protocol attribute refers directly to the protocol used.
87
self.assertIsInstance(self.account.protocol, Flickr)
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')
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)
102
def test_iter_filter(self):
103
# The get_settings_iter() filters everything that doesn't start with
105
self._callback_account.get_settings_iter.assert_called_with('friends/')
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.
111
# Start by simulating a change in the account service.
112
other_iter = SettingsIterMock()
114
(True, 'send_enabled', False),
115
(True, 'bee', 'two'),
116
(True, 'cat', 'three'),
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'))
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)
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)
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',
156
self.assertNotEqual(self.account, other)
159
accounts_manager = mock.Mock()
160
accounts_manager.new_for_service_type(
161
'microblogging').get_enabled_account_services.return_value = []
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."""
170
self.account_service = mock.Mock()
172
def test_account_manager(self):
173
# Test that the AccountManager adds the expected accounts to its
177
AccountManager(refresh)
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.
184
manager = AccountManager(refresh)
185
manager.add_new_account(self.account_service)
186
self.assertIn('faker/than fake', manager._accounts)
188
def test_account_manager_enabled_event(self):
189
# Mimic a reaction to the enabled-event callback.
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)
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.
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)
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.
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)