summaryrefslogtreecommitdiff
path: root/bzrlib/plugins/launchpad/test_register.py
diff options
context:
space:
mode:
Diffstat (limited to 'bzrlib/plugins/launchpad/test_register.py')
-rw-r--r--bzrlib/plugins/launchpad/test_register.py366
1 files changed, 366 insertions, 0 deletions
diff --git a/bzrlib/plugins/launchpad/test_register.py b/bzrlib/plugins/launchpad/test_register.py
new file mode 100644
index 0000000..81aadc7
--- /dev/null
+++ b/bzrlib/plugins/launchpad/test_register.py
@@ -0,0 +1,366 @@
+# Copyright (C) 2006-2011 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+import base64
+from StringIO import StringIO
+import urlparse
+import xmlrpclib
+
+from bzrlib import (
+ config,
+ tests,
+ ui,
+ )
+from bzrlib.tests import TestCaseWithTransport
+
+# local import
+from bzrlib.plugins.launchpad.lp_registration import (
+ BaseRequest,
+ BranchBugLinkRequest,
+ BranchRegistrationRequest,
+ ResolveLaunchpadPathRequest,
+ LaunchpadService,
+ )
+
+
+# TODO: Test that the command-line client, making sure that it'll pass the
+# request through to a dummy transport, and that the transport will validate
+# the results passed in. Not sure how to get the transport object back out to
+# validate that its OK - may not be necessary.
+
+# TODO: Add test for (and implement) other command-line options to set
+# project, author_email, description.
+
+# TODO: project_id is not properly handled -- must be passed in rpc or path.
+
+class InstrumentedXMLRPCConnection(object):
+ """Stands in place of an http connection for the purposes of testing"""
+
+ def __init__(self, testcase):
+ self.testcase = testcase
+
+ def getreply(self):
+ """Fake the http reply.
+
+ :returns: (errcode, errmsg, headers)
+ """
+ return (200, 'OK', [])
+
+ def getresponse(self, buffering=True):
+ """Fake the http reply.
+
+ This is used when running on Python 2.7, where xmlrpclib uses
+ httplib.HTTPConnection in a different way than before.
+ """
+ class FakeHttpResponse(object):
+
+ def __init__(self, status, reason, body):
+ self.status = status
+ self.reason = reason
+ self.body = body
+
+ def read(self, size=-1):
+ return self.body.read(size)
+
+ def getheader(self, name, default):
+ # We don't have headers
+ return default
+
+ return FakeHttpResponse(200, 'OK', self.getfile())
+
+ def getfile(self):
+ """Return a fake file containing the response content."""
+ return StringIO('''\
+<?xml version="1.0" ?>
+<methodResponse>
+ <params>
+ <param>
+ <value>
+ <string>victoria dock</string>
+ </value>
+ </param>
+ </params>
+</methodResponse>''')
+
+
+
+class InstrumentedXMLRPCTransport(xmlrpclib.Transport):
+
+ # Python 2.5's xmlrpclib looks for this.
+ _use_datetime = False
+
+ def __init__(self, testcase, expect_auth):
+ self.testcase = testcase
+ self.expect_auth = expect_auth
+ self._connection = (None, None)
+
+ def make_connection(self, host):
+ host, http_headers, x509 = self.get_host_info(host)
+ test = self.testcase
+ self.connected_host = host
+ if self.expect_auth:
+ auth_hdrs = [v for k,v in http_headers if k == 'Authorization']
+ if len(auth_hdrs) != 1:
+ raise AssertionError("multiple auth headers: %r"
+ % (auth_hdrs,))
+ authinfo = auth_hdrs[0]
+ expected_auth = 'testuser@launchpad.net:testpassword'
+ test.assertEquals(authinfo,
+ 'Basic ' + base64.encodestring(expected_auth).strip())
+ elif http_headers:
+ raise AssertionError()
+ return InstrumentedXMLRPCConnection(test)
+
+ def send_request(self, connection, handler_path, request_body):
+ test = self.testcase
+ self.got_request = True
+
+ def send_host(self, conn, host):
+ pass
+
+ def send_user_agent(self, conn):
+ # TODO: send special user agent string, including bzrlib version
+ # number
+ pass
+
+ def send_content(self, conn, request_body):
+ unpacked, method = xmlrpclib.loads(request_body)
+ if None in unpacked:
+ raise AssertionError(
+ "xmlrpc result %r shouldn't contain None" % (unpacked,))
+ self.sent_params = unpacked
+
+
+class MockLaunchpadService(LaunchpadService):
+
+ def send_request(self, method_name, method_params, authenticated):
+ """Stash away the method details rather than sending them to a real server"""
+ self.called_method_name = method_name
+ self.called_method_params = method_params
+ self.called_authenticated = authenticated
+
+
+class TestBranchRegistration(TestCaseWithTransport):
+
+ def setUp(self):
+ super(TestBranchRegistration, self).setUp()
+ # make sure we have a reproducible standard environment
+ self.overrideEnv('BZR_LP_XMLRPC_URL', None)
+
+ def test_register_help(self):
+ """register-branch accepts --help"""
+ out, err = self.run_bzr(['register-branch', '--help'])
+ self.assertContainsRe(out, r'Register a branch')
+
+ def test_register_no_url_no_branch(self):
+ """register-branch command requires parameters"""
+ self.make_repository('.')
+ self.run_bzr_error(
+ ['register-branch requires a public branch url - '
+ 'see bzr help register-branch'],
+ 'register-branch')
+
+ def test_register_no_url_in_published_branch_no_error(self):
+ b = self.make_branch('.')
+ b.set_public_branch('http://test-server.com/bzr/branch')
+ out, err = self.run_bzr(['register-branch', '--dry-run'])
+ self.assertEqual('Branch registered.\n', out)
+ self.assertEqual('', err)
+
+ def test_register_no_url_in_unpublished_branch_errors(self):
+ b = self.make_branch('.')
+ out, err = self.run_bzr_error(['no public branch'],
+ ['register-branch', '--dry-run'])
+ self.assertEqual('', out)
+
+ def test_register_dry_run(self):
+ out, err = self.run_bzr(['register-branch',
+ 'http://test-server.com/bzr/branch',
+ '--dry-run'])
+ self.assertEquals(out, 'Branch registered.\n')
+
+ def test_onto_transport(self):
+ """How the request is sent by transmitting across a mock Transport"""
+ # use a real transport, but intercept at the http/xml layer
+ transport = InstrumentedXMLRPCTransport(self, expect_auth=True)
+ service = LaunchpadService(transport)
+ service.registrant_email = 'testuser@launchpad.net'
+ service.registrant_password = 'testpassword'
+ rego = BranchRegistrationRequest('http://test-server.com/bzr/branch',
+ 'branch-id',
+ 'my test branch',
+ 'description',
+ 'author@launchpad.net',
+ 'product')
+ rego.submit(service)
+ self.assertEquals(transport.connected_host, 'xmlrpc.launchpad.net')
+ self.assertEquals(len(transport.sent_params), 6)
+ self.assertEquals(transport.sent_params,
+ ('http://test-server.com/bzr/branch', # branch_url
+ 'branch-id', # branch_name
+ 'my test branch', # branch_title
+ 'description',
+ 'author@launchpad.net',
+ 'product'))
+ self.assertTrue(transport.got_request)
+
+ def test_onto_transport_unauthenticated(self):
+ """An unauthenticated request is transmitted across a mock Transport"""
+ transport = InstrumentedXMLRPCTransport(self, expect_auth=False)
+ service = LaunchpadService(transport)
+ resolve = ResolveLaunchpadPathRequest('bzr')
+ resolve.submit(service)
+ self.assertEquals(transport.connected_host, 'xmlrpc.launchpad.net')
+ self.assertEquals(len(transport.sent_params), 1)
+ self.assertEquals(transport.sent_params, ('bzr', ))
+ self.assertTrue(transport.got_request)
+
+ def test_subclass_request(self):
+ """Define a new type of xmlrpc request"""
+ class DummyRequest(BaseRequest):
+ _methodname = 'dummy_request'
+ def _request_params(self):
+ return (42,)
+
+ service = MockLaunchpadService()
+ service.registrant_email = 'test@launchpad.net'
+ service.registrant_password = ''
+ request = DummyRequest()
+ request.submit(service)
+ self.assertEquals(service.called_method_name, 'dummy_request')
+ self.assertEquals(service.called_method_params, (42,))
+
+ def test_mock_server_registration(self):
+ """Send registration to mock server"""
+ test_case = self
+ class MockRegistrationService(MockLaunchpadService):
+ def send_request(self, method_name, method_params, authenticated):
+ test_case.assertEquals(method_name, "register_branch")
+ test_case.assertEquals(list(method_params),
+ ['url', 'name', 'title', 'description', 'email', 'name'])
+ test_case.assertEquals(authenticated, True)
+ return 'result'
+ service = MockRegistrationService()
+ rego = BranchRegistrationRequest('url', 'name', 'title',
+ 'description', 'email', 'name')
+ result = rego.submit(service)
+ self.assertEquals(result, 'result')
+
+ def test_mock_server_registration_with_defaults(self):
+ """Send registration to mock server"""
+ test_case = self
+ class MockRegistrationService(MockLaunchpadService):
+ def send_request(self, method_name, method_params, authenticated):
+ test_case.assertEquals(method_name, "register_branch")
+ test_case.assertEquals(list(method_params),
+ ['http://server/branch', 'branch', '', '', '', ''])
+ test_case.assertEquals(authenticated, True)
+ return 'result'
+ service = MockRegistrationService()
+ rego = BranchRegistrationRequest('http://server/branch')
+ result = rego.submit(service)
+ self.assertEquals(result, 'result')
+
+ def test_mock_bug_branch_link(self):
+ """Send bug-branch link to mock server"""
+ test_case = self
+ class MockService(MockLaunchpadService):
+ def send_request(self, method_name, method_params, authenticated):
+ test_case.assertEquals(method_name, "link_branch_to_bug")
+ test_case.assertEquals(list(method_params),
+ ['http://server/branch', 1234, ''])
+ test_case.assertEquals(authenticated, True)
+ return 'http://launchpad.net/bug/1234'
+ service = MockService()
+ rego = BranchBugLinkRequest('http://server/branch', 1234)
+ result = rego.submit(service)
+ self.assertEquals(result, 'http://launchpad.net/bug/1234')
+
+ def test_mock_resolve_lp_url(self):
+ test_case = self
+ class MockService(MockLaunchpadService):
+ def send_request(self, method_name, method_params, authenticated):
+ test_case.assertEquals(method_name, "resolve_lp_path")
+ test_case.assertEquals(list(method_params), ['bzr'])
+ test_case.assertEquals(authenticated, False)
+ return dict(urls=[
+ 'bzr+ssh://bazaar.launchpad.net~bzr/bzr/trunk',
+ 'sftp://bazaar.launchpad.net~bzr/bzr/trunk',
+ 'bzr+http://bazaar.launchpad.net~bzr/bzr/trunk',
+ 'http://bazaar.launchpad.net~bzr/bzr/trunk'])
+ service = MockService()
+ resolve = ResolveLaunchpadPathRequest('bzr')
+ result = resolve.submit(service)
+ self.assertTrue('urls' in result)
+ self.assertEquals(result['urls'], [
+ 'bzr+ssh://bazaar.launchpad.net~bzr/bzr/trunk',
+ 'sftp://bazaar.launchpad.net~bzr/bzr/trunk',
+ 'bzr+http://bazaar.launchpad.net~bzr/bzr/trunk',
+ 'http://bazaar.launchpad.net~bzr/bzr/trunk'])
+
+
+class TestGatherUserCredentials(tests.TestCaseInTempDir):
+
+ def setUp(self):
+ super(TestGatherUserCredentials, self).setUp()
+ # make sure we have a reproducible standard environment
+ self.overrideEnv('BZR_LP_XMLRPC_URL', None)
+
+ def test_gather_user_credentials_has_password(self):
+ service = LaunchpadService()
+ service.registrant_password = 'mypassword'
+ # This should be a basic no-op, since we already have the password
+ service.gather_user_credentials()
+ self.assertEqual('mypassword', service.registrant_password)
+
+ def test_gather_user_credentials_from_auth_conf(self):
+ auth_path = config.authentication_config_filename()
+ service = LaunchpadService()
+ g_conf = config.GlobalStack()
+ g_conf.set('email', 'Test User <test@user.com>')
+ f = open(auth_path, 'wb')
+ try:
+ scheme, hostinfo = urlparse.urlsplit(service.service_url)[:2]
+ f.write('[section]\n'
+ 'scheme=%s\n'
+ 'host=%s\n'
+ 'user=test@user.com\n'
+ 'password=testpass\n'
+ % (scheme, hostinfo))
+ finally:
+ f.close()
+ self.assertIs(None, service.registrant_password)
+ service.gather_user_credentials()
+ self.assertEqual('test@user.com', service.registrant_email)
+ self.assertEqual('testpass', service.registrant_password)
+
+ def test_gather_user_credentials_prompts(self):
+ service = LaunchpadService()
+ self.assertIs(None, service.registrant_password)
+ g_conf = config.GlobalStack()
+ g_conf.set('email', 'Test User <test@user.com>')
+ stdout = tests.StringIOWrapper()
+ stderr = tests.StringIOWrapper()
+ ui.ui_factory = tests.TestUIFactory(stdin='userpass\n',
+ stdout=stdout, stderr=stderr)
+ self.assertIs(None, service.registrant_password)
+ service.gather_user_credentials()
+ self.assertEqual('test@user.com', service.registrant_email)
+ self.assertEqual('userpass', service.registrant_password)
+ self.assertEquals('', stdout.getvalue())
+ self.assertContainsRe(stderr.getvalue(),
+ 'launchpad.net password for test@user\\.com')
+