~bzr-pqm/bzr/bzr.dev

4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2007-2010 Canonical Ltd
2245.8.1 by Martin Pool
Start adding tests for launchpad indirection
2
#
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.
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, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2245.8.1 by Martin Pool
Start adding tests for launchpad indirection
16
3251.4.3 by Aaron Bentley
More renames and cleanups
17
"""Tests for directory lookup through Launchpad.net"""
2245.8.1 by Martin Pool
Start adding tests for launchpad indirection
18
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
19
import os
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
20
import xmlrpclib
21
2245.8.3 by Martin Pool
Start adding indirection transport
22
from bzrlib import (
2245.8.4 by Martin Pool
lp:/// indirection works
23
    errors,
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
24
    tests,
2245.8.3 by Martin Pool
Start adding indirection transport
25
    )
2898.4.17 by James Henstridge
Add a test that the redirect actually occurs when opening an lp: URL.
26
from bzrlib.branch import Branch
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
27
from bzrlib.directory_service import directories
3777.1.18 by Aaron Bentley
Fix None handling wrt auth upgrades
28
from bzrlib.tests import (
29
    TestCaseInTempDir,
30
    TestCaseWithMemoryTransport
31
)
2898.4.17 by James Henstridge
Add a test that the redirect actually occurs when opening an lp: URL.
32
from bzrlib.transport import get_transport
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
33
from bzrlib.plugins.launchpad import (
34
    _register_directory,
35
    lp_registration,
36
    )
3251.4.3 by Aaron Bentley
More renames and cleanups
37
from bzrlib.plugins.launchpad.lp_directory import (
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
38
    LaunchpadDirectory)
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
39
from bzrlib.plugins.launchpad.account import get_lp_login
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
40
from bzrlib.tests import (
41
    http_server,
42
    http_utils,
43
    )
44
45
46
def load_tests(standard_tests, module, loader):
47
    result = loader.suiteClass()
48
    t_tests, remaining_tests = tests.split_suite_by_condition(
49
        standard_tests, tests.condition_isinstance((
4776.2.6 by Vincent Ladeuil
Fixed as per review comments.
50
                TestXMLRPCTransport,
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
51
                )))
52
    transport_scenarios = [
53
        ('http', dict(server_class=PreCannedHTTPServer,)),
54
        ]
55
    if tests.HTTPSServerFeature.available():
56
        transport_scenarios.append(
57
            ('https', dict(server_class=PreCannedHTTPSServer,)),
58
            )
59
    tests.multiply_tests(t_tests, transport_scenarios, result)
60
61
    # No parametrization for the remaining tests
62
    result.addTests(remaining_tests)
63
64
    return result
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
65
66
67
class FakeResolveFactory(object):
68
    def __init__(self, test, expected_path, result):
69
        self._test = test
70
        self._expected_path = expected_path
71
        self._result = result
72
73
    def __call__(self, path):
74
        self._test.assertEqual(self._expected_path, path)
75
        return self
76
77
    def submit(self, service):
3193.5.2 by Tim Penhey
Updated the tests to handle unknown launchpad instances.
78
        self._service_url = service.service_url
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
79
        return self._result
2245.8.1 by Martin Pool
Start adding tests for launchpad indirection
80
2245.8.7 by Martin Pool
small review cleanups
81
3777.1.18 by Aaron Bentley
Fix None handling wrt auth upgrades
82
class DirectoryUrlTests(TestCaseInTempDir):
3251.4.3 by Aaron Bentley
More renames and cleanups
83
    """Tests for branch urls through Launchpad.net directory"""
2245.8.1 by Martin Pool
Start adding tests for launchpad indirection
84
2245.8.5 by Martin Pool
Add short-form lp:PRODUCT url form
85
    def test_short_form(self):
86
        """A launchpad url should map to a http url"""
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
87
        factory = FakeResolveFactory(
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
88
            self, 'apt', dict(urls=[
89
                    'http://bazaar.launchpad.net/~apt/apt/devel']))
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
90
        directory = LaunchpadDirectory()
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
91
        self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
92
                          directory._resolve('lp:apt', factory))
3193.5.2 by Tim Penhey
Updated the tests to handle unknown launchpad instances.
93
        # Make sure that resolve went to the production server.
3221.1.12 by Martin Pool
Update Launchpad indirection tests to allow for xmlrpcs being sent to edge by default
94
        self.assertEquals('https://xmlrpc.edge.launchpad.net/bazaar/',
3193.5.2 by Tim Penhey
Updated the tests to handle unknown launchpad instances.
95
                          factory._service_url)
96
97
    def test_staging(self):
98
        """A launchpad url should map to a http url"""
99
        factory = FakeResolveFactory(
100
            self, 'apt', dict(urls=[
101
                    'http://bazaar.staging.launchpad.net/~apt/apt/devel']))
102
        url = 'lp://staging/apt'
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
103
        directory = LaunchpadDirectory()
3193.5.2 by Tim Penhey
Updated the tests to handle unknown launchpad instances.
104
        self.assertEquals('http://bazaar.staging.launchpad.net/~apt/apt/devel',
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
105
                          directory._resolve(url, factory))
3193.5.2 by Tim Penhey
Updated the tests to handle unknown launchpad instances.
106
        # Make sure that resolve went to the staging server.
107
        self.assertEquals('https://xmlrpc.staging.launchpad.net/bazaar/',
108
                          factory._service_url)
2245.8.5 by Martin Pool
Add short-form lp:PRODUCT url form
109
3251.4.3 by Aaron Bentley
More renames and cleanups
110
    def test_url_from_directory(self):
2245.8.3 by Martin Pool
Start adding indirection transport
111
        """A launchpad url should map to a http url"""
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
112
        factory = FakeResolveFactory(
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
113
            self, 'apt', dict(urls=[
114
                    'http://bazaar.launchpad.net/~apt/apt/devel']))
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
115
        directory = LaunchpadDirectory()
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
116
        self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
117
                          directory._resolve('lp:///apt', factory))
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
118
3251.4.3 by Aaron Bentley
More renames and cleanups
119
    def test_directory_skip_bad_schemes(self):
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
120
        factory = FakeResolveFactory(
121
            self, 'apt', dict(urls=[
122
                    'bad-scheme://bazaar.launchpad.net/~apt/apt/devel',
123
                    'http://bazaar.launchpad.net/~apt/apt/devel',
124
                    'http://another/location']))
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
125
        directory = LaunchpadDirectory()
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
126
        self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
127
                          directory._resolve('lp:///apt', factory))
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
128
3251.4.3 by Aaron Bentley
More renames and cleanups
129
    def test_directory_no_matching_schemes(self):
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
130
        # If the XMLRPC call does not return any protocols we support,
131
        # invalidURL is raised.
132
        factory = FakeResolveFactory(
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
133
            self, 'apt', dict(urls=[
134
                    'bad-scheme://bazaar.launchpad.net/~apt/apt/devel']))
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
135
        directory = LaunchpadDirectory()
2898.4.11 by James Henstridge
Switch back to RedirectRequested based implementation.
136
        self.assertRaises(errors.InvalidURL,
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
137
                          directory._resolve, 'lp:///apt', factory)
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
138
3251.4.3 by Aaron Bentley
More renames and cleanups
139
    def test_directory_fault(self):
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
140
        # Test that XMLRPC faults get converted to InvalidURL errors.
141
        factory = FakeResolveFactory(self, 'apt', None)
142
        def submit(service):
143
            raise xmlrpclib.Fault(42, 'something went wrong')
144
        factory.submit = submit
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
145
        directory = LaunchpadDirectory()
2898.4.11 by James Henstridge
Switch back to RedirectRequested based implementation.
146
        self.assertRaises(errors.InvalidURL,
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
147
                          directory._resolve, 'lp:///apt', factory)
2245.8.4 by Martin Pool
lp:/// indirection works
148
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
149
    def test_skip_bzr_ssh_launchpad_net_when_anonymous(self):
150
        # Test that bzr+ssh://bazaar.launchpad.net gets skipped if
151
        # Bazaar does not know the user's Launchpad ID:
152
        self.assertEqual(None, get_lp_login())
153
        factory = FakeResolveFactory(
154
            self, 'apt', dict(urls=[
155
                    'bzr+ssh://bazaar.launchpad.net/~apt/apt/devel',
156
                    'http://bazaar.launchpad.net/~apt/apt/devel']))
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
157
        directory = LaunchpadDirectory()
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
158
        self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
159
                          directory._resolve('lp:///apt', factory))
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
160
3031.2.2 by jml at canonical
Failing test for skipping SFTP.
161
    def test_skip_sftp_launchpad_net_when_anonymous(self):
162
        # Test that sftp://bazaar.launchpad.net gets skipped if
163
        # Bazaar does not know the user's Launchpad ID:
164
        self.assertEqual(None, get_lp_login())
165
        factory = FakeResolveFactory(
166
            self, 'apt', dict(urls=[
167
                    'sftp://bazaar.launchpad.net/~apt/apt/devel',
168
                    'http://bazaar.launchpad.net/~apt/apt/devel']))
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
169
        directory = LaunchpadDirectory()
3031.2.2 by jml at canonical
Failing test for skipping SFTP.
170
        self.assertEquals('http://bazaar.launchpad.net/~apt/apt/devel',
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
171
                          directory._resolve('lp:///apt', factory))
3031.2.2 by jml at canonical
Failing test for skipping SFTP.
172
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
173
    def test_rewrite_bzr_ssh_launchpad_net(self):
174
        # Test that bzr+ssh URLs get rewritten to include the user's
175
        # Launchpad ID (assuming we know the Launchpad ID).
176
        factory = FakeResolveFactory(
177
            self, 'apt', dict(urls=[
178
                    'bzr+ssh://bazaar.launchpad.net/~apt/apt/devel',
179
                    'http://bazaar.launchpad.net/~apt/apt/devel']))
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
180
        directory = LaunchpadDirectory()
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
181
        self.assertEquals(
3777.1.21 by Aaron Bentley
Stop including usernames in resolved lp: urls
182
            'bzr+ssh://bazaar.launchpad.net/~apt/apt/devel',
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
183
            directory._resolve('lp:///apt', factory, _lp_login='username'))
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
184
185
    def test_no_rewrite_of_other_bzr_ssh(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
186
        # Test that we don't rewrite bzr+ssh URLs for other
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
187
        self.assertEqual(None, get_lp_login())
188
        factory = FakeResolveFactory(
189
            self, 'apt', dict(urls=[
190
                    'bzr+ssh://example.com/~apt/apt/devel',
191
                    'http://bazaar.launchpad.net/~apt/apt/devel']))
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
192
        directory = LaunchpadDirectory()
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
193
        self.assertEquals('bzr+ssh://example.com/~apt/apt/devel',
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
194
                          directory._resolve('lp:///apt', factory))
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
195
2245.8.4 by Martin Pool
lp:/// indirection works
196
    # TODO: check we get an error if the url is unreasonable
3251.4.3 by Aaron Bentley
More renames and cleanups
197
    def test_error_for_bad_url(self):
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
198
        directory = LaunchpadDirectory()
2245.8.4 by Martin Pool
lp:/// indirection works
199
        self.assertRaises(errors.InvalidURL,
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
200
            directory._resolve, 'lp://ratotehunoahu')
2898.4.12 by James Henstridge
Add tests that redirects get issued by appropriate transport methods.
201
2898.4.17 by James Henstridge
Add a test that the redirect actually occurs when opening an lp: URL.
202
3251.4.3 by Aaron Bentley
More renames and cleanups
203
class DirectoryOpenBranchTests(TestCaseWithMemoryTransport):
2898.4.17 by James Henstridge
Add a test that the redirect actually occurs when opening an lp: URL.
204
3251.4.3 by Aaron Bentley
More renames and cleanups
205
    def test_directory_open_branch(self):
2898.4.17 by James Henstridge
Add a test that the redirect actually occurs when opening an lp: URL.
206
        # Test that opening an lp: branch redirects to the real location.
207
        target_branch = self.make_branch('target')
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
208
        class FooService(object):
209
            """A directory service that maps the name to a FILE url"""
210
211
            def look_up(self, name, url):
212
                if 'lp:///apt' == url:
213
                    return target_branch.base.rstrip('/')
3251.4.2 by Aaron Bentley
Clean up Launchpad directory service code
214
                return '!unexpected look_up value!'
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
215
216
        directories.remove('lp:')
217
        directories.register('lp:', FooService, 'Map lp URLs to local urls')
3251.4.2 by Aaron Bentley
Clean up Launchpad directory service code
218
        self.addCleanup(_register_directory)
4985.2.1 by Vincent Ladeuil
Deploy addAttrCleanup on the whole test suite.
219
        self.addCleanup(directories.remove, 'lp:')
2898.4.17 by James Henstridge
Add a test that the redirect actually occurs when opening an lp: URL.
220
        transport = get_transport('lp:///apt')
221
        branch = Branch.open_from_transport(transport)
222
        self.assertEqual(target_branch.base, branch.base)
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
223
224
225
class PredefinedRequestHandler(http_server.TestingHTTPRequestHandler):
226
    """Request handler for a unique and pre-defined request.
227
228
    The only thing we care about here is that we receive a connection. But
229
    since we want to dialog with a real http client, we have to send it correct
230
    responses.
231
232
    We expect to receive a *single* request nothing more (and we won't even
233
    check what request it is), the tests will recognize us from our response.
234
    """
235
236
    def handle_one_request(self):
237
        tcs = self.server.test_case_server
238
        requestline = self.rfile.readline()
239
        headers = self.MessageClass(self.rfile, 0)
240
        if requestline.startswith('POST'):
241
            # The body should be a single line (or we don't know where it ends
242
            # and we don't want to issue a blocking read)
243
            body = self.rfile.readline()
244
245
        self.wfile.write(tcs.canned_response)
246
4776.2.6 by Vincent Ladeuil
Fixed as per review comments.
247
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
248
class PreCannedServerMixin(object):
249
250
    def __init__(self):
251
        super(PreCannedServerMixin, self).__init__(
252
            request_handler=PredefinedRequestHandler)
253
        # Bytes read and written by the server
254
        self.bytes_read = 0
255
        self.bytes_written = 0
256
        self.canned_response = None
257
258
259
class PreCannedHTTPServer(PreCannedServerMixin, http_server.HttpServer):
260
    pass
261
262
263
if tests.HTTPSServerFeature.available():
264
    from bzrlib.tests import https_server
265
    class PreCannedHTTPSServer(PreCannedServerMixin, https_server.HTTPSServer):
266
        pass
267
268
4776.2.6 by Vincent Ladeuil
Fixed as per review comments.
269
class TestXMLRPCTransport(tests.TestCase):
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
270
271
    # set by load_tests
272
    server_class = None
273
274
    def setUp(self):
275
        tests.TestCase.setUp(self)
276
        self.server = self.server_class()
4934.3.3 by Martin Pool
Rename Server.setUp to Server.start_server
277
        self.server.start_server()
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
278
        # Ensure we don't clobber env
279
        self._captureVar('BZR_LP_XMLRPC_URL', None)
280
281
    def tearDown(self):
4934.3.1 by Martin Pool
Rename Server.tearDown to .stop_server
282
        self.server.stop_server()
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
283
        tests.TestCase.tearDown(self)
284
285
    def set_canned_response(self, server, path):
286
        response_format = '''HTTP/1.1 200 OK\r
287
Date: Tue, 11 Jul 2006 04:32:56 GMT\r
288
Server: Apache/2.0.54 (Fedora)\r
289
Last-Modified: Sun, 23 Apr 2006 19:35:20 GMT\r
290
ETag: "56691-23-38e9ae00"\r
291
Accept-Ranges: bytes\r
292
Content-Length: %(length)d\r
293
Connection: close\r
294
Content-Type: text/plain; charset=UTF-8\r
295
\r
296
<?xml version='1.0'?>
297
<methodResponse>
298
<params>
299
<param>
300
<value><struct>
301
<member>
302
<name>urls</name>
303
<value><array><data>
304
<value><string>bzr+ssh://bazaar.launchpad.net/%(path)s</string></value>
305
<value><string>http://bazaar.launchpad.net/%(path)s</string></value>
306
</data></array></value>
307
</member>
308
</struct></value>
309
</param>
310
</params>
311
</methodResponse>
312
'''
313
        length = 334 + 2 * len(path)
314
        server.canned_response = response_format % dict(length=length,
315
                                                        path=path)
316
4776.2.3 by Vincent Ladeuil
Add NEWS entry.
317
    def do_request(self, server_url):
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
318
        os.environ['BZR_LP_XMLRPC_URL'] = self.server.get_url()
319
        service = lp_registration.LaunchpadService()
320
        resolve = lp_registration.ResolveLaunchpadPathRequest('bzr')
321
        result = resolve.submit(service)
4776.2.3 by Vincent Ladeuil
Add NEWS entry.
322
        return result
323
324
    def test_direct_request(self):
325
        self.set_canned_response(self.server, '~bzr-pqm/bzr/bzr.dev')
326
        result = self.do_request(self.server.get_url())
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
327
        urls = result.get('urls', None)
328
        self.assertIsNot(None, urls)
329
        self.assertEquals(
330
            ['bzr+ssh://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev',
331
             'http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev'],
332
            urls)
4776.2.3 by Vincent Ladeuil
Add NEWS entry.
333
    # FIXME: we need to test with a real proxy, I can't find a way so simulate
334
    # CONNECT without leaving one server hanging the test :-/ Since that maybe
335
    # related to the leaking tests problems, I'll punt for now -- vila 20091030