~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2006 Canonical Ltd
0.4.1 by Martin Pool
Start lp-register command
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
17
import base64
0.4.13 by Martin Pool
Update xmlrpc api to pass product name as a parameter.
18
import os
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
19
from StringIO import StringIO
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
20
import urlparse
0.4.5 by Martin Pool
Add structured test for parameters passed through xmlrpc
21
import xmlrpclib
22
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
23
from bzrlib import (
24
    config,
25
    osutils,
26
    tests,
27
    ui,
28
    )
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
29
from bzrlib.tests import TestCaseWithTransport, TestSkipped
0.4.1 by Martin Pool
Start lp-register command
30
0.4.5 by Martin Pool
Add structured test for parameters passed through xmlrpc
31
# local import
2898.3.8 by James Henstridge
Get rid of relative imports in Launchpad plugin.
32
from bzrlib.plugins.launchpad.lp_registration import (
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
33
        BaseRequest,
34
        BranchBugLinkRequest,
35
        BranchRegistrationRequest,
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
36
        ResolveLaunchpadPathRequest,
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
37
        LaunchpadService,
38
        )
0.4.5 by Martin Pool
Add structured test for parameters passed through xmlrpc
39
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
40
0.4.12 by Martin Pool
doc
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.
45
46
# TODO: Add test for (and implement) other command-line options to set
0.4.14 by Martin Pool
Update xmlrpc api
47
# project, author_email, description.
0.4.12 by Martin Pool
doc
48
49
# TODO: project_id is not properly handled -- must be passed in rpc or path.
50
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
51
class InstrumentedXMLRPCConnection(object):
52
    """Stands in place of an http connection for the purposes of testing"""
53
54
    def __init__(self, testcase):
55
        self.testcase = testcase
56
57
    def getreply(self):
58
        """Fake the http reply.
59
60
        :returns: (errcode, errmsg, headers)
61
        """
62
        return (200, 'OK', [])
63
64
    def getfile(self):
65
        """Return a fake file containing the response content."""
66
        return StringIO('''\
67
<?xml version="1.0" ?>
68
<methodResponse>
0.4.11 by Martin Pool
Check the correct params are seen by the server
69
    <params>
70
        <param>
71
            <value>
72
                <string>victoria dock</string>
73
            </value>
74
        </param>
75
    </params>
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
76
</methodResponse>''')
77
78
79
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
80
class InstrumentedXMLRPCTransport(xmlrpclib.Transport):
81
2027.2.2 by Marien Zwart
Fixes for python 2.5.
82
    # Python 2.5's xmlrpclib looks for this.
83
    _use_datetime = False
84
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
85
    def __init__(self, testcase, expect_auth):
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
86
        self.testcase = testcase
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
87
        self.expect_auth = expect_auth
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
88
89
    def make_connection(self, host):
90
        host, http_headers, x509 = self.get_host_info(host)
91
        test = self.testcase
0.4.10 by Martin Pool
Move test-specific values out of InstrumentedXMLRPCTransport
92
        self.connected_host = host
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
93
        if self.expect_auth:
94
            auth_hdrs = [v for k,v in http_headers if k == 'Authorization']
3376.2.15 by Martin Pool
Fix assertions in Launchpad registration tests
95
            if len(auth_hdrs) != 1:
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
96
                raise AssertionError("multiple auth headers: %r"
97
                    % (auth_hdrs,))
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
98
            authinfo = auth_hdrs[0]
99
            expected_auth = 'testuser@launchpad.net:testpassword'
100
            test.assertEquals(authinfo,
101
                    'Basic ' + base64.encodestring(expected_auth).strip())
3376.2.15 by Martin Pool
Fix assertions in Launchpad registration tests
102
        elif http_headers:
103
            raise AssertionError()
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
104
        return InstrumentedXMLRPCConnection(test)
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
105
106
    def send_request(self, connection, handler_path, request_body):
107
        test = self.testcase
108
        self.got_request = True
109
110
    def send_host(self, conn, host):
111
        pass
112
113
    def send_user_agent(self, conn):
114
        # TODO: send special user agent string, including bzrlib version
115
        # number
116
        pass
117
118
    def send_content(self, conn, request_body):
119
        unpacked, method = xmlrpclib.loads(request_body)
3376.2.15 by Martin Pool
Fix assertions in Launchpad registration tests
120
        if None in unpacked:
121
            raise AssertionError(
122
                "xmlrpc result %r shouldn't contain None" % (unpacked,))
0.4.10 by Martin Pool
Move test-specific values out of InstrumentedXMLRPCTransport
123
        self.sent_params = unpacked
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
124
125
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
126
class MockLaunchpadService(LaunchpadService):
127
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
128
    def send_request(self, method_name, method_params, authenticated):
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
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
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
132
        self.called_authenticated = authenticated
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
133
134
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
135
class TestBranchRegistration(TestCaseWithTransport):
0.4.4 by Martin Pool
Start forming xmlrpc requests
136
    SAMPLE_URL = 'http://bazaar-vcs.org/bzr/bzr.dev/'
0.4.6 by Martin Pool
Put the rest of the parameters into the registration request.
137
    SAMPLE_OWNER = 'jhacker@foo.com'
138
    SAMPLE_BRANCH_ID = 'bzr.dev'
0.4.4 by Martin Pool
Start forming xmlrpc requests
139
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
140
    def setUp(self):
141
        super(TestBranchRegistration, self).setUp()
142
        # make sure we have a reproducible standard environment
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
143
        self._captureVar('BZR_LP_XMLRPC_URL', None)
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
144
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
145
    def test_register_help(self):
146
        """register-branch accepts --help"""
2581.1.4 by Martin Pool
fixup run_bzr syntax in launchpad plugin
147
        out, err = self.run_bzr(['register-branch', '--help'])
0.4.2 by Martin Pool
Rename command to 'register-branch'
148
        self.assertContainsRe(out, r'Register a branch')
149
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
150
    def test_register_no_url_no_branch(self):
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
151
        """register-branch command requires parameters"""
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
152
        self.make_repository('.')
153
        self.run_bzr_error(
154
            ['register-branch requires a public branch url - '
155
             'see bzr help register-branch'],
156
            'register-branch')
157
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)
164
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)
0.4.3 by Martin Pool
More command line processing
170
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
171
    def test_register_dry_run(self):
2581.1.4 by Martin Pool
fixup run_bzr syntax in launchpad plugin
172
        out, err = self.run_bzr(['register-branch',
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
173
                                'http://test-server.com/bzr/branch',
2581.1.4 by Martin Pool
fixup run_bzr syntax in launchpad plugin
174
                                '--dry-run'])
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
175
        self.assertEquals(out, 'Branch registered.\n')
0.4.4 by Martin Pool
Start forming xmlrpc requests
176
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
177
    def test_onto_transport(self):
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
178
        """Test how the request is sent by transmitting across a mock Transport"""
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
179
        # use a real transport, but intercept at the http/xml layer
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
180
        transport = InstrumentedXMLRPCTransport(self, expect_auth=True)
0.4.30 by Martin Pool
Fix wierd syntax errors in test
181
        service = LaunchpadService(transport)
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
182
        service.registrant_email = 'testuser@launchpad.net'
183
        service.registrant_password = 'testpassword'
0.4.7 by Martin Pool
Start making provision to test using a mock xmlrpc transport.
184
        rego = BranchRegistrationRequest('http://test-server.com/bzr/branch',
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
185
                'branch-id',
186
                'my test branch',
187
                'description',
188
                'author@launchpad.net',
189
                'product')
0.4.20 by Bjorn Tillenius
try to fix the tests.
190
        rego.submit(service)
3200.2.2 by Robert Collins
* The launchpad plugin now uses the ``edge`` xmlrpc server to avoid
191
        self.assertEquals(transport.connected_host, 'xmlrpc.edge.launchpad.net')
0.4.14 by Martin Pool
Update xmlrpc api
192
        self.assertEquals(len(transport.sent_params), 6)
0.4.11 by Martin Pool
Check the correct params are seen by the server
193
        self.assertEquals(transport.sent_params,
0.4.23 by Martin Pool
(register-branch) fix ordering of parameters and restore transport-level test.
194
                ('http://test-server.com/bzr/branch',  # branch_url
195
                 'branch-id',                          # branch_name
196
                 'my test branch',                     # branch_title
197
                 'description',
198
                 'author@launchpad.net',
199
                 'product'))
0.4.8 by Martin Pool
More instrumentation of xmlrpc requests
200
        self.assertTrue(transport.got_request)
0.4.13 by Martin Pool
Update xmlrpc api to pass product name as a parameter.
201
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
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)
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
206
        resolve = ResolveLaunchpadPathRequest('bzr')
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
207
        resolve.submit(service)
3200.2.2 by Robert Collins
* The launchpad plugin now uses the ``edge`` xmlrpc server to avoid
208
        self.assertEquals(transport.connected_host, 'xmlrpc.edge.launchpad.net')
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
209
        self.assertEquals(len(transport.sent_params), 1)
210
        self.assertEquals(transport.sent_params, ('bzr', ))
211
        self.assertTrue(transport.got_request)
212
0.4.22 by Martin Pool
(register-branch) Update tests to be in-process calls to a mock server.
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):
218
                return (42,)
219
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,))
227
0.4.24 by Martin Pool
(register-branch) additional test case against mock server
228
    def test_mock_server_registration(self):
229
        """Send registration to mock server"""
230
        test_case = self
231
        class MockRegistrationService(MockLaunchpadService):
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
232
            def send_request(self, method_name, method_params, authenticated):
0.4.24 by Martin Pool
(register-branch) additional test case against mock server
233
                test_case.assertEquals(method_name, "register_branch")
234
                test_case.assertEquals(list(method_params),
235
                        ['url', 'name', 'title', 'description', 'email', 'name'])
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
236
                test_case.assertEquals(authenticated, True)
0.4.24 by Martin Pool
(register-branch) additional test case against mock server
237
                return 'result'
238
        service = MockRegistrationService()
239
        rego = BranchRegistrationRequest('url', 'name', 'title',
240
                        'description', 'email', 'name')
241
        result = rego.submit(service)
242
        self.assertEquals(result, 'result')
0.4.25 by Martin Pool
(register-branch) additional test for registration with defaults
243
244
    def test_mock_server_registration_with_defaults(self):
245
        """Send registration to mock server"""
246
        test_case = self
247
        class MockRegistrationService(MockLaunchpadService):
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
248
            def send_request(self, method_name, method_params, authenticated):
0.4.25 by Martin Pool
(register-branch) additional test for registration with defaults
249
                test_case.assertEquals(method_name, "register_branch")
250
                test_case.assertEquals(list(method_params),
251
                        ['http://server/branch', 'branch', '', '', '', ''])
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
252
                test_case.assertEquals(authenticated, True)
0.4.25 by Martin Pool
(register-branch) additional test for registration with defaults
253
                return 'result'
254
        service = MockRegistrationService()
255
        rego = BranchRegistrationRequest('http://server/branch')
256
        result = rego.submit(service)
257
        self.assertEquals(result, 'result')
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
258
259
    def test_mock_bug_branch_link(self):
260
        """Send bug-branch link to mock server"""
261
        test_case = self
262
        class MockService(MockLaunchpadService):
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
263
            def send_request(self, method_name, method_params, authenticated):
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
264
                test_case.assertEquals(method_name, "link_branch_to_bug")
265
                test_case.assertEquals(list(method_params),
266
                        ['http://server/branch', 1234, ''])
2898.4.1 by James Henstridge
Make it possible to make unauthenticated XML-RPC requests.
267
                test_case.assertEquals(authenticated, True)
0.4.26 by Martin Pool
(register-branch) Add test for link_branch_to_bug and fix its parameters
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')
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
273
274
    def test_mock_resolve_lp_url(self):
275
        test_case = self
276
        class MockService(MockLaunchpadService):
277
            def send_request(self, method_name, method_params, authenticated):
2898.4.7 by James Henstridge
Fix up tests.
278
                test_case.assertEquals(method_name, "resolve_lp_path")
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
279
                test_case.assertEquals(list(method_params), ['bzr'])
280
                test_case.assertEquals(authenticated, False)
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
281
                return dict(urls=[
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'])
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
286
        service = MockService()
2898.4.3 by James Henstridge
Make launchpad_transport_indirect() use XMLRPC to resolve the lp: URL.
287
        resolve = ResolveLaunchpadPathRequest('bzr')
2898.4.2 by James Henstridge
Add ResolveLaunchpadURLRequest() class to handle lp: URL resolution.
288
        result = resolve.submit(service)
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
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'])
2978.5.2 by John Arbash Meinel
merge bzr.dev 2991
295
2978.5.1 by John Arbash Meinel
Fix bug #162494, 'bzr register-branch' needs proper auth handling.
296
297
class TestGatherUserCredentials(tests.TestCaseInTempDir):
298
299
    def setUp(self):
300
        super(TestGatherUserCredentials, self).setUp()
301
        # make sure we have a reproducible standard environment
302
        self._captureVar('BZR_LP_XMLRPC_URL', None)
303
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)
310
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')
317
        try:
318
            scheme, hostinfo = urlparse.urlsplit(service.service_url)[:2]
319
            f.write('[section]\n'
320
                    'scheme=%s\n'
321
                    'host=%s\n'
322
                    'user=test@user.com\n'
323
                    'password=testpass\n'
324
                    % (scheme, hostinfo))
325
        finally:
326
            f.close()
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)
331
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
        ui.ui_factory = tests.TestUIFactory(stdin='userpass\n',
339
                                            stdout=stdout)
340
        self.assertIs(None, service.registrant_password)
341
        service.gather_user_credentials()
342
        self.assertEqual('test@user.com', service.registrant_email)
343
        self.assertEqual('userpass', service.registrant_password)
344
        self.assertContainsRe(stdout.getvalue(),
345
                             'launchpad.net password for test@user\\.com')
346