1
# Copyright (C) 2006 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
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, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
from StringIO import StringIO
29
from bzrlib.tests import TestCaseWithTransport, TestSkipped
32
from bzrlib.plugins.launchpad.lp_registration import (
35
BranchRegistrationRequest,
36
ResolveLaunchpadPathRequest,
41
# TODO: Test that the command-line client, making sure that it'll pass the
42
# request through to a dummy transport, and that the transport will validate
43
# the results passed in. Not sure how to get the transport object back out to
44
# validate that its OK - may not be necessary.
46
# TODO: Add test for (and implement) other command-line options to set
47
# project, author_email, description.
49
# TODO: project_id is not properly handled -- must be passed in rpc or path.
51
class InstrumentedXMLRPCConnection(object):
52
"""Stands in place of an http connection for the purposes of testing"""
54
def __init__(self, testcase):
55
self.testcase = testcase
58
"""Fake the http reply.
60
:returns: (errcode, errmsg, headers)
62
return (200, 'OK', [])
65
"""Return a fake file containing the response content."""
67
<?xml version="1.0" ?>
72
<string>victoria dock</string>
80
class InstrumentedXMLRPCTransport(xmlrpclib.Transport):
82
# Python 2.5's xmlrpclib looks for this.
85
def __init__(self, testcase, expect_auth):
86
self.testcase = testcase
87
self.expect_auth = expect_auth
89
def make_connection(self, host):
90
host, http_headers, x509 = self.get_host_info(host)
92
self.connected_host = host
94
auth_hdrs = [v for k,v in http_headers if k == 'Authorization']
95
if len(auth_hdrs) != 1:
96
raise AssertionError("multiple auth headers: %r"
98
authinfo = auth_hdrs[0]
99
expected_auth = 'testuser@launchpad.net:testpassword'
100
test.assertEquals(authinfo,
101
'Basic ' + base64.encodestring(expected_auth).strip())
103
raise AssertionError()
104
return InstrumentedXMLRPCConnection(test)
106
def send_request(self, connection, handler_path, request_body):
108
self.got_request = True
110
def send_host(self, conn, host):
113
def send_user_agent(self, conn):
114
# TODO: send special user agent string, including bzrlib version
118
def send_content(self, conn, request_body):
119
unpacked, method = xmlrpclib.loads(request_body)
121
raise AssertionError(
122
"xmlrpc result %r shouldn't contain None" % (unpacked,))
123
self.sent_params = unpacked
126
class MockLaunchpadService(LaunchpadService):
128
def send_request(self, method_name, method_params, authenticated):
129
"""Stash away the method details rather than sending them to a real server"""
130
self.called_method_name = method_name
131
self.called_method_params = method_params
132
self.called_authenticated = authenticated
135
class TestBranchRegistration(TestCaseWithTransport):
136
SAMPLE_URL = 'http://bazaar-vcs.org/bzr/bzr.dev/'
137
SAMPLE_OWNER = 'jhacker@foo.com'
138
SAMPLE_BRANCH_ID = 'bzr.dev'
141
super(TestBranchRegistration, self).setUp()
142
# make sure we have a reproducible standard environment
143
self._captureVar('BZR_LP_XMLRPC_URL', None)
145
def test_register_help(self):
146
"""register-branch accepts --help"""
147
out, err = self.run_bzr(['register-branch', '--help'])
148
self.assertContainsRe(out, r'Register a branch')
150
def test_register_no_url_no_branch(self):
151
"""register-branch command requires parameters"""
152
self.make_repository('.')
154
['register-branch requires a public branch url - '
155
'see bzr help register-branch'],
158
def test_register_no_url_in_published_branch_no_error(self):
159
b = self.make_branch('.')
160
b.set_public_branch('http://test-server.com/bzr/branch')
161
out, err = self.run_bzr(['register-branch', '--dry-run'])
162
self.assertEqual('Branch registered.\n', out)
163
self.assertEqual('', err)
165
def test_register_no_url_in_unpublished_branch_errors(self):
166
b = self.make_branch('.')
167
out, err = self.run_bzr_error(['no public branch'],
168
['register-branch', '--dry-run'])
169
self.assertEqual('', out)
171
def test_register_dry_run(self):
172
out, err = self.run_bzr(['register-branch',
173
'http://test-server.com/bzr/branch',
175
self.assertEquals(out, 'Branch registered.\n')
177
def test_onto_transport(self):
178
"""Test how the request is sent by transmitting across a mock Transport"""
179
# use a real transport, but intercept at the http/xml layer
180
transport = InstrumentedXMLRPCTransport(self, expect_auth=True)
181
service = LaunchpadService(transport)
182
service.registrant_email = 'testuser@launchpad.net'
183
service.registrant_password = 'testpassword'
184
rego = BranchRegistrationRequest('http://test-server.com/bzr/branch',
188
'author@launchpad.net',
191
self.assertEquals(transport.connected_host, 'xmlrpc.edge.launchpad.net')
192
self.assertEquals(len(transport.sent_params), 6)
193
self.assertEquals(transport.sent_params,
194
('http://test-server.com/bzr/branch', # branch_url
195
'branch-id', # branch_name
196
'my test branch', # branch_title
198
'author@launchpad.net',
200
self.assertTrue(transport.got_request)
202
def test_onto_transport_unauthenticated(self):
203
"""Test how an unauthenticated request is transmitted across a mock Transport"""
204
transport = InstrumentedXMLRPCTransport(self, expect_auth=False)
205
service = LaunchpadService(transport)
206
resolve = ResolveLaunchpadPathRequest('bzr')
207
resolve.submit(service)
208
self.assertEquals(transport.connected_host, 'xmlrpc.edge.launchpad.net')
209
self.assertEquals(len(transport.sent_params), 1)
210
self.assertEquals(transport.sent_params, ('bzr', ))
211
self.assertTrue(transport.got_request)
213
def test_subclass_request(self):
214
"""Define a new type of xmlrpc request"""
215
class DummyRequest(BaseRequest):
216
_methodname = 'dummy_request'
217
def _request_params(self):
220
service = MockLaunchpadService()
221
service.registrant_email = 'test@launchpad.net'
222
service.registrant_password = ''
223
request = DummyRequest()
224
request.submit(service)
225
self.assertEquals(service.called_method_name, 'dummy_request')
226
self.assertEquals(service.called_method_params, (42,))
228
def test_mock_server_registration(self):
229
"""Send registration to mock server"""
231
class MockRegistrationService(MockLaunchpadService):
232
def send_request(self, method_name, method_params, authenticated):
233
test_case.assertEquals(method_name, "register_branch")
234
test_case.assertEquals(list(method_params),
235
['url', 'name', 'title', 'description', 'email', 'name'])
236
test_case.assertEquals(authenticated, True)
238
service = MockRegistrationService()
239
rego = BranchRegistrationRequest('url', 'name', 'title',
240
'description', 'email', 'name')
241
result = rego.submit(service)
242
self.assertEquals(result, 'result')
244
def test_mock_server_registration_with_defaults(self):
245
"""Send registration to mock server"""
247
class MockRegistrationService(MockLaunchpadService):
248
def send_request(self, method_name, method_params, authenticated):
249
test_case.assertEquals(method_name, "register_branch")
250
test_case.assertEquals(list(method_params),
251
['http://server/branch', 'branch', '', '', '', ''])
252
test_case.assertEquals(authenticated, True)
254
service = MockRegistrationService()
255
rego = BranchRegistrationRequest('http://server/branch')
256
result = rego.submit(service)
257
self.assertEquals(result, 'result')
259
def test_mock_bug_branch_link(self):
260
"""Send bug-branch link to mock server"""
262
class MockService(MockLaunchpadService):
263
def send_request(self, method_name, method_params, authenticated):
264
test_case.assertEquals(method_name, "link_branch_to_bug")
265
test_case.assertEquals(list(method_params),
266
['http://server/branch', 1234, ''])
267
test_case.assertEquals(authenticated, True)
268
return 'http://launchpad.net/bug/1234'
269
service = MockService()
270
rego = BranchBugLinkRequest('http://server/branch', 1234)
271
result = rego.submit(service)
272
self.assertEquals(result, 'http://launchpad.net/bug/1234')
274
def test_mock_resolve_lp_url(self):
276
class MockService(MockLaunchpadService):
277
def send_request(self, method_name, method_params, authenticated):
278
test_case.assertEquals(method_name, "resolve_lp_path")
279
test_case.assertEquals(list(method_params), ['bzr'])
280
test_case.assertEquals(authenticated, False)
282
'bzr+ssh://bazaar.launchpad.net~bzr/bzr/trunk',
283
'sftp://bazaar.launchpad.net~bzr/bzr/trunk',
284
'bzr+http://bazaar.launchpad.net~bzr/bzr/trunk',
285
'http://bazaar.launchpad.net~bzr/bzr/trunk'])
286
service = MockService()
287
resolve = ResolveLaunchpadPathRequest('bzr')
288
result = resolve.submit(service)
289
self.assertTrue('urls' in result)
290
self.assertEquals(result['urls'], [
291
'bzr+ssh://bazaar.launchpad.net~bzr/bzr/trunk',
292
'sftp://bazaar.launchpad.net~bzr/bzr/trunk',
293
'bzr+http://bazaar.launchpad.net~bzr/bzr/trunk',
294
'http://bazaar.launchpad.net~bzr/bzr/trunk'])
297
class TestGatherUserCredentials(tests.TestCaseInTempDir):
300
super(TestGatherUserCredentials, self).setUp()
301
# make sure we have a reproducible standard environment
302
self._captureVar('BZR_LP_XMLRPC_URL', None)
304
def test_gather_user_credentials_has_password(self):
305
service = LaunchpadService()
306
service.registrant_password = 'mypassword'
307
# This should be a basic no-op, since we already have the password
308
service.gather_user_credentials()
309
self.assertEqual('mypassword', service.registrant_password)
311
def test_gather_user_credentials_from_auth_conf(self):
312
auth_path = config.authentication_config_filename()
313
service = LaunchpadService()
314
g_conf = config.GlobalConfig()
315
g_conf.set_user_option('email', 'Test User <test@user.com>')
316
f = open(auth_path, 'wb')
318
scheme, hostinfo = urlparse.urlsplit(service.service_url)[:2]
319
f.write('[section]\n'
322
'user=test@user.com\n'
323
'password=testpass\n'
324
% (scheme, hostinfo))
327
self.assertIs(None, service.registrant_password)
328
service.gather_user_credentials()
329
self.assertEqual('test@user.com', service.registrant_email)
330
self.assertEqual('testpass', service.registrant_password)
332
def test_gather_user_credentials_prompts(self):
333
service = LaunchpadService()
334
self.assertIs(None, service.registrant_password)
335
g_conf = config.GlobalConfig()
336
g_conf.set_user_option('email', 'Test User <test@user.com>')
337
stdout = tests.StringIOWrapper()
338
stderr = tests.StringIOWrapper()
339
ui.ui_factory = tests.TestUIFactory(stdin='userpass\n',
340
stdout=stdout, stderr=stderr)
341
self.assertIs(None, service.registrant_password)
342
service.gather_user_credentials()
343
self.assertEqual('test@user.com', service.registrant_email)
344
self.assertEqual('userpass', service.registrant_password)
345
self.assertEquals('', stdout.getvalue())
346
self.assertContainsRe(stderr.getvalue(),
347
'launchpad.net password for test@user\\.com')