Rev 2980: (robertc) Improved lp:/// URL support for the Launchpad plugin (James Henstridge). in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Mon Nov 12 21:23:11 GMT 2007


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 2980
revision-id: pqm at pqm.ubuntu.com-20071112212307-eusj64ymto8l9abk
parent: pqm at pqm.ubuntu.com-20071112195430-0xgqswqpc1j2pk2m
parent: james at jamesh.id.au-20071031232330-c9zuhvdhhvch9wly
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2007-11-12 21:23:07 +0000
message:
  (robertc) Improved lp:/// URL support for the Launchpad plugin (James Henstridge).
modified:
  bzrlib/plugins/launchpad/__init__.py __init__.py-20060315182712-2d5feebd2a1032dc
  bzrlib/plugins/launchpad/lp_indirect.py lp_indirect.py-20070126012204-de5rugwlt22c7u7e-1
  bzrlib/plugins/launchpad/lp_registration.py lp_registration.py-20060315190948-daa617eafe3a8d48
  bzrlib/plugins/launchpad/test_lp_indirect.py test_lp_indirect.py-20070126002743-oyle362tzv9cd8mi-1
  bzrlib/plugins/launchpad/test_register.py test_register.py-20060315182712-40f5dda945c829a8
    ------------------------------------------------------------
    revno: 2898.4.17
    merged: james at jamesh.id.au-20071031232330-c9zuhvdhhvch9wly
    parent: james at jamesh.id.au-20071031231023-3m5adgrdgluedbm0
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Wed 2007-10-31 19:23:30 -0400
    message:
      Add a test that the redirect actually occurs when opening an lp: URL.
    ------------------------------------------------------------
    revno: 2898.4.16
    merged: james at jamesh.id.au-20071031231023-3m5adgrdgluedbm0
    parent: james at jamesh.id.au-20071031194745-u5nlc14tr74w7t3g
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Wed 2007-10-31 19:10:23 -0400
    message:
      Small spelling errors pointed out by Ian.
    ------------------------------------------------------------
    revno: 2898.4.15
    merged: james at jamesh.id.au-20071031194745-u5nlc14tr74w7t3g
    parent: james at jamesh.id.au-20071031164031-9v35bhm3bb6tpes7
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Wed 2007-10-31 15:47:45 -0400
    message:
      Use get_transport() to decide whether Bazaar supports a given URL.
    ------------------------------------------------------------
    revno: 2898.4.14
    merged: james at jamesh.id.au-20071031164031-9v35bhm3bb6tpes7
    parent: james at jamesh.id.au-20071030205501-hpkyk9zl43kubuea
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Wed 2007-10-31 12:40:31 -0400
    message:
      * Use urlsplit() to process URLs.
      * Log the result of the RPC call if the 'launchpad' debug flag is set.
    ------------------------------------------------------------
    revno: 2898.4.13
    merged: james at jamesh.id.au-20071030205501-hpkyk9zl43kubuea
    parent: james at jamesh.id.au-20071030204623-57gtf0rwqp50cn13
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Tue 2007-10-30 16:55:01 -0400
    message:
      Remove some transport methods that shouldn't be needed to process the 
      redirect.
    ------------------------------------------------------------
    revno: 2898.4.12
    merged: james at jamesh.id.au-20071030204623-57gtf0rwqp50cn13
    parent: james at jamesh.id.au-20071030203505-ee7csno3ruhofal9
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Tue 2007-10-30 16:46:23 -0400
    message:
      Add tests that redirects get issued by appropriate transport methods.
    ------------------------------------------------------------
    revno: 2898.4.11
    merged: james at jamesh.id.au-20071030203505-ee7csno3ruhofal9
    parent: james at jamesh.id.au-20071018145455-sdli9rfh3y1z06b4
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Tue 2007-10-30 16:35:05 -0400
    message:
      Switch back to RedirectRequested based implementation.
    ------------------------------------------------------------
    revno: 2898.4.10
    merged: james at jamesh.id.au-20071018145455-sdli9rfh3y1z06b4
    parent: james at jamesh.id.au-20071018145412-i9rv84pmxkvojpup
    parent: james at jamesh.id.au-20071018085819-zv64jvki41van9fq
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Thu 2007-10-18 22:54:55 +0800
    message:
      Merge from bzr.lp-login branch, to pick up test fixes.
    ------------------------------------------------------------
    revno: 2898.4.9
    merged: james at jamesh.id.au-20071018145412-i9rv84pmxkvojpup
    parent: james at jamesh.id.au-20071016072727-liemzmhq891upybl
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Thu 2007-10-18 22:54:12 +0800
    message:
      Add some more tests for the bzr+ssh://bazaar.launchpad.net URL 
      rewriting (and fix the code to actually work).
    ------------------------------------------------------------
    revno: 2898.4.8
    merged: james at jamesh.id.au-20071016072727-liemzmhq891upybl
    parent: james at jamesh.id.au-20071014095255-val0oiv5749km3vp
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Tue 2007-10-16 20:27:27 +1300
    message:
      Switch lp: over to a pass-through transport, so that the XMLRPC gets 
      delayed to the point where the transport is actually used.
    ------------------------------------------------------------
    revno: 2898.4.7
    merged: james at jamesh.id.au-20071014095255-val0oiv5749km3vp
    parent: james at jamesh.id.au-20071014095015-j97ewee17veci33b
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Sun 2007-10-14 22:52:55 +1300
    message:
      Fix up tests.
    ------------------------------------------------------------
    revno: 2898.4.6
    merged: james at jamesh.id.au-20071014095015-j97ewee17veci33b
    parent: james at jamesh.id.au-20071010055425-4ubwojz01blad91s
    parent: james at jamesh.id.au-20071012044216-1aystcylm32fjb67
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-121200
    timestamp: Sun 2007-10-14 22:50:15 +1300
    message:
      merge from launchpad-login branch
    ------------------------------------------------------------
    revno: 2898.4.5
    merged: james at jamesh.id.au-20071010055425-4ubwojz01blad91s
    parent: james at jamesh.id.au-20071010052400-ufkjeee2zzlsli03
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-1200
    timestamp: Wed 2007-10-10 18:54:25 +1300
    message:
      get rid of extra space in messages
    ------------------------------------------------------------
    revno: 2898.4.4
    merged: james at jamesh.id.au-20071010052400-ufkjeee2zzlsli03
    parent: james at jamesh.id.au-20071010033447-3wjyvy4tlnhphwi7
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-1200
    timestamp: Wed 2007-10-10 18:24:00 +1300
    message:
      Changes to account for modifications to the XMLRPC API.
    ------------------------------------------------------------
    revno: 2898.4.3
    merged: james at jamesh.id.au-20071010033447-3wjyvy4tlnhphwi7
    parent: james at jamesh.id.au-20071010015711-u0uq2y7ldbqj3fmz
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-1200
    timestamp: Wed 2007-10-10 16:34:47 +1300
    message:
      Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
    ------------------------------------------------------------
    revno: 2898.4.2
    merged: james at jamesh.id.au-20071010015711-u0uq2y7ldbqj3fmz
    parent: james at jamesh.id.au-20071010012542-d0cq7ttawfhptlve
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-1200
    timestamp: Wed 2007-10-10 14:57:11 +1300
    message:
      Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
    ------------------------------------------------------------
    revno: 2898.4.1
    merged: james at jamesh.id.au-20071010012542-d0cq7ttawfhptlve
    parent: pqm at pqm.ubuntu.com-20071009044446-uliu5z9a52bzmps8
    committer: James Henstridge <james at jamesh.id.au>
    branch nick: bzr.lp-urls-bug-1200
    timestamp: Wed 2007-10-10 14:25:42 +1300
    message:
      Make it possible to make unauthenticated XML-RPC requests.
=== modified file 'bzrlib/plugins/launchpad/__init__.py'
--- a/bzrlib/plugins/launchpad/__init__.py	2007-10-24 08:12:18 +0000
+++ b/bzrlib/plugins/launchpad/__init__.py	2007-11-12 21:23:07 +0000
@@ -158,12 +158,12 @@
 register_lazy_transport(
     'lp:',
     'bzrlib.plugins.launchpad.lp_indirect',
-    'launchpad_transport_indirect')
+    'LaunchpadTransport')
 
 register_lazy_transport(
     'lp://',
     'bzrlib.plugins.launchpad.lp_indirect',
-    'launchpad_transport_indirect')
+    'LaunchpadTransport')
 
 def test_suite():
     """Called by bzrlib to fetch tests for this plugin"""

=== modified file 'bzrlib/plugins/launchpad/lp_indirect.py'
--- a/bzrlib/plugins/launchpad/lp_indirect.py	2007-01-26 04:44:10 +0000
+++ b/bzrlib/plugins/launchpad/lp_indirect.py	2007-10-31 19:47:45 +0000
@@ -22,24 +22,112 @@
 again.
 """
 
+from urlparse import urlsplit, urlunsplit
+import xmlrpclib
+
 from bzrlib import (
+    debug,
     errors,
+    trace,
+    urlutils,
     )
 from bzrlib.transport import (
     get_transport,
+    register_urlparse_netloc_protocol,
     Transport,
     )
 
-
-def launchpad_transport_indirect(base_url):
-    """Uses Launchpad.net as a directory of open source software"""
-    if base_url.startswith('lp:///'):
-        real_url = 'http://code.launchpad.net/' + base_url[6:]
-    elif base_url.startswith('lp:') and base_url[3] != '/':
-        real_url = 'http://code.launchpad.net/' + base_url[3:]
-    else:
-        raise errors.InvalidURL(path=base_url)
-    return get_transport(real_url)
+from bzrlib.plugins.launchpad.lp_registration import (
+    LaunchpadService, ResolveLaunchpadPathRequest)
+from bzrlib.plugins.launchpad.account import get_lp_login
+
+
+# As bzrlib.transport.remote may not be loaded yet, make sure bzr+ssh
+# is counted as a netloc protocol.
+register_urlparse_netloc_protocol('bzr+ssh')
+register_urlparse_netloc_protocol('lp')
+
+
+class LaunchpadTransport(Transport):
+    """lp:/// URL transport
+
+    This transport redirects requests to the real branch location
+    after resolving the URL via an XMLRPC request to Launchpad.
+    """
+
+    def __init__(self, base):
+        super(LaunchpadTransport, self).__init__(base)
+        # We only support URLs without a netloc
+        netloc = urlsplit(base)[1]
+        if netloc != '':
+            raise errors.InvalidURL(path=base)
+
+    def _resolve(self, abspath,
+                 _request_factory=ResolveLaunchpadPathRequest,
+                 _lp_login=None):
+        """Resolve the base URL for this transport."""
+        path = urlsplit(abspath)[2].lstrip('/')
+        # Perform an XMLRPC request to resolve the path
+        resolve = _request_factory(path)
+        service = LaunchpadService()
+        try:
+            result = resolve.submit(service)
+        except xmlrpclib.Fault, fault:
+            raise errors.InvalidURL(
+                path=abspath, extra=fault.faultString)
+
+        if 'launchpad' in debug.debug_flags:
+            trace.mutter("resolve_lp_path(%r) == %r", path, result)
+
+        if _lp_login is None:
+            _lp_login = get_lp_login()
+        for url in result['urls']:
+            scheme, netloc, path, query, fragment = urlsplit(url)
+            if scheme == 'bzr+ssh' and (netloc.endswith('launchpad.net') or
+                                        netloc.endswith('launchpad.dev')):
+                # Only accept launchpad.net bzr+ssh URLs if we know
+                # the user's Launchpad login:
+                if _lp_login is None:
+                    continue
+                url = urlunsplit((scheme, '%s@%s' % (_lp_login, netloc),
+                                  path, query, fragment))
+                break
+            else:
+                # Use the URL if we can create a transport for it.
+                try:
+                    get_transport(url)
+                except (errors.PathError, errors.TransportError):
+                    pass
+                else:
+                    break
+        else:
+            raise errors.InvalidURL(path=abspath,
+                                    extra='no supported schemes')
+        return url
+
+    def _request_redirect(self, relpath):
+        source = urlutils.join(self.base, relpath)
+        # Split the source location into the branch location, and the
+        # extra path components.
+        pos = source.find('/.bzr/')
+        if pos >= 0:
+            branchpath = source[:pos]
+            extra = source[pos:]
+        else:
+            branchpath = source
+            extra = ''
+        target = self._resolve(branchpath) + extra
+        raise errors.RedirectRequested(
+            source=source,
+            target=target)
+
+    def get(self, relpath):
+        """See Transport.get()."""
+        self._request_redirect(relpath)
+
+    def mkdir(self, relpath, mode=None):
+        """See Transport.mkdir()."""
+        self._request_redirect(relpath)
 
 
 def get_test_permutations():

=== modified file 'bzrlib/plugins/launchpad/lp_registration.py'
--- a/bzrlib/plugins/launchpad/lp_registration.py	2007-10-23 14:31:10 +0000
+++ b/bzrlib/plugins/launchpad/lp_registration.py	2007-11-12 21:23:07 +0000
@@ -72,25 +72,29 @@
         else:
             return self.DEFAULT_SERVICE_URL
 
-    def get_proxy(self):
+    def get_proxy(self, authenticated):
         """Return the proxy for XMLRPC requests."""
-        # auth info must be in url
-        # TODO: if there's no registrant email perhaps we should just connect
-        # anonymously?
-        scheme, hostinfo, path = urlsplit(self.service_url)[:3]
-        assert '@' not in hostinfo
-        assert self.registrant_email is not None
-        assert self.registrant_password is not None
-        # TODO: perhaps fully quote the password to make it very slightly
-        # obscured
-        # TODO: can we perhaps add extra Authorization headers directly to the 
-        # request, rather than putting this into the url?  perhaps a bit more 
-        # secure against accidentally revealing it.  std66 s3.2.1 discourages putting
-        # the password in the url.
-        hostinfo = '%s:%s@%s' % (urllib.quote(self.registrant_email),
-                                 urllib.quote(self.registrant_password),
-                                 hostinfo)
-        url = urlunsplit((scheme, hostinfo, path, '', ''))
+        if authenticated:
+            # auth info must be in url
+            # TODO: if there's no registrant email perhaps we should
+            # just connect anonymously?
+            scheme, hostinfo, path = urlsplit(self.service_url)[:3]
+            assert '@' not in hostinfo
+            assert self.registrant_email is not None
+            assert self.registrant_password is not None
+            # TODO: perhaps fully quote the password to make it very slightly
+            # obscured
+            # TODO: can we perhaps add extra Authorization headers
+            # directly to the request, rather than putting this into
+            # the url?  perhaps a bit more secure against accidentally
+            # revealing it.  std66 s3.2.1 discourages putting the
+            # password in the url.
+            hostinfo = '%s:%s@%s' % (urllib.quote(self.registrant_email),
+                                     urllib.quote(self.registrant_password),
+                                     hostinfo)
+            url = urlunsplit((scheme, hostinfo, path, '', ''))
+        else:
+            url = self.service_url
         return xmlrpclib.ServerProxy(url, transport=self.transport)
 
     def gather_user_credentials(self):
@@ -107,8 +111,8 @@
             self.registrant_password = auth.get_password(scheme, hostinfo,
                                                          prompt=prompt)
 
-    def send_request(self, method_name, method_params):
-        proxy = self.get_proxy()
+    def send_request(self, method_name, method_params, authenticated):
+        proxy = self.get_proxy(authenticated)
         assert method_name
         method = getattr(proxy, method_name)
         try:
@@ -134,6 +138,7 @@
 
     # Set this to the XMLRPC method name.
     _methodname = None
+    _authenticated = True
 
     def _request_params(self):
         """Return the arguments to pass to the method"""
@@ -145,7 +150,8 @@
         :param service: LaunchpadService indicating where to send
             the request and the authentication credentials.
         """
-        return service.send_request(self._methodname, self._request_params())
+        return service.send_request(self._methodname, self._request_params(),
+                                    self._authenticated)
 
 
 class DryRunLaunchpadService(LaunchpadService):
@@ -154,7 +160,7 @@
     The dummy service does not need authentication.
     """
 
-    def send_request(self, method_name, method_params):
+    def send_request(self, method_name, method_params, authenticated):
         pass
 
     def gather_user_credentials(self):
@@ -216,3 +222,18 @@
         # This must match the parameter tuple expected by Launchpad for this
         # method
         return (self.branch_url, self.bug_id, '')
+
+
+class ResolveLaunchpadPathRequest(BaseRequest):
+    """Request to resolve the path component of an lp: URL."""
+
+    _methodname = 'resolve_lp_path'
+    _authenticated = False
+
+    def __init__(self, path):
+        assert path
+        self.path = path
+
+    def _request_params(self):
+        """Return xmlrpc request parameters"""
+        return (self.path,)

=== modified file 'bzrlib/plugins/launchpad/test_lp_indirect.py'
--- a/bzrlib/plugins/launchpad/test_lp_indirect.py	2007-02-08 08:33:26 +0000
+++ b/bzrlib/plugins/launchpad/test_lp_indirect.py	2007-10-31 23:23:30 +0000
@@ -16,12 +16,31 @@
 
 """Tests for indirect branch urls through Launchpad.net"""
 
+import xmlrpclib
+
 from bzrlib import (
     errors,
-    transport,
     )
+from bzrlib.branch import Branch
+from bzrlib.tests import TestCase, TestCaseWithMemoryTransport
 from bzrlib.transport import get_transport
-from bzrlib.tests import TestCase, TestSkipped
+from bzrlib.plugins.launchpad.lp_indirect import (
+    LaunchpadTransport)
+from bzrlib.plugins.launchpad.account import get_lp_login
+
+
+class FakeResolveFactory(object):
+    def __init__(self, test, expected_path, result):
+        self._test = test
+        self._expected_path = expected_path
+        self._result = result
+
+    def __call__(self, path):
+        self._test.assertEqual(self._expected_path, path)
+        return self
+
+    def submit(self, service):
+        return self._result
 
 
 class IndirectUrlTests(TestCase):
@@ -29,22 +48,127 @@
 
     def test_short_form(self):
         """A launchpad url should map to a http url"""
-        url = 'lp:apt'
-        t = get_transport(url)
-        self.assertEquals(t.base, 'http://code.launchpad.net/apt/')
+        factory = FakeResolveFactory(
+            self, 'apt', dict(urls=[
+                    'http://bazaar.launchpad.net/~apt/apt/devel']))
+        transport = LaunchpadTransport('lp:///')
+        self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
+                          transport._resolve('lp:apt', factory))
 
     def test_indirect_through_url(self):
         """A launchpad url should map to a http url"""
-        # These can change to use the smartserver protocol or something 
-        # else in the future.
-        url = 'lp:///apt'
-        t = get_transport(url)
-        real_url = t.base
-        self.assertEquals(real_url, 'http://code.launchpad.net/apt/')
+        factory = FakeResolveFactory(
+            self, 'apt', dict(urls=[
+                    'http://bazaar.launchpad.net/~apt/apt/devel']))
+        transport = LaunchpadTransport('lp:///')
+        self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
+                          transport._resolve('lp:///apt', factory))
+
+    def test_indirect_skip_bad_schemes(self):
+        factory = FakeResolveFactory(
+            self, 'apt', dict(urls=[
+                    'bad-scheme://bazaar.launchpad.net/~apt/apt/devel',
+                    'http://bazaar.launchpad.net/~apt/apt/devel',
+                    'http://another/location']))
+        transport = LaunchpadTransport('lp:///')
+        self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
+                          transport._resolve('lp:///apt', factory))
+
+    def test_indirect_no_matching_schemes(self):
+        # If the XMLRPC call does not return any protocols we support,
+        # invalidURL is raised.
+        factory = FakeResolveFactory(
+            self, 'apt', dict(urls=[
+                    'bad-scheme://bazaar.launchpad.net/~apt/apt/devel']))
+        transport = LaunchpadTransport('lp:///')
+        self.assertRaises(errors.InvalidURL,
+                          transport._resolve, 'lp:///apt', factory)
+
+    def test_indirect_fault(self):
+        # Test that XMLRPC faults get converted to InvalidURL errors.
+        factory = FakeResolveFactory(self, 'apt', None)
+        def submit(service):
+            raise xmlrpclib.Fault(42, 'something went wrong')
+        factory.submit = submit
+        transport = LaunchpadTransport('lp:///')
+        self.assertRaises(errors.InvalidURL,
+                          transport._resolve, 'lp:///apt', factory)
+
+    def test_skip_bzr_ssh_launchpad_net_when_anonymous(self):
+        # Test that bzr+ssh://bazaar.launchpad.net gets skipped if
+        # Bazaar does not know the user's Launchpad ID:
+        self.assertEqual(None, get_lp_login())
+        factory = FakeResolveFactory(
+            self, 'apt', dict(urls=[
+                    'bzr+ssh://bazaar.launchpad.net/~apt/apt/devel',
+                    'http://bazaar.launchpad.net/~apt/apt/devel']))
+        transport = LaunchpadTransport('lp:///')
+        self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
+                          transport._resolve('lp:///apt', factory))
+
+    def test_rewrite_bzr_ssh_launchpad_net(self):
+        # Test that bzr+ssh URLs get rewritten to include the user's
+        # Launchpad ID (assuming we know the Launchpad ID).
+        factory = FakeResolveFactory(
+            self, 'apt', dict(urls=[
+                    'bzr+ssh://bazaar.launchpad.net/~apt/apt/devel',
+                    'http://bazaar.launchpad.net/~apt/apt/devel']))
+        transport = LaunchpadTransport('lp:///')
+        self.assertEquals(
+            'bzr+ssh://username@bazaar.launchpad.net/~apt/apt/devel',
+            transport._resolve('lp:///apt', factory, _lp_login='username'))
+
+    def test_no_rewrite_of_other_bzr_ssh(self):
+        # Test that we don't rewrite bzr+ssh URLs for other 
+        self.assertEqual(None, get_lp_login())
+        factory = FakeResolveFactory(
+            self, 'apt', dict(urls=[
+                    'bzr+ssh://example.com/~apt/apt/devel',
+                    'http://bazaar.launchpad.net/~apt/apt/devel']))
+        transport = LaunchpadTransport('lp:///')
+        self.assertEquals('bzr+ssh://example.com/~apt/apt/devel',
+                          transport._resolve('lp:///apt', factory))
 
     # TODO: check we get an error if the url is unreasonable
     def test_error_for_bad_indirection(self):
         self.assertRaises(errors.InvalidURL,
-            get_transport,
-            'lp://ratotehunoahu')
-
+            LaunchpadTransport, 'lp://ratotehunoahu')
+
+    def catch_redirect(self, methodname, *args):
+        transport = LaunchpadTransport('lp:///apt')
+        def _resolve(abspath):
+            self.assertEqual('lp:///apt', abspath)
+            return 'http://example.com/~apt/apt/devel'
+        transport._resolve = _resolve
+        try:
+            getattr(transport, methodname)(*args)
+        except errors.RedirectRequested, exc:
+            return exc
+        else:
+            raise self.failException('RedirectRequested not raised')
+
+    def test_redirect_on_get(self):
+        exc = self.catch_redirect('get', '.bzr/branch-format')
+        self.assertEqual('lp:///apt/.bzr/branch-format', exc.source)
+        self.assertEqual(
+            'http://example.com/~apt/apt/devel/.bzr/branch-format', exc.target)
+
+    def test_redirect_on_mkdir(self):
+        exc = self.catch_redirect('mkdir', '.')
+        self.assertEqual('lp:///apt', exc.source)
+        self.assertEqual(
+            'http://example.com/~apt/apt/devel', exc.target)
+
+
+class IndirectOpenBranchTests(TestCaseWithMemoryTransport):
+
+    def test_indirect_open_branch(self):
+        # Test that opening an lp: branch redirects to the real location.
+        target_branch = self.make_branch('target')
+        transport = get_transport('lp:///apt')
+        def _resolve(abspath):
+            self.assertEqual('lp:///apt', abspath)
+            return target_branch.base.rstrip('/')
+        transport._resolve = _resolve
+        branch = Branch.open_from_transport(transport)
+        self.assertEqual(target_branch.base, branch.base)

=== modified file 'bzrlib/plugins/launchpad/test_register.py'
--- a/bzrlib/plugins/launchpad/test_register.py	2007-10-12 03:18:30 +0000
+++ b/bzrlib/plugins/launchpad/test_register.py	2007-10-14 09:52:55 +0000
@@ -26,6 +26,7 @@
         BaseRequest,
         BranchBugLinkRequest,
         BranchRegistrationRequest,
+        ResolveLaunchpadPathRequest,
         LaunchpadService,
         )
 
@@ -74,19 +75,23 @@
     # Python 2.5's xmlrpclib looks for this.
     _use_datetime = False
 
-    def __init__(self, testcase):
+    def __init__(self, testcase, expect_auth):
         self.testcase = testcase
+        self.expect_auth = expect_auth
 
     def make_connection(self, host):
         host, http_headers, x509 = self.get_host_info(host)
         test = self.testcase
         self.connected_host = host
-        auth_hdrs = [v for k,v in http_headers if k == 'Authorization']
-        assert len(auth_hdrs) == 1
-        authinfo = auth_hdrs[0]
-        expected_auth = 'testuser at launchpad.net:testpassword'
-        test.assertEquals(authinfo,
-                'Basic ' + base64.encodestring(expected_auth).strip())
+        if self.expect_auth:
+            auth_hdrs = [v for k,v in http_headers if k == 'Authorization']
+            assert len(auth_hdrs) == 1
+            authinfo = auth_hdrs[0]
+            expected_auth = 'testuser at launchpad.net:testpassword'
+            test.assertEquals(authinfo,
+                    'Basic ' + base64.encodestring(expected_auth).strip())
+        else:
+            assert not http_headers
         return InstrumentedXMLRPCConnection(test)
 
     def send_request(self, connection, handler_path, request_body):
@@ -110,10 +115,11 @@
 
 class MockLaunchpadService(LaunchpadService):
 
-    def send_request(self, method_name, method_params):
+    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(TestCase):
@@ -145,7 +151,7 @@
     def test_onto_transport(self):
         """Test 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)
+        transport = InstrumentedXMLRPCTransport(self, expect_auth=True)
         service = LaunchpadService(transport)
         service.registrant_email = 'testuser at launchpad.net'
         service.registrant_password = 'testpassword'
@@ -167,6 +173,17 @@
                  'product'))
         self.assertTrue(transport.got_request)
 
+    def test_onto_transport_unauthenticated(self):
+        """Test how 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):
@@ -186,10 +203,11 @@
         """Send registration to mock server"""
         test_case = self
         class MockRegistrationService(MockLaunchpadService):
-            def send_request(self, method_name, method_params):
+            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',
@@ -201,10 +219,11 @@
         """Send registration to mock server"""
         test_case = self
         class MockRegistrationService(MockLaunchpadService):
-            def send_request(self, method_name, method_params):
+            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')
@@ -215,12 +234,35 @@
         """Send bug-branch link to mock server"""
         test_case = self
         class MockService(MockLaunchpadService):
-            def send_request(self, method_name, method_params):
+            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'])




More information about the bazaar-commits mailing list